APK로 직접 설치하는 경우, 삼성 OS 내의 기능으로 인해 몇몇 기능이 정상 작동되지 않을 수 있다.

 

삼성 OS의 정책 때문에 발생함.

[기본 정책]
- 공식 스토어 외 APP 설치 시, 사용자 경고 및 차단 조치가 발생할 수 있습니다.
- 공식 스토어 통하지 않은 앱은 사용자 전화 통화 중에는 일부 권한이 제한될 수 있습니다.
- 공식 스토어 (e.g., Galaxy 스토어, 구글Play) 를 통한 앱 설치는 제한되지 않습니다.
* 스토어를 통한 앱 배포 및 사용을 권장 드립니다.

 

 

 

APK로 설치하려는 앱을 출처 미상 앱에서 예외 조치해달라고 삼성에 메일을 보내야 한다.

 

- 메일 주소 : unknown.sec@samsung.com

- 기업 앱이라면 기업 메일로 보내야 함
    -> test@naver.com 이런 거 안 됨.. 단, 요청 메일을 기업 계정으로 보냈다면 APK 파일은 naver 계정으로 보내도 괜찮았음)

- APK 파일 (다운로드 링크가 아닌 파일)

- 담당자 연락처, 담당자 명함, 기업 정보(홈페이지 주소 등)

- 스토어에 등록된 앱은 구글 스토어에 올라간 앱 페이지 링크

 

 

참고 링크 :

https://r1.community.samsung.com/t5/서비스-기타/기업에서-사용하는-apk-설치-문의/td-p/25248138

'개발' 카테고리의 다른 글

안드로이드와 iOS에서의 FCM 수신 문제  (0) 2024.08.29
abstract class와 interface에 대한 짧은 정리  (0) 2022.12.19
앞으로의 공부 계획  (0) 2022.12.06

문제상황

- FCM 메시지를 받을 때 안드로이드에서는 notification 없이 data만 와야 하고, iOS는 notification을 받아야 했음. 

 

방법 1.

백엔드에서 플랫폼 별로 메시지 구조를 다르게 해서 준다.

https://firebase.google.com/docs/cloud-messaging/concept-options?hl=ko#example-notification-message-with-platform-specific-delivery-options

 

FCM 메시지 정보  |  Firebase 클라우드 메시징

Google I/O 2023에서 Firebase의 주요 소식을 확인하세요. 자세히 알아보기 의견 보내기 FCM 메시지 정보 컬렉션을 사용해 정리하기 내 환경설정을 기준으로 콘텐츠를 저장하고 분류하세요. Firebase 클라

firebase.google.com

 

방법 2.

백엔드에서는 notification, data 둘 다 보내고 안드로이드에서 FCM 메시지에서 notification 관련 데이터를 삭제한다.

아래 코드를 테스트하진 않았음 (...)

override fun handleIntent(intent: Intent?) {
    
    val new = intent?.apply {
        val temp = extras?.apply {
            keyset()?.forEach { key ->
                if(key.startsWith(NOTIFICATION_PREFIX) || key.startsWith(NOTIFICATION_PREFIX_OLD)) {
                    //notification 관련 데이터는 삭제
                    remove(key)
                }
            }
        }
        replaceParam(temp)
    }
    
    super.handleIntent(new)

}

 

참고 링크 : https://medium.com/@jms8732/background에서-onmessagereceived가-호출-안되는-현상에-관하여-7595df624d91

'개발' 카테고리의 다른 글

삼성 출처미상 앱 제보  (0) 2024.08.29
abstract class와 interface에 대한 짧은 정리  (0) 2022.12.19
앞으로의 공부 계획  (0) 2022.12.06
구현하려고 했던 것

ViewPager2(이하 뷰페이저)를 이용한 Carousel 뷰를 구현하려고 하였음.

뷰페이저의 어댑터는 RecyclerView.Adapter를 상속받은 클래스를 사용하였음.

양 옆의 뷰가 좌우에서 조금씩 나오게 하기 위해 setPageTransformer로 각 뷰의 translationX를 조정하였음.

 

 

문제 상황

 

뷰페이저의 아이템을 클릭하면 새로운 액티비티를 띄우는데, 해당 액티비티를 종료 후 현재 액티비티로 돌아오면 뷰페이저의 아이템 뷰의 좌우 간격이 이상해지는 현상이 발생함.

 

 

원인

 

리사이클러뷰 어댑터를 사용하며 뷰를 재사용하는 과정에서, 기존 뷰에 설정한 translationX 값이 그대로 남아있었음.

예를 들어 나는 3번째 뷰를 표시한다고 생각했지만 실제로는 뷰를 재사용하면서 0번째 뷰의 translationX 값이 적용된 것.

 

