
36개월차 우리차의 주행거리
힘들어겠다...
올해도 무사히 잘 다녀 보자...
빌코리아의 홈페이지 입니다.
https://ssaurel.medium.com/create-a-blur-effect-on-android-with-renderscript-aa05dae0bd7d
Create a Blur Effect on Android with RenderScript
In image processing, a Blur Effect, also known as Gaussian Blur, is the result of blurring an image by applying a Gaussian function. The…
ssaurel.medium.com
먼저 출처를 밝혀본다... 앱안에 이미지를 넣을껀데... 이미지을 흐릿하게 만들고 싶을 경우가 있을 것 같다. 이걸 구현하는 예제를 찾았다. 그래서 잠시 옮겨 볼까 한다. 먼저 글쓴이분에게 심심한 감사를 표하며... 따라하기를 해 보겠다.
gradle 파일에 추가하기...
plugins {
id 'com.android.application'
id 'com.google.gms.google-services'
id 'com.google.firebase.crashlytics'
}
android {
...
compileSdkVersion 32
defaultConfig {
applicationId "com.nari.notify2kakao"
minSdkVersion 26
targetSdkVersion 32
versionCode 21
versionName '1.2.4'
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
renderscriptTargetApi 18
renderscriptSupportModeEnabled true
}
....
}
dependencies {
...
}renderscript 을 이용하는 것이라고 하는데... 2줄 추가 했다.
renderscriptTargetApi 18
renderscriptSupportModeEnabled true
다음은 blur 처리하는 class 을 하나 만들어 보겠다.
import android.content.Context;
import android.graphics.Bitmap;
import androidx.renderscript.Allocation;
import androidx.renderscript.Element;
import androidx.renderscript.RenderScript;
import androidx.renderscript.ScriptIntrinsicBlur;
public class BlurBuilder {
private static final float BITMAP_SCALE = 0.6f;
private static final float BLUR_RADIUS = 15f;
public static Bitmap blur(Context context, Bitmap image) {
int width = Math.round(image.getWidth() * BITMAP_SCALE);
int height = Math.round(image.getHeight() * BITMAP_SCALE);
Bitmap inputBitmap = Bitmap.createScaledBitmap(image, width, height, false);
Bitmap outputBitmap = Bitmap.createBitmap(inputBitmap);
RenderScript rs = RenderScript.create(context);
ScriptIntrinsicBlur intrinsicBlur = ScriptIntrinsicBlur.create(rs, Element.U8_4(rs));
Allocation tmpIn = Allocation.createFromBitmap(rs, inputBitmap);
Allocation tmpOut = Allocation.createFromBitmap(rs, outputBitmap);
intrinsicBlur.setRadius(BLUR_RADIUS);
intrinsicBlur.setInput(tmpIn);
intrinsicBlur.forEach(tmpOut);
tmpOut.copyTo(outputBitmap);
return outputBitmap;
}
}이건 뭐 그냥 복붙 이라... ㅋ
다음은 나의 activity 에 추가해 본다. layout 에는 imageview 을 하나 넣었고, activity 에서는 다음과 같이 만들어 이미지를 넣어 보았다. 참 그전에 이미지를 넣을 bitmap 파일을 하나 drawable 밑에 추가해 주어야 한다.
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
binding = ActivityWithDrawView2Binding.inflate(getLayoutInflater());
setContentView(binding.getRoot());
...
Bitmap mango = BlurBuilder.blur(this, BitmapFactory.decodeResource(getResources(), R.drawable.mango));
ImageView imageView = findViewById(R.id.imageView2);
imageView.setImageBitmap(mango);
....
}우히... 이렇게만 코딩을 해 주면 다음과 같은 이미지의 변화를 볼 수 있다. 먼저 원본 이미지...

그 다음은 앱에 들어간 이미지는 어떻게 ???

깔끔했던 망고(?)가 흐릿하게 보인다...
또 하나의 이미지 새로운 처리 방법을 알게 되어 기쁘다...
아래 링크는 위 출처의 원본 : 배워야할 것들이 있어서 일단... 링크 keep !!!
https://medium.com/sampingan-tech/implementing-glassmorphism-in-android-app-e73a2fd83b80
Implementing Glassmorphism in Android App
Glassmorphism getting more popular, but how can we implement it in a real app? Especially on the android app. Let's talk about it.
medium.com

