Skip to content

Core Function & How to build💻

heewon edited this page Jul 17, 2020 · 2 revisions

🚪로그인 Kakao api, Google api 사용

  • firebaseAuth, googleSignInClient, user, RC_SIGN_IN 객체를 전역으로 선언
    private lateinit var firebaseAuth: FirebaseAuth  
    private lateinit var googleSignInClient: GoogleSignInClient  
    private val RC_SIGN_IN = 99 //private const val TAG = "GoogleActivity"  
    val user = FirebaseAuth.getInstance().currentUser**

  • GoogleSignInOption객체를 구성할 때 requestIdToken을 호출
  • 로그인 버튼을 눌렀을 때 signIn 함수 실행되고 구글 계정 인증 Activity가 실행
    imagebutton_login_google.setOnClickListener {  
      signIn()  
        Log.d("GoogleLogIn", "버튼 눌림")  
    }  
    val gso = GoogleSignInOptions.Builder(GoogleSignInOptions.DEFAULT_SIGN_IN)  
        .requestIdToken(getString(R.string.default_web_client_id))  
        .requestEmail()  
        .build()
    private fun signIn() {  
        val signInIntent = googleSignInClient.signInIntent  
      startActivityForResult(signInIntent, RC_SIGN_IN)  
    }

  • 로그인이 정상적으로 수행되고 requestCode가 RC_SIGN_IN이면 firebaseWithGoogle함수 호출
	if (requestCode == RC_SIGN_IN) {  
    val task = GoogleSignIn.getSignedInAccountFromIntent(data)  
    try {  
		  val account = task.getResult(ApiException::class.java)  
	      firebaseAuthWithGoogle(account!!)  
		  } catch (e: ApiException) {  
			  Log.w("LoginActivity", "Google sign in failed", e)  
      }  
	}

  • GoogleSignInAccount객체에서 IdToken을 가져와 FirebaseAuth로 교환하고 인증

➡️ 성공적으로 수행되면 RetrofitClient를 통해서 서버로 userName, userTokenGoogle, userUploadUser 송신

	private fun firebaseAuthWithGoogle(acct: GoogleSignInAccount) {  
	    Log.d("LoginActivity", "firebaseAuthWithGoogle:" + acct.id!!)  
  
	  val credential = GoogleAuthProvider.getCredential(acct.idToken, null)  
  
    firebaseAuth.signInWithCredential(credential)  
        .addOnCompleteListener(this) { task ->  
	  if (task.isSuccessful) {  
                val user = FirebaseAuth.getInstance().currentUser  
  
  
     RetrofitClient.create(InterfaceSignUp::class.java).interfaceSignUp(  
                    userName, userTokenGoogle, null, userUploadImage  
                )  
                    .enqueue(object : Callback<ResponseSignUpModel> {  
                        override fun onFailure(call: Call<ResponseSignUpModel>, t: Throwable) {  
                            Log.d("SignUp Google", "${t}")  
                        }  
  
                        override fun onResponse(  
                            call: Call<ResponseSignUpModel>,  
	  response: Response<ResponseSignUpModel>  
                        ) {  
                            if(response.isSuccessful){  
                                if(response.body()!!.success){  
                                    Log.d("SignUp 통신성공", "통신성공")  
  
                                    preference.setUserIdx(response.body()!!.data.toString().toInt())  
  
                                    Log.w("LoginActivity", "firebaseAuthWithGoogle 성공", task.exception)  
                                    toMainActivity(firebaseAuth?.currentUser)  
                                }  
                            }  
  
                        }  
                    })  
  
            } else {  
                Log.w("LoginActivity", "firebaseAuthWithGoogle 실패", task.exception)  
                Snackbar.make(constraintlayout_login, "로그인에 실패하였습니다.", Snackbar.LENGTH_SHORT)  
                    .show()  
            }  
        }  
	}
	
	fun toMainActivity(user: FirebaseUser?) {  
	    if (user != null) { // MainActivity 로 이동  
	  startActivity(Intent(this, MainActivity::class.java))  
	        finish()  
	    }  
	}

Kakao api

  • Application을 상속하는 GlobalAppication.kt

  • KakaoAdapter를 상속하는 KakaoSDKAdapter

  • SessionCallback.kt를 별도로 생성

<SessionCallback.kt>

  • SessionCallback.kt에서 로그인 세션이 성공했을 때와 실패했을 때의 행동을 정의
    override fun onSessionOpenFailed(exception: KakaoException?) {  
        Log.e("KaKaoLogin","Session Call back :: onSessionOpenFailed ${exception?.message}")  
    }  
    override fun onSessionOpened() {  
      
        UserManagement.getInstance().me(object : MeV2ResponseCallback() {  
      
            override fun onFailure(erroResult: ErrorResult?) {  
                Log.i("KaKaoLogin", "Session Call back:: on failed ${erroResult?.errorMessage}")  
            }  
      
            override fun onSessionClosed(errorResult: ErrorResult?) {  
                Log.i("KaKaoLogin", "Session Call back:: on Closed ${errorResult?.errorMessage}")  
            }  
      
            override fun onSuccess(result: MeV2Response?) {  
                Log.d("KaKaoLogin","성공했습니다.")  
                startInetnt()  
                checkNotNull(result) { "session response null" }  
      }  
        })  
    }



🎨Drawing 기능 구현

KakaoTalk_20200717_224015161

CanvasDrawingFragment.kt

    private var isDrew = false
    
    override fun initCanvas() {
        signaturepad.setOnSignedListener(object : SignaturePad.OnSignedListener {
            override fun onStartSigning() {
                isDrew = true
            }

            override fun onClear() {
                isDrew = false
            }

            override fun onSigned() {
                //Doing nothing. prevent error.
                signaturepad
            }
        })
    }

    override fun onTrashed() {
        signaturepad.clear()
    }

