Notice
Recent Posts
Recent Comments
Link
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | ||||||
2 | 3 | 4 | 5 | 6 | 7 | 8 |
9 | 10 | 11 | 12 | 13 | 14 | 15 |
16 | 17 | 18 | 19 | 20 | 21 | 22 |
23 | 24 | 25 | 26 | 27 | 28 |
Tags
- button
- 구글맵스
- RecyclerView
- npm
- 스프링부트
- GoogleMaps
- Linux
- array
- nodejs
- 코틀린
- scrollview
- JS
- 오버라이딩
- TypeScript
- Javscript
- Hook
- 랜덤넘버
- Android
- TextView
- React
- Java
- 리액트
- stylesheet
- 랜덤번호
- Kotlin
- fragment
- 자바스크립트
- JavaScript
- 안드로이드
- SpringBoot
Archives
- Today
- Total
타닥타닥 개발자의 일상
Kotlin 사진찍은 장소 위치 메모와 함께 저장하는 Android 앱 만들기 / RecyclerView, Camera, SQLite, Singleton 사용 본문
코딩 기록/Kotlin
Kotlin 사진찍은 장소 위치 메모와 함께 저장하는 Android 앱 만들기 / RecyclerView, Camera, SQLite, Singleton 사용
NomadHaven 2022. 3. 3. 19:41폴더 및 파일구성
코딩 하기 전 AndroidManifest.xml / build.gradle 파일을 수정해 사전 설정을 완료해야한다.
사전설정
AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.mydocument">
<uses-permission android:name="android.permission.CAMERA"></uses-permission>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"></uses-permission>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"></uses-permission>
<!--geo-->
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.INTERNET"/>
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/Theme.MyDocument"
android:requestLegacyExternalStorage="true">
<activity
android:name=".SaveActivity"
android:exported="true"/>
<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>
build.gradle(:app)
plugins {
id 'com.android.application'
id 'kotlin-android'
}
android {
compileSdk 32
defaultConfig {
applicationId "com.example.mydocument"
minSdk 21
targetSdk 32
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'
}
}
dependencies {
implementation 'com.google.android.gms:play-services-location:17.0.0'
implementation 'com.google.android.gms:play-services-maps:17.0.0'
implementation 'androidx.recyclerview:recyclerview:1.1.0' // 추가
implementation 'com.github.bumptech.glide:glide:4.11.0' // 추가
annotationProcessor 'com.github.bumptech.glide:compiler:4.11.0' // 추가
implementation 'androidx.core:core-ktx:1.7.0'
implementation 'androidx.appcompat:appcompat:1.4.1'
implementation 'com.google.android.material:material:1.5.0'
implementation 'androidx.constraintlayout:constraintlayout:2.1.3'
implementation 'com.google.android.gms:play-services-location:19.0.1'
testImplementation 'junit:junit:4.+'
androidTestImplementation 'androidx.test.ext:junit:1.1.3'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'
}
activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<androidx.recyclerview.widget.RecyclerView
android:layout_width="503dp"
android:layout_height="633dp"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
android:layout_marginBottom="48dp" app:layout_constraintHorizontal_bias="0.504"
android:id="@+id/recyclerView"/>
<Button
android:text="추가"
android:layout_width="298dp"
android:layout_height="58dp" android:id="@+id/mainAddBtn"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" app:layout_constraintBottom_toTopOf="@+id/recyclerView"
app:layout_constraintVertical_bias="0.692"/>
</androidx.constraintlayout.widget.ConstraintLayout>
*Activity 생성은 example 폴더 우클릭 > 새로만들기 > Activity > Empty Activity로 생성
MainActivity.kt
package com.example.mydocument
import android.content.Intent
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.widget.Button
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val mainAddBtn = findViewById<Button>(R.id.mainAddBtn)
mainAddBtn.setOnClickListener {
val intent = Intent(this, SaveActivity::class.java)
startActivity(intent)
}
val dbHelper = DBHelper.getInstance(this,"localInfo.db",)
val list = dbHelper.printList()
// 확인!
for (i in list.indices){
println(list[i].toString())
}
val recyclerView = findViewById<RecyclerView>(R.id.recyclerView)
val mAdapter = CustomAdapter(this,list)
recyclerView.adapter = mAdapter
val layout = LinearLayoutManager(this)
recyclerView.layoutManager=layout //recycler 뷰에 리니어 레이아웃 적용
recyclerView.setHasFixedSize(true)
}
}
view.item_layout.xml
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="60dp"
android:layout_margin="5dp">
<TextView
android:id="@+id/RcLocation"
android:text="주소"
android:layout_width="259dp"
android:layout_height="45dp"
android:textSize="15sp"
android:textStyle="bold"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintHorizontal_bias="0.377"
app:layout_constraintVertical_bias="0.533"/>
<TextView
android:id="@+id/RcDate"
android:text="일자"
android:layout_width="134dp"
android:layout_height="19dp"
android:textSize="10sp"
android:textStyle="bold"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintVertical_bias="0.2" app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintHorizontal_bias="1.0"/>
<TextView
android:id="@+id/rcContent"
android:text="메모"
android:layout_width="133dp"
android:layout_height="20dp"
android:textSize="10sp"
android:textStyle="bold"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintVertical_bias="0.825" app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintHorizontal_bias="1.0"/>
<TextView
android:id="@+id/RcSeq"
android:text="번호"
android:layout_width="34dp"
android:layout_height="24dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintHorizontal_bias="0.012"/>
<ImageView
android:layout_width="52dp"
android:layout_height="45dp" tools:srcCompat="@tools:sample/avatars" android:id="@+id/RcimageView"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintTop_toTopOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintHorizontal_bias="0.096"
app:layout_constraintVertical_bias="0.533"/>
</androidx.constraintlayout.widget.ConstraintLayout>
CustomAdapter.kt
package com.example.mydocument
import android.content.Context
import android.graphics.BitmapFactory
import android.util.Log
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.ImageView
import android.widget.TextView
import androidx.recyclerview.widget.RecyclerView
import java.io.File
class CustomAdapter(private val context:Context, private val dataList: ArrayList<SaveInfoData>)
:RecyclerView.Adapter<CustomAdapter.ItemViewHolder>()
{
//RecyclerView에 binding 해줄 Adapter를 연결시킨다.
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ItemViewHolder {
val view = LayoutInflater.from(context).inflate(R.layout.view_item_layout,parent, false)
return ItemViewHolder(view)
}
override fun onBindViewHolder(holder: ItemViewHolder, position: Int) {
holder.bind(dataList[position], context)
//recycleView에 있는 각각의 아이템과 아이템의 위치
}
override fun getItemCount(): Int {
return dataList.size
}
inner class ItemViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView){
private val RcSeq = itemView.findViewById<TextView>(R.id.RcSeq)
private val RcimageView = itemView.findViewById<ImageView>(R.id.RcimageView)
private val RcLocation = itemView.findViewById<TextView>(R.id.RcLocation)
private val RcDate = itemView.findViewById<TextView>(R.id.RcDate)
private val RcContent = itemView.findViewById<TextView>(R.id.rcContent)
//data -> resource (binding)
fun bind(dataVo: SaveInfoData,context: Context){
//사진 처리
if (dataVo.imagePath != "") {
val resourceId =
context.resources.getIdentifier(dataVo.imagePath, "drawable", context.packageName)
if (resourceId > 0) {
RcimageView.setImageResource(resourceId)
} else {
// userPhoto.setImageResource(R.mipmap.ic_launcher_round)
// Glide.with(itemView).load(dataVo.photo).into(userPhoto)
Log.d("","~~~~~~~~~~~~~~~~~~~~~ 들어옴")
val file: File = File(dataVo.imagePath)
val bExist = file.exists()
if (bExist) {
Log.d("","이미지 파일 있음")
val myBitmap = BitmapFactory.decodeFile(dataVo.imagePath)
RcimageView.setImageBitmap(myBitmap)
}else{
Log.d("","${dataVo.imagePath} 이미지 파일 없음")
}
}
} else {
RcimageView.setImageResource(R.mipmap.ic_launcher_round)
}
//TextView 데이터 세팅 작업
RcSeq.text = dataVo.seq.toString()
RcLocation.text =dataVo.location
RcDate.text = dataVo.date
RcContent.text =dataVo.content
// 클릭시에
itemView.setOnClickListener {
println(dataVo.content)
/*Intent(context, ProfileDetailActivity::class.java).apply {
putExtra("data", dataVo)
addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) // 새로운 태스크를 생성하여 그 태스크안에 엑티비티를 추가하게 됩니다.
}.run { context.startActivity(this) }*/
}
}
}
}
DocumentDto.kt
package com.example.mydocument
data class DocumentDto(var seq:Int, var imagePath:String, var location:String, var content:String, var date:String){
override fun toString(): String {
return "$seq $imagePath $location $content $date"
}
}
DBHelper.kt
package com.example.mydocument
import android.content.Context
import android.database.sqlite.SQLiteDatabase
import android.database.sqlite.SQLiteOpenHelper
class DBHelper(context: Context, filename:String): SQLiteOpenHelper(context,filename,null,1) {
companion object {
var dbhelper: DBHelper? = null
fun getInstance(context: Context, filename: String): DBHelper {
if (dbhelper == null) {
dbhelper = DBHelper(context, filename)
}
return dbhelper!!
}
}
override fun onCreate(db: SQLiteDatabase?) {
var sql: String = "CREATE TABLE IF NOT EXISTS localInfo( " +
"SEQ INTEGER PRIMARY KEY AUTOINCREMENT, " +
"ImagePath STRING, " +
"Location STRING," +
"Content STRING, " +
"Date TIMESTAMP DEFAULT CURRENT_TIMESTAMP NOT NULL)"
db?.execSQL(sql)
}
override fun onUpgrade(db: SQLiteDatabase?, oldVersion: Int, newVersion: Int) {
val sql: String = "DROP TABLE if exists localInfo"
db?.execSQL(sql)
onCreate(db)
}
fun insert(dto: SaveInfoData){
println("데이터 입력 완료")
var sql = " INSERT INTO localInfo(ImagePath,Location,Content) " +
" VALUES('${dto.imagePath}','${dto.location}','${dto.content}')"
var db = this.writableDatabase
db.execSQL(sql)
// 확인용
var query = "SELECT * FROM localInfo"
var cursor = db.rawQuery(query,null)
while(cursor.moveToNext()){
println(cursor.getInt(cursor.getColumnIndex("SEQ")).toString() + " : "
+ cursor.getString(cursor.getColumnIndex("ImagePath")) + " : "
+ cursor.getString(cursor.getColumnIndex("Location")) + " : "
+ cursor.getString(cursor.getColumnIndex("Content")) + " : "
+ cursor.getString(cursor.getColumnIndex("Date"))
)
}
}
fun printList(): ArrayList<SaveInfoData>{
var list = ArrayList<SaveInfoData>()
val query = " SELECT * FROM localInfo"
var db = this.writableDatabase
var cursor = db.rawQuery(query, null)
while (cursor.moveToNext()) {
var _seq = cursor.getColumnIndex("SEQ")
var _imagepath = cursor.getColumnIndex("ImagePath")
var _location = cursor.getColumnIndex("Location")
var _content = cursor.getColumnIndex("Content")
var _date = cursor.getColumnIndex("Date")
val seq = cursor.getInt(_seq)
val imagepath = cursor.getString(_imagepath)
val location = cursor.getString(_location)
val content = cursor.getString(_content)
val date = cursor.getString(_date)
println("$seq : $imagepath: $location : $content")
list.add(SaveInfoData(seq,imagepath,location,content,date)) //모든 매개변수가 지정되지 않아 에러난다
}
cursor.close()
return list
}
}
activity_save.xml
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".SaveActivity">
<Button
android:text="save"
android:layout_width="136dp"
android:layout_height="53dp"
android:id="@+id/saveContentBtn"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintHorizontal_bias="0.721"
app:layout_constraintVertical_bias="0.05"/>
<Button
android:text="camera"
android:layout_width="136dp"
android:layout_height="53dp"
android:id="@+id/saveCamBtn"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintHorizontal_bias="0.29"
app:layout_constraintVertical_bias="0.05"/>
<ImageView
android:layout_width="431dp"
android:layout_height="423dp"
tools:srcCompat="@tools:sample/avatars"
android:id="@+id/avatars"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintHorizontal_bias="0.55"
app:layout_constraintVertical_bias="0.232"/>
<EditText
android:layout_width="414dp"
android:layout_height="74dp"
android:inputType="textPersonName"
android:ems="10"
android:id="@+id/saveEditTxtContent"
android:hint="추가로 기입할 정보"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintVertical_bias="0.737"/>
<TextView
android:text="이미지 경로"
android:layout_width="417dp"
android:layout_height="71dp" android:id="@+id/saveTxtView"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintHorizontal_bias="0.491"
app:layout_constraintVertical_bias="0.844"/>
</androidx.constraintlayout.widget.ConstraintLayout>
SaveActivity.kt
package com.example.mydocument
import android.Manifest
import android.annotation.SuppressLint
import android.app.Activity
import android.content.ContentValues
import android.content.DialogInterface
import android.content.Intent
import android.content.pm.PackageManager
import android.database.Cursor
import android.graphics.Bitmap
import android.location.Address
import android.location.Geocoder
import android.net.Uri
import android.os.Build
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.os.Looper
import android.provider.MediaStore
import android.util.Log
import android.widget.*
import androidx.activity.result.ActivityResultLauncher
import androidx.activity.result.contract.ActivityResultContracts
import androidx.appcompat.app.AlertDialog
import androidx.core.app.ActivityCompat
import androidx.core.content.ContextCompat
import com.google.android.gms.location.*
import java.io.FileOutputStream
import java.io.IOException
import java.text.SimpleDateFormat
class SaveActivity : AppCompatActivity() {
// storage 권한 처리에 필요한 변수
val CAMERA = arrayOf(Manifest.permission.CAMERA)
val STORAGE = arrayOf(Manifest.permission.READ_EXTERNAL_STORAGE,
Manifest.permission.WRITE_EXTERNAL_STORAGE)
val CAMERA_CODE = 98
val STORAGE_CODE = 99
// 권한처리
lateinit var locationPermission: ActivityResultLauncher<Array<String>>
lateinit var fusedLocationClient: FusedLocationProviderClient
lateinit var locationCallback: LocationCallback
// 위도와 경도
var latitude:Double = 0.0
var longitude:Double = 0.0
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_save)
// 권한
locationPermission = registerForActivityResult(
ActivityResultContracts.RequestMultiplePermissions()) { results ->
if (!results.all { it.value }) {
Toast.makeText(this, "권한 승인이 필요합니다", Toast.LENGTH_LONG).show()
}
}
// 권한 요청
locationPermission.launch(
arrayOf(
Manifest.permission.ACCESS_COARSE_LOCATION,
Manifest.permission.ACCESS_FINE_LOCATION
)
)
fusedLocationClient = LocationServices.getFusedLocationProviderClient(this)
// 위치 정보를 실시간으로 받기 위한 함수를 호출
updateLocation()
//카메라 촬영
val saveCamBtn = findViewById<Button>(R.id.saveCamBtn)
saveCamBtn.setOnClickListener {
CallCamera()
}
// 이미지 경로
val saveTxtView = findViewById<TextView>(R.id.saveTxtView)
// 내용
val saveEditTxtContent = findViewById<EditText>(R.id.saveEditTxtContent)
//저장버튼
val saveContentBtn = findViewById<Button>(R.id.saveContentBtn)
saveContentBtn.setOnClickListener {
// 이미지 경로, 현재의 위치 주소, 내용
val address = getAddress(latitude, longitude)
var message = "위도:$latitude 경도:$longitude \n" +
"이미지 경로:${saveTxtView.text} \n" + //아직 아무것도 안 뜬곳 아닌가?
"내용:${saveEditTxtContent.text} "
// 우선은 출력해보자!
AlertDialog.Builder(this@SaveActivity)
.setTitle("대화 상자")
.setMessage(message)
.setCancelable(false)
.setNeutralButton("닫기", DialogInterface.OnClickListener { dialog, which ->
}).show()
DBHelper.getInstance(this, "localInfo.db")
.insert(
SaveInfoData(0,
saveTxtView.text.toString(),
address,
saveEditTxtContent.text.toString(),
"")
)
Toast.makeText(this, "저장되었습니다", Toast.LENGTH_SHORT).show()
val intent = Intent(this, MainActivity::class.java)
startActivity(intent)
}
}
fun getAddress(lat:Double, lon:Double):String{
val geocoder:Geocoder = Geocoder(this)
var list: List<Address>? = null
var address = ""
try {
list = geocoder.getFromLocation(
lat, // 위도
lon, // 경도
10
) // 얻어올 값의 개수
} catch (e: IOException) {
e.printStackTrace()
Log.e("test", "입출력 오류 - 서버에서 주소변환시 에러발생")
}
if (list != null) {
if (list.isEmpty()) {
Toast.makeText(this, "해당되는 주소 정보는 없습니다", Toast.LENGTH_LONG).show()
} else {
address = list[0].getAddressLine(0).toString()
}
}
return address
}
// 카메라 권한, 저장소 권한
// 요청 권한
override fun onRequestPermissionsResult(requestCode: Int,
permissions: Array<out String>, grantResults: IntArray) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults)
when(requestCode){
CAMERA_CODE -> {
for (grant in grantResults){
if(grant != PackageManager.PERMISSION_GRANTED){
Toast.makeText(this, "카메라 권한을 승인해 주세요", Toast.LENGTH_LONG).show()
}
}
}
STORAGE_CODE -> {
for(grant in grantResults){
if(grant != PackageManager.PERMISSION_GRANTED){
Toast.makeText(this, "저장소 권한을 승인해 주세요", Toast.LENGTH_LONG).show()
}
}
}
}
}
// 다른 권한등도 확인이 가능하도록
fun checkPermission(permissions: Array<out String>, type:Int):Boolean{
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.M){
for (permission in permissions){
if(ContextCompat.checkSelfPermission(this, permission)
!= PackageManager.PERMISSION_GRANTED){
ActivityCompat.requestPermissions(this, permissions, type)
return false
}
}
}
return true
}
// 카메라 촬영 - 권한 처리
fun CallCamera(){
if(checkPermission(CAMERA, CAMERA_CODE) && checkPermission(STORAGE, STORAGE_CODE)){
val itt = Intent(MediaStore.ACTION_IMAGE_CAPTURE)
startActivityForResult(itt, CAMERA_CODE)
}
}
// 사진 저장
fun saveFile(fileName:String, mimeType:String, bitmap: Bitmap): Uri?{
var CV = ContentValues()
// MediaStore 에 파일명, mimeType 을 지정
CV.put(MediaStore.Images.Media.DISPLAY_NAME, fileName)
CV.put(MediaStore.Images.Media.MIME_TYPE, mimeType)
// 안정성 검사
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q){
CV.put(MediaStore.Images.Media.IS_PENDING, 1)
}
// MediaStore 에 파일을 저장
val uri = contentResolver.insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, CV)
if(uri != null){
var scriptor = contentResolver.openFileDescriptor(uri, "w")
val fos = FileOutputStream(scriptor?.fileDescriptor)
bitmap.compress(Bitmap.CompressFormat.JPEG, 100, fos)
fos.close()
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q){
CV.clear()
// IS_PENDING 을 초기화
CV.put(MediaStore.Images.Media.IS_PENDING, 0)
contentResolver.update(uri, CV, null, null)
}
}
return uri
}
// 결과
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
val imageView = findViewById<ImageView>(R.id.avatars)
val saveTxtView = findViewById<TextView>(R.id.saveTxtView)
if(resultCode == Activity.RESULT_OK){
when(requestCode){
CAMERA_CODE -> {
if(data?.extras?.get("data") != null){
val img = data?.extras?.get("data") as Bitmap
val uri = saveFile(RandomFileName(), "image/jpeg", img)
imageView.setImageURI(uri)
Toast.makeText(this, "$uri", Toast.LENGTH_LONG).show()
println("이미지 경로: $uri")
// 이미지 경로를 TextView 에 저장
saveTxtView.text = getPath(uri)
println("실제 이미지 경로:" + getPath(uri))
}
}
STORAGE_CODE -> {
val uri = data?.data
imageView.setImageURI(uri)
}
}
}
}
// 파일명을 날짜 저장
fun RandomFileName() : String{
val fileName = SimpleDateFormat("yyyyMMddHHmmss").format(System.currentTimeMillis())
return fileName
}
// 실제 경로로 변경해 주는 함수
// Uri -> String
fun getPath(uri: Uri?): String {
val projection = arrayOf<String>(MediaStore.Images.Media.DATA)
val cursor: Cursor = managedQuery(uri, projection, null, null, null)
startManagingCursor(cursor)
val columnIndex: Int = cursor.getColumnIndexOrThrow(MediaStore.Images.Media.DATA)
cursor.moveToFirst()
return cursor.getString(columnIndex)
}
// 위치 취득 함수
@SuppressLint("MissingPermission")
fun updateLocation(){
val locationRequest = LocationRequest.create()
locationRequest.run {
priority = LocationRequest.PRIORITY_HIGH_ACCURACY
interval = 1000
}
// 실시간으로 자신의 위치를 취득
locationCallback = object : LocationCallback(){
// 1초에 한번씩 변경된 위치 정보가 onLocationResult 로 전달된다
/*override fun onLocationResult(locationResult: LocationResult?) {
locationResult?.let {
for (location in it.locations){
Log.d( "위치정보", " - 위도:${location.latitude} 경도:${location.longitude}")
// 변수에 저장한다
latitude = location.latitude
longitude = location.longitude
}
}
}*/
override fun onLocationResult(locationResult: LocationResult) {
locationResult?.let {
for (location in it.locations){
Log.d( "위치정보", " - 위도:${location.latitude} 경도:${location.longitude}")
// 변수에 저장한다
latitude = location.latitude
longitude = location.longitude
}
}
}
}
// 권한 처리
fusedLocationClient.requestLocationUpdates(locationRequest, locationCallback, Looper.myLooper()!!)
}
}
SaveInfoData.kt
package com.example.mydocument
import android.os.Parcel
import android.os.Parcelable
class SaveInfoData(var seq: Int, var imagePath: String?,
var location: String?, var content:String?, var date:String?): Parcelable {
constructor(parcel: Parcel) : this(
parcel.readInt(), //seq
parcel.readString(), //imagepath
parcel.readString(), //location
parcel.readString(), //content
parcel.readString()
) {
}
override fun writeToParcel(parcel: Parcel, flags: Int) {
parcel.writeInt(seq)
parcel.writeString(imagePath)
parcel.writeString(location)
parcel.writeString(content)
parcel.writeString(date)
}
override fun describeContents(): Int {
return 0
}
companion object CREATOR : Parcelable.Creator<SaveInfoData> {
override fun createFromParcel(parcel: Parcel): SaveInfoData {
return SaveInfoData(parcel)
}
override fun newArray(size: Int): Array<SaveInfoData?> {
return arrayOfNulls(size)
}
}
}
실행화면
'코딩 기록 > Kotlin' 카테고리의 다른 글
Comments