2026/03/03

오늘의 이야기

https://developers.google.com/identity/one-tap/android/get-saved-credentials#disable-one-tap



 


저장된 자격 증명으로 사용자 로그인  |  One Tap for Android  |  Google Developers


경고 : 이 데이터는 Google 사용자 데이터 정책에 따라 제공됩니다. 정책을 검토하고 준수하십시오. 그렇게하지 않으면 프로젝트가 정지되거나 계정이 정지 될 수 있습니다. 이 페이지는 Cloud Transl


developers.google.com




 


16: Caller has been temporarily blocked due to too many canceled sign-in prompts.  이런 오류 메시지을 만나게 되면


 


당황스럽지 않을까 ?  구글에서 찾아보니 이런 해결책이 있었다. 나의 경우는 


 


참고 : 개발 과정이 24 시간 재사용 대기 기간이 발생하는 경우, 당신은 구글 플레이 서비스 '응용 프로그램 저장을 취소하여 재사용 대기 시간을 초기화 할 수 있습니다. 또한, 테스트 장치에이 재사용 대기 시간 ON / OFF 전환 및 / 또는 에뮬레이터 모두, 단순히 걸기 응용 프로그램 입력에 다음 코드를 이동 : *#*#66382723#*#* . 제출 시 피드백은 없지만 다이얼러는 모든 입력을 지우고 닫힐 수 있습니다. 이 후에는 쿨다운을 해제해야 합니다. 다시 켜려면 같은 코드를 다시 입력하세요.


 


이런 가이드를 따라 하는 것으로 해결이 완결 되었다.


 


 





오늘의 이야기


#스하리1000명프로젝트,
Đôi khi thật khó để nói chuyện với người lao động nước ngoài phải không?
Tôi đã tạo một ứng dụng đơn giản có ích! Bạn viết bằng ngôn ngữ của bạn và những người khác nhìn thấy nó bằng ngôn ngữ của họ.
Nó tự động dịch dựa trên cài đặt.
Siêu tiện dụng để trò chuyện dễ dàng. Hãy xem khi bạn có cơ hội!
https://play.google.com/store/apps/details?id=com.billcoreatech.multichat416




오늘의 이야기

https://amirdiafi.medium.com/how-i-code-for-8-hours-without-feeling-tired-3d2b22f917af



 


👨🏻‍💻How I code for 8 hours without feeling tired.


🔴 I have coded wrong my whole life.


amirdiafi.medium.com




 


인터넷 펌 글...   그냥 읽어보면서 느낌을 알아야 하는 글...





오늘의 이야기

https://developer.android.com/studio/build/shrink-code.html?hl=ko#kts 



 


앱 축소, 난독화 및 최적화  |  Android 개발자  |  Android Developers


사용하지 않는 코드와 리소스를 삭제하기 위해 출시 빌드에서 코드를 축소하는 방법을 알아보세요.


developer.android.com




playstore에 앱을 등록하려고 하다 보면 코드 난독화를 통해 debugging을 대비 하라는 요구(?)를 받게 된다. 


그래서 한번 해 보기로 했다.  먼저 gradle 에 적용을 해 보았다.


 