setPageTransformer로 설정했던 것은 onResume 시에는 호출되지 않는다.

 

어떻게 보면 리사이클러뷰 아이템 뷰에서 체크박스 사용할 때 체크가 제대로 안 되는 문제와 동일한 원인이기도 함.

 

 

해결 방법

 

1. 어댑터에 아이템 뷰들의 translationX 값을 저장할 변수(이하 txList) 추가

- txList는 List가 갱신되거나 하면 이에 맞게 초기화되어야 함

 

2. 어댑터의 onBindViewHolder에서, 해당 itemView의 translationX 값을 txList에서 가져와서 적용

holder.itemView.translationX = txList[position]

 

2. 어댑터에 선택된 페이지를 기준으로, 나머지 translationX 값을 계산해서 txList 변경하는 메소드 추가

fun resetViewTranslationXList(center: Int) {
    txList = txList.mapIndexed { index, tx ->
        when(index-center) {
            -1 -> -pageTranslationX //현재 보이는 뷰 바로 앞 포지션
            1 -> pageTranslationX   //현재 보이는 뷰 바로 뒤 포지션
            else -> 0f  //현재 보이는 뷰 또는 화면에 보이지 않는 뷰
        }
    }.toMutableList()
}

 

 

3. 액티비티의 onPause() 메소드 내에 2에서 추가한 메소드를 호출하도록 함.

override fun onPause() {
    super.onPause()
    pagerAdapter.resetViewTranslationXList(requireBinding().pager.currentItem)
}

 

 

---- 또는 아래와 같이도 가능할 듯 ----

 

1. 어댑터에 currentItem 변수 추가 (뷰페이저가 선택한 아이템의 인덱스를 저장)

class BSSMapAdapter: RecyclerView.Adapter<BSSMapHolder>() {
    ...
    var currentItem = 0	//현재 뷰페이저에 표시되는 뷰
    ...
}

 

2. 어댑터의 onBindViewHolder에 현재 position에 따라 translationX 설정하는 코드 추가

when(holder.layoutPosition-currentItem) {
    -1 -> -pageMargin
    1 -> pageMargin
    else -> 0f
}.let {
    holder.itemView.translationX = it
}

 

3. onPause() 시 뷰페이저의 currentItem 값을 어댑터에 저장

pagerAdapter.currentItem = requireBinding().pager.currentItem

 

 

수정 후 결과

 

사용자가 페이지를 넘길 때는 setPageTransformer로 설정한 대로 각 아이템뷰의 translationX가 설정됨.

액티비티가 pause 상태가 될 때 각 뷰에 설정되어야 할 translationX 수치가 어댑터 내부 변수에 저장됨.

 

이후 다른 액티비티를 다녀오면 리사이클러의 onBindViewHolder가 실행되며,

저장했던 translationX 수치가 각 아이템 뷰에 적용됨.

 

* 일단 다른 액티비티를 다녀올 때만 문제가 발생해서 onPause()에 resetViewTransitionXList()를 호출하도록 하였으나,

뷰페이저의 페이지 변경 콜백에서 페이지가 선택된 이후에 호출하게 할 수도 있을 것임. 필요한 경우에 맞게 선택하면 될 듯.

라이브러리를 만들 때 AAR로 만드는 방법이 있고 JAR로 만드는 방법이 있는데,
공식 홈페이지에서는 AAR만 설명되어있고

인터넷에는 전부 옛날 버전으로만 적혀있어서 삽질을 많이 했다.

 

AAR - 리소스 포함 가능 (UI 라이브러리에 사용함)

JAR - 리소스 포함 불가 (유틸 라이브러리에 사용함)

 

1. 프로젝트 우클릭->New->Module (혹은 File-New-NewModule)

 

2. 왼쪽의 Templates 에서 'Java or Kotlin Library' 선택하고 우측에 정보 입력

(Phone&Tablet은 구동 가능한 Application module, Android Library는 AAR 라이브러리)

 

3. 프로젝트를 빌드하면 해당 모듈의 build/libs 안에 jar 파일이 생성된다.

 

task.delete(type: Delete) 
이런 코드 없어도 됨

스터디를 하면서 백엔드를 찍먹하기로 했다.

이후에 참고용으로 순서만 적어두고, 삽질 기록을 적어본다.

 

1. EC2 인스턴스 생성 (우분투 AMI)

2. 탄력적 IP 연결, 보안그룹 설정

    -> 인바운드 규칙에서 SSH 접근 가능 IP를 내 IP로 제한하니까 EC2 Instance Connect로 연결이 안 되었음. PuTTY로 연결하니 정상적으로 연결 됨