📌 OnSignedListener를 view에 설정

  • onStartSigning() : pad를 터치했을 때 isDrew의 값이 true로 변경
  • onClear() : pad에 그려진 내용을 지울 때 이벤트 발생
  • onTrached() : pad에 그려진 그림을 전체 삭제



💾Drawing 파일처리 및 저장

✔️ 그린 그림을 DB에 저장

CanvasDrawingFragment.kt

    private fun saveCardIntoDB(bitmap: Bitmap) {
        savedCardRepository.insert(
            SavedCardEntity(preference.getProjectIdx()!!, preference.getRoundIdx()!!, SavedCardEntity.FALSE, SavedCardEntity.DRAWING,
                BitmapConverter.bitmapToString(bitmap), null
            )
        )
    }

✔️ 비트맵을 문자열로 변환하여 DB에 저장

BitmapConverter.kt

    object BitmapConverter {

        private const val TAG = "BitmapConverter"
        private const val QUALITY = 70

        // String -> Bitmap
        fun stringToBitmap(encodedString: String?): Bitmap? {
            return try {
                val encodeByte: ByteArray = Base64.decode(encodedString, Base64.DEFAULT)
                BitmapFactory.decodeByteArray(encodeByte, 0, encodeByte.size)

            } catch (e: Exception) {
                Log.e(TAG, e.message.toString())
                null
            }
        }

        //Bitmap -> String
        fun bitmapToString(bitmap: Bitmap): String {
            val baos = ByteArrayOutputStream()
            bitmap.compress(Bitmap.CompressFormat.PNG, QUALITY, baos)

            val bytes: ByteArray = baos.toByteArray()
            return Base64.encodeToString(bytes, Base64.DEFAULT)
        }

        //Bitmap -> ByteArray
        fun bitmapToByteArray(bitmap: Bitmap): ByteArray {
            val baos = ByteArrayOutputStream()
            bitmap.compress(Bitmap.CompressFormat.JPEG, QUALITY, baos)
            return baos.toByteArray()
        }
    } 



📶Socket 통신

SocketClientObject

	object SocketClient {  
    private val TAG = javaClass.name  
  
	  //Todo: URL 받으면 추가하기  
		  private const val SERVER_URL = "http://52.78.113.197:3000"  
  
	  private var socket: Socket? = null  
  
	 fun getInstance() : Socket? {  
        if (socket == null) {  
            socket = init()  
        }  
  
        return socket  
	  }  
	    fun connection(){  
        socket?.connect()  
    }  
  
    private fun init(): Socket? {  
        if (socket == null) {  
            try {  
                socket = IO.socket(SERVER_URL)  
            } catch (e: URISyntaxException) {  
                e.printStackTrace()  
            }  
        }  
  
        Log.d(TAG, socket.toString())  
  
        return socket  
	  }  
  
    fun responseEvent(event: String, callback: Emitter.Listener) {  
        if (init() != null) {  
            socket!!.on(event, callback)  
        } else {  
            Log.d(TAG, "initial is failed")  
        }  
    }  
  
    fun sendEvent(event: String, data: String) : Boolean {  
        return if (init() != null) {  
            try {  
                socket!!.emit(event, data)  
                true  
	  } catch (e: JSONException) {  
                e.printStackTrace()  
                false  
	  }  
        } else {  
            Log.d(TAG, "initial is failed")  
            false  
		  }  
	    } 
	}

HostRoundSettingFragment

    fun joinRoundRoom(){  
      
        SocketClient.getInstance()  
        SocketClient.connection()  
      
        SocketClient.sendEvent("joinRoom", "roomCode")  
        SocketClient.sendEvent("roundSetting", "roomCode")  
      
        SocketClient.responseEvent("roundComplete", Emitter.Listener {  
      Log.d("SocketJoinRoom", "Success.")  
      
            //방에 들어갔으면 프래그먼트 전환  
      goToFragment(RoundStartFragment::class.java, null)  
        })  
    }

fun refreshParticipantSocket() {  
    SocketClient.getInstance()  
    SocketClient.connection()  
  
    SocketClient.responseEvent("roundComplete", Emitter.Listener {  
  Log.d("refresh_socket", "참가자가 들어왔습니다.")  
        showRoundUserLIst(preference.getRoundIdx()!!)  
    })  
}



🎥Animation 적용

1️⃣ Lottie Animation : 스플래쉬, 로그인, 라운드 대기 중 로딩

📌 로그인 애니메이션

KakaoTalk_20200717_220423480

    private fun initView() {  
        val animationView = findViewById<LottieAnimationView>(R.id.lottieanimation_login)  
        animationView.setAnimation("login_bg.json")  
        animationView.repeatCount = INFINITE  
      animationView.playAnimation()  
    }

📌 라운드 대기 중 로딩 애니메이션

KakaoTalk_20200717_223922331

    <com.airbnb.lottie.LottieAnimationView
                android:id="@+id/lottieAnimationView"
                android:layout_width="45dp"
                android:layout_height="15dp"
                android:layout_marginStart="10dp"
                app:layout_constraintBottom_toBottomOf="@+id/textview_round_ready"
                app:layout_constraintEnd_toEndOf="parent"
                app:layout_constraintStart_toEndOf="@+id/textview_round_ready"
                app:layout_constraintTop_toTopOf="@+id/textview_round_ready"
                app:lottie_autoPlay="true"
                app:lottie_rawRes="@raw/loading_animation"
                app:lottie_loop="true" />

📢Notification




📌 핵심 기능 화면 정리

readme2