바탕 화면에 나오는 배경 사진 겨울 이야기... 눈사람 가족...
어린 시절에도 눈사람은 만들어 본 기억이 없는 것 같다. 그 시절에는 그런 걸 몰랐고, 어른이 돼서는 사는 게 뭔지?
흠.
이런 철부지 없는 삶이 어떤가 싶기도 하고. 어느새 나이가 들어 이게 뭐 하는 건가 싶기도 하고...
나 무엇을 위해 살고 있는 가? 예전에는 그저 평범한 삶이 최고 일 거라 생각했는데, 꼭 그런 것만은 아닌 것 같기도 하고
알 수 없는 게 사는 것 같아... 언제면 알게 될는지...
아직도 난 철부지 ?
어느날 받은 메일함이 기억나서 오늘도 나는 admob center 을 열어 보았다. ㅠㅠ;; 아니나 다를까 또 다른앱의 정책 위반 이야기가 나와 있다.

이번엔 무엇 때문에 이런일이 생길것인가 ??? 내용은 뭐 말 그대로
1. 의도하지 않은 광고 유도
2. 광고프레임 크기 변형
1번의 경우는 어떤때 발생하는 가 ? 아마도 변형된 Toast 때문에 발생한다고 보인다. 앱을 종료할 떄 알림을 주기 위해서 Toast 을 변형하고 그 안에 광고를 넣었다. 그랬더니 앱이 종료를 시도 할 떄 Toast 가 노출 되지만, 실제 앱이 종료된 이후에도 잔상(?)이 남아 있게 되면, 그것을 의도하지 않은 광고 유도 라고 판단하는 것 같다.
2번의 경우는 광고 프레임 변형 이라고 하는데, 그건 아마도 layout 배치를 하는 과정에서 banner 광고를 사용하고 있는데, banner 광고의 전체가 노출 되지 못하도록 layout 을 구성해서 그런것으로 이해가 된다.
그런걸 정리해야 하는 시기가 된 것이다.
그래서 오늘도 난 ... 밤을 보내고 있다. ㅋ~
https://developers.google.com/identity/one-tap/android/get-started
그간은 firebase 에서 지원하는 구글 로그인만 보고 있었는데, 오늘은 은인(?)을 만나게 되어 다른거 하나를 알게 되었다. 구글에서 말하는 로그인 / 가입 방식... 이걸 이용하면 웹 사이트 운영시에는 도움이 될 것 같기도 하다.
2024.10.16 다시 찾아 본 글에서 이제는 더 이상 one tap login 을 사용할 수 없을 듯 합니다.
저 자료를 보면서 구현을 해 보았다. 제일 먼저 구현할 부분은 gradle 파일에 설정하기
dependencies { ... implementation 'com.google.android.gms:play-services-auth:20.0.1' }가이드에 나와있는데로... 그 다음은 API 활용을 위해서 cloud 에 사용자 인증정보를 추가 하고 클라이언트ID을 받아서 챙겨 두자. 등록할 때 앱의 package 이름 그리고 SHA-1 디지털지문이 필요하므로 이것도 반드시 기억.여기서 주의할 부분을 찾았다. 가이드에 나와 있는 부분인데, 간과하고 넘어갔더니.
내가 안드로이드앱을 개발 한다고 해서 안드로이드 앱용 client id 을 만들어야 하는 게 아니였다. 그래서 수정본에서는 다음과 같이 구성을 만들었다.