3. 도메인 연결

    -> 가비아에서 산 도메인을 route53에 연결할 때... 호스팅 영역을 만들고 NS레코드에 있는 값들을 복사해서 가비아 홈페이지의 도메인 관리에 네임 서버로 옮겨야 한다. (라우트53에서 만들어진 NS 레코드의 값들이 곧 가비아 해당 도메인의 새로운 네임 서버인 것) 이렇게 하니까 10분만에 됨 (...)

4. PuTTY와 연결

5. 타임존 변경, 호스트네임 변경

6. 도커 설치

7. 도커에 아파치2, MySQL, vsftpd 이미지 생성

  - 도커 파일 작성해서 아파치2 이미지를 생성하던 와중... 명령어를 실행하고 싶어서 CMD 구문을 추가했는데 계속 이미지 생성할 때 알 수 없는 명령어라고 나옴.. 알고보니까 'CMD apachectl -D -FOREGROUND' 그대로 입력해야 했음. (대괄호 ㄴㄴ)

  - 도커 이미지 삭제가 안 될 때, <none>으로 뜰 때는 해당 이미지로 만든 컨테이너가 존재하는지 확인한다. 해당 컨테이너가 실행중이라면 컨테이너를 중지하고(docker stop [컨테이너명]) 컨테이너를 삭제한 뒤(docker rm [컨테이너명]) 이미지를 삭제하면(docker rmi [이미지명 또는 이미지 ID]) 된다. 

  - 도커 파일을 각 이미지별로 하나씩 만들어야 했다.

8. 7에서 받은 이미지를 이용해서 컨테이너로 만들기

  - volumes로 로컬 폴더를 컨테이너의 폴더로 마운트할 때... 파일 경로를 잘못 입력해서 마운트가 안 됐음. (보내는 곳에서 절대 경로로 써 주고, 받는 곳도 /var/www/html/ 로 설정함.. 파일이 마운트가 안 된다면 내 로컬 폴더에 있는 경로인지 꼭 확인할 것!

  - 도커 컴포즈 파일 작성해서 아파치 2 컨테이너를 생성하려고 docker-compose up을 했는데 attaching to... 단계에서 너무 오랜 시간이 걸리는 것임... 근데 그냥 'docker-compose up -d' 를 해 주면 되는 거였다.

  - ports contains an invalid type, it should be a number, or an object 라는 에러를 뱉을 때는

      > 포트 각 항목을 따옴표로 묶는다 (0.0.0.0:20:20이 아닌 '0.0.0.0:20:20')

      > ip 빼고 포트 번호만 입력한다. ('0.0.0.0:20:20'이 아닌 '20:20'으로 입력한다.)

  - vsftpd는 컴포즈 파일로 만들다가 무슨 문제가 나서 그냥 명령어로 컨테이너를 만들었음..

docker run -d -v /var/ftp:/home/vsftpd \
-p 20:20 -p 21:21 -p 50000-50010:50000-50010 \
-e PASV_ADDRESS={아이피주소} -e PASV_MIN_PORT=50000 -e PASV_MAX_PORT=50010 \
-e FILE_OPEN_MODE=0644 -e LOCAL_UMASK=022 \
--name vsftpd2 --restart=always {도커 이미지 이름}

9. 방화벽 설정하기 (ufw allow ...)

10. 각 서비스 별 기본 세팅 하기

  - mysql : 문자셋을 UTF-8로 설정

    > vim이 설치가 안 되어 있는데 bash 안에서 설치하려고 하면 멈춰서 (...) 도커 파일에서 이미지 생성 시 만드도록 함. (RUN microdnf install -y vim)

  - vsftpd :

    > 익명으로도 접속이 안 될 때 : 도커 이미지 만들 때 EXPOSE로 포트 열었는지 확인

    > 익명으로는 접속이 되는데 유저로는 안 될 때 :

      - 해당 유저가 있는지 확인

      - /etc/pam.d/vsftpd 에서 auth required pam_shells.so를 주석(#)처리

      - /etc/passwd 에서 해당 사용자 맨 끝에 /bin/bash를 /sbin/nologin으로 수정

      - /etc/vsftpd/vsftpd.conf 에서 pam_service_name=vsftpd로 되어있는지 확인

11. 각 서비스 실제로 사용 할 프로그램과 연결

  - mysql -> heidiSQL => docker-compose 파일에 포트를 지정 안 해 줘서 연결이 안 되고 자꾸 에러 코드 10061만 내뱉음.. 포트 지정해주니까 정상 연결 됨

 

 

 

 

 

기타 삽질 내용

1. vi 로 새 파일 만들었는데 저장을 못한대 <- sudo vi 써야 함 (파일 편집 단계에서 해결할 수 있는 걸로 암.. 검색 ㄱㄱ)

2. 권한이 없대 <- sudo 붙이면 됨

+ Recent posts