buildTypes {
debug {
buildConfigField "Boolean", "DEBUG_MODE", "true"
}
release {
buildConfigField "Boolean", "DEBUG_MODE", "false"
minifyEnabled true
shrinkResources true
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}

 


새로 빌드된 앱을 playstore 에 올리고 게시가 되기를 기다렸다가,  실행해 보았다. 개발할 때는 오류가 나지 않던 앱의 실행에 문제가 생긴다. 왜?  오류를 확인하기 위한 log을 찍어보고 찾아보고... 흠흠...  하다가 혹시나 하고 생성된 database을 삭제해 보았다.


 


그리고 실행된 후 새로 실행해서 보았더니, database 가 그림과 같이 생성이 되어 버렸다.  코딩할 때 생성했던 코드는 이런데도 말이다.


 


...

data class ChatRooms(
var chatRooms:String = "",
var chatTitle:String = "",
var roomOwner:String = "",
var chatNo:String = "",
var locale:String = "",
var badUser:String = "",
var chatGuests:Int = 0,
var secret:Boolean = false,
var passwd:String = ""
)

 


코드 축소된 경우의 realtime database



이유는 무었일까 ??? 


 


생각을 하다 보니, 코드 난독화에서 발생하는 현상이었다. 


 


이제 이 난국(?)을 어떻게 해소할 것인가 ?


 


 


 


 


 


 


 


 


 


 


 


 


 


 


 


난독화는 뭐래 ?



 


그 방법은 다음과 같이 설명이 되어 있다.  


유지할 코드 설정



 


이제 실체를 확인해 보아야겠다.   결과는 다음에...





오늘의 이야기


#billcorea #운동동아리관리앱
🏸 Schneedle, một ứng dụng cần có cho các câu lạc bộ cầu lông!
👉 Đấu trận – Ghi điểm & Tìm đối thủ 🎉
Hoàn hảo cho mọi nơi, một mình, với bạn bè hoặc trong câu lạc bộ! 🤝
Nếu bạn thích cầu lông, nhất định phải thử nó

Vào ứng dụng 👉 https://play.google.com/store/apps/details?id=com.billcorea.matchplay




오늘의 이야기

이래도 되는 건가?
비둘기에게 강냉이를 뿌렸나 보다



재네들은 파티(?)중
나는 산보 중...





오늘의 이야기

이미지 저장 예시



앱을 만들다 보면 프로필 가져오기 기능을 구현해 보는 경우가 간혹 생긴 게 된다. 


 


오늘은 compose을 이용한 구현을 하는 과정에서 


갤러리에서 이미지를 불러와서 프로필 사진으로 저장하는 과정을 구현해 보고자 한다.


 


그림과 같이 구현해 볼 예정이다. 


 


 


 


 


 


 


 


 


 


 


 


 


 


 


 


 


 


 


 


 


 


 


 


 


 


전체 소스의 일부는 아래와 같이 구현이 되었다.


    @Composable
private fun mainContent(padding: Modifier) {

Column(modifier = Modifier
.fillMaxHeight()
.fillMaxWidth()
.padding(16.dp),
verticalArrangement = Arrangement.Top,
horizontalAlignment = Alignment.Start
) {
var imageUri by remember {
mutableStateOf<Uri?>(null)
}
var imageTy by remember {
mutableStateOf<Boolean>(false)
}
val context = LocalContext.current
val bitmap = remember {
mutableStateOf<Bitmap?>(null)
}
val launcher = rememberLauncherForActivityResult(contract =
ActivityResultContracts.GetContent()) { uri: Uri? ->
imageUri = uri
}

Card(modifier = Modifier
.fillMaxWidth()
.height(150.dp)
.padding(8.dp)
.border(1.dp, Color.Gray),
content = {
Text(getString(R.string.profileImage))
Row (
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.End
)
{
imageUri?.let {
if (Build.VERSION.SDK_INT < 28) {
bitmap.value = MediaStore.Images
.Media.getBitmap(context.contentResolver,it)
} else {
val source = ImageDecoder
.createSource(context.contentResolver,it)
bitmap.value = ImageDecoder.decodeBitmap(source)
}
bitmap.value?.let { btm ->
val baos = ByteArrayOutputStream()
btm.compress(
Bitmap.CompressFormat.PNG,
100,
baos
)
val b: ByteArray = baos.toByteArray()
val encoded: String = Base64.encodeToString(b, Base64.DEFAULT)
editor.putString("profileImage", encoded)
editor.commit()
Image(bitmap = btm.asImageBitmap(),
contentDescription = "profile",
contentScale = ContentScale.Crop,
modifier = Modifier
.clip(shape = RoundedCornerShape(16.dp))
.size(150.dp, 250.dp))
}
}
if (!"".equals(sp.getString("profileImage","")) && !imageTy) {
var encoded = sp.getString("profileImage","")
val imageAsBytes: ByteArray =
Base64.decode(encoded?.toByteArray(), Base64.DEFAULT)
var bitMap = BitmapFactory.decodeByteArray(imageAsBytes, 0, imageAsBytes.size)
Image(bitmap = bitMap.asImageBitmap(),
contentDescription = "profile",
contentScale = ContentScale.Crop,
modifier = Modifier
.clip(shape = RoundedCornerShape(16.dp))
.size(150.dp, 250.dp))
}
Spacer(modifier = Modifier.padding(10.dp))
IconButton(onClick = {
imageTy = true
launcher.launch("image/*")
}) {
Icon(imageVector = Icons.Default.PhotoAlbum, contentDescription = "Search Profile", tint = Color.Blue)
}
}
})
Text(getString(R.string.title_translate_ty))
Spacer(modifier = Modifier.padding(10.dp))
Card(modifier = Modifier
.fillMaxWidth()
.height(60.dp)
.padding(8.dp)
.border(1.dp, Color.Gray),
content = {
Row (verticalAlignment = Alignment.CenterVertically)
{
if (isTranslate.value) {
Text(text = getString(R.string.msgAutoTranslate))
} else {
Text(text = getString(R.string.msgTranslateNo))
}
Switch(checked = isTranslate.value, onCheckedChange = {
isTranslate.value = it
})
}

})
Spacer(modifier = Modifier.padding(10.dp))
Row (verticalAlignment = Alignment.CenterVertically)
{
Text(getString(R.string.title_master_language))
}
Spacer(modifier = Modifier.padding(10.dp))
Card(modifier = Modifier
.fillMaxWidth()
.height(60.dp)
.padding(8.dp)
.border(1.dp, Color.Gray),
content = {
if (languages.size > 0) {
DropdownDemo()
}
})
}
}

여기서 살펴 보아야 하는 부분은 다음과 같다.


 


갤러리에 있는 이미지를 불러오는 실행은 launcher을 호출해서 실행을 하고 있고.


launcher.launch("image/*")

 


실행된 결과를 아래와 같은 코드 구현을 통해서 bitmap 이미지를 변환 하여 화면에 Image에 속성을 넣어주고 있는 것을 확인할 수 있었다.


                       imageUri?.let {
if (Build.VERSION.SDK_INT < 28) {
bitmap.value = MediaStore.Images
.Media.getBitmap(context.contentResolver,it)
} else {
val source = ImageDecoder
.createSource(context.contentResolver,it)
bitmap.value = ImageDecoder.decodeBitmap(source)
}
bitmap.value?.let { btm ->
val baos = ByteArrayOutputStream()
btm.compress(
Bitmap.CompressFormat.PNG,
100,
baos
)
val b: ByteArray = baos.toByteArray()
val encoded: String = Base64.encodeToString(b, Base64.DEFAULT)
editor.putString("profileImage", encoded)
editor.commit()
Image(bitmap = btm.asImageBitmap(),
contentDescription = "profile",
contentScale = ContentScale.Crop,
modifier = Modifier
.clip(shape = RoundedCornerShape(16.dp))
.size(150.dp, 250.dp))
}

이런 코드를 Kotlin을 통해서 구현을 한다고 하면 아마 다음과 같은 구현이 될 것 같다. 


 


 


intent을 통해서 갤러리에 있는 이미지를 열어 오는 구현을 하고...


Intent(Intent.ACTION_GET_CONTENT).apply {
type = "image/*"
startActivityForResult(
Intent.createChooser(this, "Get Album"),
REQ_SELECT_IMG
)
}

 


onActivityResult을 통해서 받아온 이미지를 처리하는 모양으로 구현이 될 것 같은데...


    override fun onActivityResult(requestCode: Int, resultCode: Int, intent: Intent?) {
super.onActivityResult(requestCode, resultCode, intent)

if (resultCode == Activity.RESULT_OK) {
when (requestCode) {
REQ_SELECT_IMG -> {

val currentImageUri = intent?.data ?: return //이미지 URL

val needAdjust = true
if (needAdjust) {
setAdjImgUri(currentImageUri) //방법 2
} else {
setImgUri(currentImageUri) //방법 1
}
}

}
}
}

위에서 구현된 코드와 비교를 해 보면 훨씬 간략해졌음을 느끼게 된다. 


 


 


java 코드로 구현된 예시는 아래 링크를 참고해 보면 좋을 것 같다.


https://billcorea.tistory.com/40



 


안드로이드 앱 만들기 갤러리 에서 이미지 받아오기 (자동회전 방지)


앱을 만들다가... 갤러리에서 이미지 받아오는 와서 사용하는 것을 구현하고 있는 중인데... 사진이 돌아간다. 흑~ 그래서 구글링 신에서 질문을 했다... 답... Exif 을 구현해서 사진을 돌리는 코드


billcorea.tistory.com




 


또한 compose에서 구현은 아래 site에서 참고했음을 밝힌다.


https://yjyoon-dev.github.io/android/2022/04/09/android-05/



 


[Android] 서버에 이미지 업로드하기(feat. Android 10, Compose)


Android 10 이상에서 Jetpack Compose를 통해 기기의 이미지를 선택한 뒤 서버에 업로드해보는 과정을 알아보자


yjyoon-dev.github.io




 





오늘의 이야기


#스하리1000명프로젝트,
迷失在韩国?即使您不会说韩语,这个应用程序也可以帮助您轻松出行。
只需说出您的语言即可 - 它会翻译、搜索并以您的语言显示结果。
非常适合旅行者!支持英语、日语、中文、越南语等10多种语言。
现在就试试吧!
https://play.google.com/store/apps/details?id=com.billcoreatech.opdgang1127




2026/03/02

오늘의 이야기

 




계절의 여왕이라는 오월

길가에 장미가 싱그럽다. 아침 햇쌀을 맞는 장미를 보며, 나의 시간들도 싱그럽기를 기원한다.





오늘의 이야기



길가에 핀 들꽃이 예쁘다.
이름도 모르기는 하나  그냥 이쁘다.
더위에 지쳐가는 마음에 작은 바람이 불게 한다.





오늘의 이야기



#스치니1000프로젝트 #재미 #행운기원 #Compose #Firebase

🎯 야 너 토요일마다 로또 확인하냐?
나도 맨날 "혹시나~" 하면서 봤거든 ㅋㅋ

근데 이제는 그냥 안 해
AI한테 맡겼어 🤖✨

그것도 구글 Gemini로다가!

그래서 앱 하나 만들었지
👉 "로또 예상번호 by Gemini" 🎱

AI가 분석해서 번호 딱! 뽑아줌
그냥 보고 참고만 하면 됨

재미로 해도 좋고…
혹시 모르는 거잖아? 😏


https://play.google.com/store/apps/details?id=com.billcorea.gptlotto1127




오늘의 이야기

새순 따스해지는 기온을 느끼며, 이제는 봄날이 올 거라는 믿음이 조금은 강하게 느껴져 옵니다.  입춘도 지났고 경칩까지는 아직 보름도 넘게 남아 있기는 하나,  그제보다는 어제가, 어제 보다는 오늘이 조금은 더 따스하게 느껴져 옵니다. 동백   동백은...