위 가이드의 설명처럼 자바스크립트 원본 URI나 승인된 리디렉션 URI 등은 입력하지 않아도 된다. 잘 보고 따라 하시길...
import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.appcompat.app.AppCompatActivity; import android.content.Intent; import android.content.IntentSender; import android.os.Bundle; import android.util.Log; import com.google.android.gms.auth.api.identity.BeginSignInRequest; import com.google.android.gms.auth.api.identity.BeginSignInResult; import com.google.android.gms.auth.api.identity.Identity; import com.google.android.gms.auth.api.identity.SignInClient; import com.google.android.gms.auth.api.identity.SignInCredential; import com.google.android.gms.common.api.ApiException; import com.google.android.gms.common.api.CommonStatusCodes; import com.google.android.gms.tasks.OnFailureListener; import com.google.android.gms.tasks.OnSuccessListener; public class MainActivity2 extends AppCompatActivity { private static final int REQ_ONE_TAP = 100; private static final String TAG = "MainActivity"; boolean showOneTapUI = false ; private SignInClient oneTapClient; private BeginSignInRequest signInRequest; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main2); oneTapClient = Identity.getSignInClient(this); signInRequest = BeginSignInRequest.builder() .setPasswordRequestOptions(BeginSignInRequest.PasswordRequestOptions.builder() .setSupported(true) .build()) .setGoogleIdTokenRequestOptions(BeginSignInRequest.GoogleIdTokenRequestOptions.builder() .setSupported(true) // Your server's client ID, not your Android client ID. .setServerClientId(getString(R.string.default_web_client_id)) // Only show accounts previously used to sign in. .setFilterByAuthorizedAccounts(true) .build()) // Automatically sign in when exactly one credential is retrieved. .setAutoSelectEnabled(true) .build(); oneTapClient.beginSignIn(signInRequest) .addOnSuccessListener(this, new OnSuccessListener<BeginSignInResult>() { @Override public void onSuccess(BeginSignInResult result) { try { Log.e(TAG, "onSuccess..................."); startIntentSenderForResult( result.getPendingIntent().getIntentSender(), REQ_ONE_TAP, null, 0, 0, 0); } catch (IntentSender.SendIntentException e) { Log.e(TAG, "Couldn't start One Tap UI: " + e.getLocalizedMessage()); } } }) .addOnFailureListener(this, new OnFailureListener() { @Override public void onFailure(@NonNull Exception e) { // No saved credentials found. Launch the One Tap sign-up flow, or // do nothing and continue presenting the signed-out UI. Log.e(TAG, e.getLocalizedMessage()); } }); } @Override protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) { super.onActivityResult(requestCode, resultCode, data); switch (requestCode) { case REQ_ONE_TAP: try { SignInCredential credential = oneTapClient.getSignInCredentialFromIntent(data); String idToken = credential.getGoogleIdToken(); String username = credential.getId(); String password = credential.getPassword(); if (idToken != null) { // Got an ID token from Google. Use it to authenticate // with your backend. Log.e(TAG, "Got ID token."); } else if (password != null) { // Got a saved username and password. Use them to authenticate // with your backend. Log.e(TAG, "Got password."); } } catch (ApiException e) { switch (e.getStatusCode()) { case CommonStatusCodes.CANCELED: Log.e(TAG, "One-tap dialog was closed."); // Don't re-prompt the user. showOneTapUI = false; break; case CommonStatusCodes.NETWORK_ERROR: Log.e(TAG, "One-tap encountered a network error."); // Try again or just ignore. break; default: Log.e(TAG, "Couldn't get credential from result." + e.getLocalizedMessage()); break; } } break; } } @Override public void onBackPressed() { super.onBackPressed(); oneTapClient.signOut(); } }그 다음은 가이드에 나와있는 코드 일부를 뽑아서 나의 코드에 넣기... 설명은 oneTapClient 을 생성하고 signInRequest 을 build하고 oneTapClient.beginSignIn 을 통해서 호출하면 실행이 된다는 것이다. 여기서 발견한 것은 가이드에 오타(?)가 있는 것만 같은 생각이 들었다.

가이드에서는 oneTapClient.beginSignIn( signUpRequest ) 라고 되어 있으나, 코틀린 예제는 다르다. 아마도 오타(?)일 것 같은 생각이 든다. 뭐 암튼... 실행해 보면 다음 처럼 볼 수 있다.
앱을 실행해 보면 구글 One Tap 로그인을 할 수 있는 화면이 실행이 되었다. 다만, 저걸 클릭후 오류가 발생했는데...
Couldn't get credential from result.10: Developer console is not set up correctly
이런 오류를 만나게 되었다면, 위에서 설명한 것 처럼 웹 어플용 ID 을 설정해야 한다.
다른 메시지는 16: Cannot find a matching credential. 이런걸 만날 수 있다. 이 경우는 clound console 에
OAuth 2.0 클라이언트 ID가 등록 되지 않은 경우에 만날 수 있다.
또는 다음 링크와 같은 경우에도 만날 수 있다.
https://stackoverflow.com/questions/61768804/getting-16-cannot-find-a-matching-credential-when-doing-one-tap-sign-in-and-s
Getting "16: Cannot find a matching credential" when doing One Tap sign-in and sign-up on Android
I have been following the guide for setting up One Tap sign-in and sign-up on Android, and have used the example code to set up the One Tap client, but every time I call oneTapClient.beginSignIn(
stackoverflow.com
https://developer.android.com/courses/pathways/android-basics-kotlin-four
Add a button to an app | Android Basics in Kotlin - Intro - Add a button | Android Developers
Learn more concepts in Kotlin—including classes, objects, and conditionals—to create an interactive app for your users.
developer.android.com
앱을 하나 만들어 보았다. Kotlin 으로 하는 첫번째 프로젝트. 이 걸 해 보는 이유는 java 만으로 하는 앱은 많이 해 보았는데, kotlin 으로 하는 코딩은 처음인지라, 배워보고자 해서 developer 에서 코트랩을 살펴보았다.
ㅋ 구현되는 모습은 어떨까 ?
동작은 그냥 하는 주사위 모양이다. 동작하는 기능 구현은 아래 소스를 보는 도움이 될 것 같다.
import android.os.Bundle
import android.util.Log
import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity
import com.billcoreatech.kotlinexam0115.databinding.ActivityMain2Binding
class MainActivity2 : AppCompatActivity() {
var TAG: String = "MainActivity2";
lateinit var binding: ActivityMain2Binding ;
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityMain2Binding.inflate(layoutInflater)
setContentView(binding.root)
binding.button.setOnClickListener{
Log.e(TAG, "");
Toast.makeText(this, rollDice(), Toast.LENGTH_SHORT).show()
}
}
private fun rollDice(): String {
val dice = Dice(6)
val diceRoll = dice.roll()
binding.textView3.setText(diceRoll.toString())
when(diceRoll) {
1 -> binding.imageView2.setImageResource(R.drawable.dice_1)
2 -> binding.imageView2.setImageResource(R.drawable.dice_2)
3 -> binding.imageView2.setImageResource(R.drawable.dice_3)
4 -> binding.imageView2.setImageResource(R.drawable.dice_4)
5 -> binding.imageView2.setImageResource(R.drawable.dice_5)
6 -> binding.imageView2.setImageResource(R.drawable.dice_6)
}
return diceRoll.toString()
}
}
이게 java 로 구현이 되면 아래와 같이 이렇게 구현이 될 것 같다.
import android.os.Bundle;
import android.view.View;
import android.widget.Toast;
import androidx.appcompat.app.AppCompatActivity;
import com.billcoreatech.kotlinexam0115.databinding.ActivityMain2Binding;
public class MainActivity3 extends AppCompatActivity {
ActivityMain2Binding binding ;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
binding = ActivityMain2Binding.inflate(getLayoutInflater());
setContentView(binding.getRoot());
binding.button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
Toast.makeText(getApplicationContext(), rollDice() , Toast.LENGTH_SHORT).show();
}
});
}
private String rollDice() {
Dice dice = new Dice(6);
int iDice = dice.roll() ;
binding.textView3.setText(String.valueOf(iDice));
switch (iDice) {
case 1: binding.imageView2.setImageResource(R.drawable.dice_1); break ;
case 2: binding.imageView2.setImageResource(R.drawable.dice_2); break ;
case 3: binding.imageView2.setImageResource(R.drawable.dice_3); break ;
case 4: binding.imageView2.setImageResource(R.drawable.dice_4); break ;
case 5: binding.imageView2.setImageResource(R.drawable.dice_5); break ;
case 6: binding.imageView2.setImageResource(R.drawable.dice_6); break ;
}
return String.valueOf(iDice) ;
}
}아직은 어떤게 더 코드 작업이 수월하지는 잘 모르겠다. 이런 걸 여러번 하다 보면, 알게 될려는지... layout 은 그대로 하나 만들어서 했고, 구현하는 부분만 kotlin 으로, java 로 만들어 보았다.
어떻게 달라질지...