초창기에는 GCM으로 푸시 서버를 구현했다면, 


최근 구글은 Firebase 회사를 인수하면서 FCM(Firebase Cloud Message) 기술을 선보이게 됬습니다.


오늘 포스팅 할 내용은 안드로이드(클라이언트)의 FCM 설정에 관련된 내용입니다.



파이어베이스 프로젝트 생성하기



https://console.firebase.google.com/?hl=ko


위 URL에서 프로젝트를 추가합니다.


추가하는 과정에서 알려주는 Server API Key help, Sender ID: help 데이터는 따로 적어두셔야 합니다.



Google-service.json 파일 다운받기



추가한 Firebase 프로젝트에서 설정탭을 누릅니다.



오른쪽에 google-service.json을 다운 받습니다.



안드로이드 스튜디오에서 프로젝트 생성하기



안드로이드 프로젝트를 생성합니다. 안드로이드 프로젝트 패키지 이름은 Firebase에서 만든  패키지 이름과


동일하게 합니다.



다운 받은 google-service.json 파일을 Project 폴더- app 폴더 안에 넣습니다.




안드로이드 스튜디오 Gradle 설정하기



안드로이드 스튜디오에는 Gradle 파일이 두개 있습니다. 


app 폴더에 한개, Project 폴더에 한개 있습니다.


먼저 Project폴더에 있는 build.gradle 파일로 접근합니다.


아래와 같이 classpath 'com.google.gms:google-services:3.0.0' 를 작성합니다.


추가하시고, sync 해주세요.


다음으로, app 폴더에 있는 build.gradle 파일로 접근합니다.


맨 윗줄에, apply plugin: 'com.android.applicaiton' 이 적혀있습니다.


아래에 스샷 처럼 바로 아랫줄에 apply plugin: 'com.google.gms.google-services'를 작성해주세요.




다음으로 dependencies 지역에 아래 스샷 처럼 


compile 'com.google.firebase:firebase-messaging:9.6.1' 를 추가합니다.




다 추가하셨으면 Sync 해주세요.


여기까지 오류 없이 성공해야 합니다.

 

오류 없이 성공하셨다면 셋팅은 완료했습니다.


다음 게시글은 Firebase 구동을 위한 코드 작성을 포스팅 하겠습니다.


읽어주셔서 감사합니다.


포스팅이 도움 되셨다면, 커피 한잔 후원해주세요!
더 좋은 포스팅 작성에 큰 힘이 됩니다.

Buy me a coffeeBuy me a coffee
  1. looksh 2018.08.05 13:33

    ScalableLayout 사용법 포스팅해주실 수 잇을까요

    • Mommoo 2018.08.05 16:21 신고

      안드로이드에서 ScalableLayout이 자주 쓰이나요?? 저도 안써본지라... 포스팅 하려면 공부를 좀 해야할거 같습니다. 자주 쓰이는 기술이면, 공부해서 포스팅 할 생각있습니다. 답변주세요 ^^

  2. leejunan 2020.11.26 02:23

    재미있는 내용 매우 잘 보고 가요

오늘 포스팅할 내용은,


안드로이드 디바이스가 현재


인터넷을 사용할 수 있는 상태인지


또한 사용할 수 있는 상태라면 


어떤 인터넷 환경인지 (Wifi or 3g ,4g ) 알아보는 방법에 대해 기술한다.



인터넷을 사용할 수 있는 상태인지를 체크하기 앞서,


인터넷이 연결되 있는 경우


어떤 인터넷 환경인지 가져오는 것부터 할 것이다.



먼저 AndoridManifest.xml 에 


해당 퍼미션을 등록해준다.


<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
<uses-permission android:name="android.permission.INTERNET"/>

인터넷 환경을 탐색하기 위한 멤버변수를 선언 해준다.


public static final String WIFI_STATE = "WIFE";
public static final String MOBILE_STATE = "MOBILE";
public static final String NONE_STATE = "NONE";

그 후 아래의 메서드를 작성한다.


public static String getWhatKindOfNetwork(Context context){
ConnectivityManager cm = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
NetworkInfo activeNetwork = cm.getActiveNetworkInfo();
if (activeNetwork != null) {
if (activeNetwork.getType() == ConnectivityManager.TYPE_WIFI) {
return WIFI_STATE;
} else if (activeNetwork.getType() == ConnectivityManager.TYPE_MOBILE) {
return MOBILE_STATE;
}
}
return NONE_STATE;
}


핵심코드는 getActiveNetworkInfo( ) 메서드이다.


해당 메서드는 인터넷이 연결되있는 경우 인터넷 환경에 대한 여러가지 정보를 담은 객체를 리턴한다.


만약 인터넷 환경이 연결되있는 상태가 아니라면 null을 리턴한다.


위의 메서드를 사용할 시, 인터넷 환경이 wifi 상태인지 3g,4g 상태인지 알 수 있다.



이 메서드를 통해서 인터넷이 연결됬는지 확인 할 수도 있지만,


이 메서드만 사용한다면 캐치 못하는 경우의 수가 있다. 



Wifi 연결은 되었지만 로그인, 인증서 등 권한을 획득하지 못하여 인터넷을 쓰지 못하는 경우.



위의 경우의 수에서는 사용자가 인터넷을 사용하진 못하지만 디바이스에서는 wifi가 연결된 걸로 인지한다.


이러한 경우의 수까지 캐치 하고 싶다면 아래의 코딩을 추가 해야한다.



1. Ping 보내기.


 ping이란, 원하는 네트워크 주소에 신호를 보내, 응답이 도착하는가 확인하는 것이다.

 

 만약 인터넷을 사용하지 못한다면, ping 신호는 도달 하지 못할 것이다.


 안드로이드도 리눅스 기반 이므로, 리눅스 명령어를 통해 ping 을 보낼 수 있는데,


 java 에서는 제공해주는 리눅스 api가 없기 때문에,


 밑단까지 내려가 명령을 줘야한다.  아래의 코드를 보자.



 private static class PingTask extends Thread {

private boolean success;
private String host;

public PingTask(String host) {
this.host = host;
}

@Override
public void run() {
super.run();
try {
Runtime runtime = Runtime.getRuntime();
Process proc = runtime.exec("ping -c 1 -W 100" + host);
proc.waitFor();
int exitCode = proc.exitValue();
if (exitCode == 0) {
success = true;
} else {
success = false;
}
} catch (IOException e) {
e.printStackTrace();
success = false;
} catch (InterruptedException e) {
e.printStackTrace();
success = false;
}
}

public boolean isSuccess() {
return success;
}
}

윗 코드는 리눅스 터미널까지 내려가 리눅스 명령어를 내리는 코드이다.


해당 코드를 쓰면 인터넷의 사용 가능 상태를 체크 할 수 있다.


하지만, 문제점은 생각보다 느렸다.


백그라운드 스레드로 사용한다면 상관없겠지만,


만약 인터넷 사용 가능 상태를 미리 조사를 해야 하는 상황이라면, 


사용자가 느끼기엔 느릴 것이다.


느린 원인은 잘 모르겠다. 추측성으론, 


안드로이드 명령어가 아닌 시스템 명령어를 내리는 것이므로 오버헤드가 발생한것이 아닌가 생각한다.


대안책으로 아래의 코드를 보자.



2. HttpURLConnection 객체 이용하기.


 HttpURLConnection객체를 응용해서 인터넷 사용 가능 상태를 검사할 것이다.


 문제점은 HttpURLConnection이 연결되는지 안되는지 기준은 HTTP_OK 상태 이므로,


 만약 인증받지 않은 와이파이 일지라도 HttpURLConnection은 연결상태로 뜰 것이다.


 따라서 아래의 방법으로 해결한다.



HTTP_OK 상태로만 연결 상태를 체크하지 말고, HTTP_OK 상태 체크 후,


웹페이지의 원하는 내용을 긁어올 수 는지 여부로 체크하기.



 무슨 말 이냐면, 인터넷 사용 가능한 권한을 받지 못하는 경우도 connect 는 되지만 원하는 웹페에지의 내용은 안긁어와진다.


 하지만 인터넷 사용 가능한 환경이 주어진다면, connect는 당연히 되고, 원하는 웹페이지의 내용도 긁어올수 있을것이다.


아래의 URL은 위의 논리를 구현하기 위해 구글이 제공하는 URL이다.


http://clients3.google.com/generate_204


해당 URL은 아무내용도 없는 즉, 인터넷이 연결된 상태에서 긁어올 내용을 넣지 않았다.


해당 URL은 HTTP_OK 를 넘어서, HTTP_NO_CONTENT ( 204번 ) 을 리턴한다.


이 URL을 사용 할 시, wifi 가 연결되었지만 권한이 없는경우는 200번을 리턴하고,


권한이 있는경우는 204번을 리턴한다.



아래의 코드를 보자.



멤버변수로 아래의 코드를 추가한다.


public static final String CONNECTION_CONFIRM_CLIENT_URL = "http://clients3.google.com/generate_204";

HttpURLConnection 객체를 호출하는 코드도 추가한다.


private static class CheckConnect extends Thread{
private boolean success;
private String host;

public CheckConnect(String host){
this.host = host;
}

@Override
public void run() {

HttpURLConnection conn = null;
try {
conn = (HttpURLConnection)new URL(host).openConnection();
conn.setRequestProperty("User-Agent","Android");
conn.setConnectTimeout(1000);
conn.connect();
int responseCode = conn.getResponseCode();
if(responseCode == 204) success = true;
else success = false;
}
catch (Exception e) {
e.printStackTrace();
success = false;
}
if(conn != null){
conn.disconnect();
}
}

public boolean isSuccess(){
return success;
}

}

이제 인터넷 상태를 체크해주는 메서드도 만들어본다.


public static boolean isOnline() {
CheckConnect cc = new CheckConnect(CONNECTION_CONFIRM_CLIENT_URL);
cc.start();
try{
cc.join();
return cc.isSuccess();
}catch (Exception e){
e.printStackTrace();
}
return false;
}

필자는 인터넷 환경체크를 우선시 하므로 


스레드에 join을 걸어 mainThread의 작업이랑 동기화 하였다.


만약 서브스레드로만 돌려도 되는 상황이라면 join을 지우면 되겠다.




이제 isOnline( ) 메서드와 getWhatKindOfNetwork( ) 메서드만 사용한다면,


인터넷 상태를 완벽하게 체크 할 수 있다.





 


포스팅이 도움 되셨다면, 커피 한잔 후원해주세요!
더 좋은 포스팅 작성에 큰 힘이 됩니다.

Buy me a coffeeBuy me a coffee
  1. 손님 2016.10.26 13:18

    글 잘 봤습니다~!
    if (activeNetwork != null) {
    if (activeNetwork.getType() == ConnectivityManager.TYPE_WIFI) {
    return WIFE_STATE;
    } else if (activeNetwork.getType() == ConnectivityManager.TYPE_MOBILE) {
    return MOBILE_STATE;
    }
    }
    return NONE_STATE;

    혹시 이 부분에 마지막 return NONE_STATE;를 else로 감싸야하지 않나요?

    • Mommoo 2016.10.26 13:23 신고

      읽어주셔서 감사합니다~
      해당코드는 else로 감싸셔도 되고 안감싸도 되는 코드입니다.
      하지만, 제 생각엔 추후에 다른 인터넷 연결방법이 나온다고 생각한다면 굳이 안감싸도 될꺼 같습니다.

    • Haewon Lee 2018.07.30 13:15 신고

      if와 else if안에 리턴이 있기때문에,
      리턴되지 않으면 NONE_STATE까지 오는겁니다 : ) 따라서 else를 적어줘도 안적어줘도 되겠네요~

      일반적으로 적는게 가독성에 좋긴하지만요

  2. sulkun 2017.06.26 11:04 신고

    저기 extends Thread 로 선안하고 사용하면 쓰레드가 작업 끝날때까지 MainThread가 정지 상태로 되는건가요??

    어떻게 되는지 궁금하네요 ㅜㅜ

    • Mommoo 2017.06.26 11:16 신고

      MainThread랑은 별개로 작동하므로 영향을 주지 않습니다. 다만 join연산 같은 MainThread동기화 연산을 쓰신다면 추가스레드의 작업이 끝날때까지 join메서드 아래에 작성한 코드들이 실행되지 않습니다.

  3. dkfree 2017.10.30 11:46

    WIFE 를 WIFI 로 바꾸면 더 좋을 것 같습니다. ㅎㅎㅎ
    좋은 코드 감사합니다.

    • Mommoo 2017.10.30 11:55 신고

      앗... 그런 오타가;;ㅎㅎ
      읽어주셔서 감사합니다^^.

    • commete 2020.02.11 10:13

      안됩니다.
      아무리 코드에 문제가 있어도 아내 (WIFE) 분을 바꾸시면 안됩니다. ^^

  4. huan 2018.01.29 10:59

    너무 멋진 게시글입니다. :) 정말 많은 도움이 되었습니다. 감사합니다. :)

  5. lolmc 2018.04.03 23:21

    유용한 정보 게시 해주셔서 정말 감사합니다.

오늘 포스팅은 안드로이드 퍼미션에 관한 내용이다.


안드로이드 마시멜로우 OS가 등장함에 따라,


기존 환경에서 바뀐점들이 있다. 

(https://developer.android.com/about/versions/marshmallow/android-6.0-changes.html)


그 중, 제일 골치썩는 문제는 Runtime Permission이 아닐까 싶다.


아래의 글은 위 URL 에서 발췌한 내용중 하나이다.

Runtime Permissions


This release introduces a new permissions model, where users can now directly manage app permissions at runtime. This model gives users improved visibility and control over permissions, while streamlining the installation and auto-update processes for app developers. Users can grant or revoke permissions individually for installed apps.

On your apps that target Android 6.0 (API level 23) or higher, make sure to check for and request permissions at runtime. To determine if your app has been granted a permission, call the newcheckSelfPermission() method. To request a permission, call the new requestPermissions() method. Even if your app is not targeting Android 6.0 (API level 23), you should test your app under the new permissions model.

For details on supporting the new permissions model in your app, see Working with System Permissions. For tips on how to assess the impact on your app, see Permissions Best Practices.



간단하게 해석해보면, 


사용자가 접근 권한이 필요한 기능( 예를 들자면, 전화 바로 걸기)을 수행할때,


사용자로 하여금 해당 권한을 앱에 허락 할 것인지 묻고, 개발자가 아닌 사용자가 


자신의 디바이스의 접근 권한을 결정하는 방식이다.



이전 버전에서(API 22 이하 버전)의 접근 권한 수용방식은 앱을 설치할때,


사용자로 하여금 해당 권한을 사용한다는 것만 보여준다. 


즉, 앱을 설치하면 해당 권한등을 동의하는 것으로 간주된다.



API 22 아래 버전.


위 경우에서 개발자는 단순히 Manifest.xml에 권한 추가후 코딩하면 되겠지만,


아래와 같이 마시멜로우 버전에서의 런타임 퍼미션 체크방식 일 시, 코딩하기 녹록지 않다.


API 23 이상 버전.




하지만 모든 권한을 사용자로 하여금 확인받아야 하는 것은 아니다.


개인적인 생각이지만, 개인정보와 관련있는 권한은 확인 받지만


관련이 없는 권한은 확인을 안받아도 되는것 같다.


아래는 확인 받아야 할 권한 목록이다.



출처 : https://developer.android.com/guide/topics/security/permissions.html?hl=ko#normal-dangerous

Permission GroupPermissions
CALENDAR
CAMERA
CONTACTS
LOCATION
MICROPHONE
PHONE
SENSORS
SMS
STORAGE


지금부터 메시멜로우 os의 런타임 퍼미션을 위해 필요한 코딩등을 소개한다.

총 4가지의 메서드를 사용 할 것이다.

  • ContextCompat.checkSelfPermission()
  • ActivityCompat.shouldShowRequestPermissionRationale()
  • ActivityCompat.requestPermissions()
  • onRequestPermissionsResult()



첫번째 부터 세번째 까지의 메서드는 매개변수로 Context만 넣어줄 수 있다면, 어디서든 사용가능하다.


네번째 메서드는 Activity 안에서의 오버라이드 메서드 이다.


이름에서 볼 수 있다시피, 퍼미션 여부를 수행한 후 결과가 들어오는 곳이다.


아래의 예제를 보자.


1
2
3
4
5
6
7
8
/**
  해당 권한이 승낙 상태인지 거절 상태인지 확인한다.
*/
if (ContextCompat.checkSelfPermission(this, Manifest.permission.READ_CALENDAR) == PackageManager.PERMISSION_GRANTED){
   //Manifest.permission.READ_CALENDAR이 접근 승낙 상태 일때
} else{
   //Manifest.permission.READ_CALENDAR이 접근 거절 상태 일때
}
cs


최초 앱 실행시, 당연히 접근 권한은 없으므로 else 블록을 타게 된다.


위 이미지에서 보는 것 처럼 권한 설정 요구 다이얼로그를 뜨게 하려면


아래 예시처럼 requestPermission() 메서드를 추가해야 한다.


1
2
3
4
5
6
7
8
if (ContextCompat.checkSelfPermission(this, Manifest.permission.READ_CALENDAR) == PackageManager.PERMISSION_GRANTED){
   //Manifest.permission.READ_CALENDAR이 접근 승낙 상태 일때
} else{
   //Manifest.permission.READ_CALENDAR이 접근 거절 상태 일때
  
   //사용자에게 접근권한 설정을 요구하는 다이얼로그를 띄운다.
   ActivityCompat.requestPermissions(this,new String[]{Manifest.permission.READ_CALENDAR},0);
}
cs


requestPermission 메서드는 매개변수로 Context, String 배열, int형 변수 를 원한다.


int형 변수는 후에 onRequestPermissionsResult() 메서드에 결과물이 전돨될 시,


결과물들을 구분 짓는 index 번호이다.


onActivityResult 메서드랑 똑같이 보면 될 것이다.




만약 사용자가 다이얼로그를 보고 승낙을 누른다면 좋겠지만,


만약 거절을 누른다면,


더 나아가 다시 보지 않기 체크 박스에 체크후 거절 버튼을 누른다면


상황은 복잡해진다.


그러한 상황을 캐치 하기 위해 ActivityCompat.shouldShowRequestPermissionRationale() 메서드를 써야한다.


해당 메서드를 쓰기 앞서 


사용자가 다이얼로그를 띄웠을 시, 할 수 있는 경우의 수를 계산 해보자.


1. 승낙하는 경우.


2. 거절하는 경우.


3. 다시 보기 않기 체크 후 거절 하는 경우.


이렇게 3가지 이다.


2번의 경우의 수인 그냥 거절 하는 경우, 후에 다시 앱을 킬때 


ActivityCompat.shouldShowRequestPermissionRationale() 메서드를 사용하면 true 가 리턴이 된다.


즉, 사용자가 거절한 이력이 있나 없나를 알 수 있는 메서드이다.  


이 메서드는 조심해서 써야한다. 


사용자가 최초 앱 접속시 뜨는 다이얼로그는 거절 이력이 없기때문에


ActivityCompat.shouldShowRequestPermissionRationale() 메서드를 사용시 false 가 리턴된다.


또한, 사용자가 3번 경우의 수를 선택 했을 경우도 마찬가지로  false가 리턴된다.


사용자가 3번 경우의 수를 선택 했을 경우는 후에 앱이 다시 켜질때,


requestPermission() 메서드를 쓰더라도 구글이 만든 권한 설정 요구 다이얼로그가 뜨질 않는다.


그 후, 곧바로 OnRequestPermissionResult 메서드가 실행된다.


따라서, 개발자 자체적으로 다이얼로그를 띄어 사용자가 직접 설정에 들어가 권한을 설정하게끔 유도를 해야한다.



아래는 ActivityCompat.shouldShowRequestPermissionRationale() 메서드를 추가한 예시이다.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
if (ContextCompat.checkSelfPermission(this, Manifest.permission.READ_CALENDAR) == PackageManager.PERMISSION_GRANTED){
   //Manifest.permission.READ_CALENDAR이 접근 승낙 상태 일때
else{
   //Manifest.permission.READ_CALENDAR이 접근 거절 상태 일때
   if (ActivityCompat.shouldShowRequestPermissionRationale(this,Manifest.permission.READ_CALENDAR)){
      //사용자가 다시 보지 않기에 체크를 하지 않고, 권한 설정을 거절한 이력이 있는 경우
   } else{
      //사용자가 다시 보지 않기에 체크하고, 권한 설정을 거절한 이력이 있는 경우
   }
 
   //사용자에게 접근권한 설정을 요구하는 다이얼로그를 띄운다.
   //만약 사용자가 다시 보지 않기에 체크를 했을 경우엔 권한 설정 다이얼로그가 뜨지 않고,
   //곧바로 OnRequestPermissionResult가 실행된다.
   ActivityCompat.requestPermissions(this,new String[]{Manifest.permission.READ_CALENDAR},0);
   
}
cs


최종 결과값은 OnRequestPermissionResult() 메서드로 받아야 한다.


아래의 코드는 예시이다.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResult){
  super.onRequestPermissionsResult(requestCode, permissions, grantResults);
  //위 예시에서 requestPermission 메서드를 썼을시 , 마지막 매개변수에 0을 넣어 줬으므로, 매칭
  if(requestCode == 0){
     // requestPermission의 두번째 매개변수는 배열이므로 아이템이 여러개 있을 수 있기 때문에 결과를 배열로 받는다.
     // 해당 예시는 요청 퍼미션이 한개 이므로 i=0 만 호출한다.
     if(grantResult[0== 0){
        //해당 권한이 승낙된 경우.
     }else{
        //해당 권한이 거절된 경우.
     }
  }
}
cs


onRequestPermissionResult 메서드는 사용자가 무얼 선택하던, 무조건 타는 메서드 이다.


따라서 이 메서드를 통해 사용자가 권한을 거절했을 경우 대처해야한다.


필자는 복잡한 권한체크를 해야한다는 부담감과 설령 구현하더라도 해당 코드들이 난잡하게 섞이는게 싫어서, 


자체적으로 라이브러리를 만들었다.


손쉽게 권한체크를 하고싶은 독자들은 아래의 Github 주소를 참고 하길 바란다.


 

Github에서 Star를 눌러준다면, 더 좋은 오픈소스를 만들 수 있다.



  https://github.com/Mommoo/MommooPermission




포스팅이 도움 되셨다면, 커피 한잔 후원해주세요!
더 좋은 포스팅 작성에 큰 힘이 됩니다.

Buy me a coffeeBuy me a coffee
  1. 익명 2016.11.30 12:17

    비밀댓글입니다

    • 익명 2016.11.30 15:38

      비밀댓글입니다

  2. 익명 2017.04.04 19:57

    비밀댓글입니다

    • 익명 2017.04.19 16:14

      비밀댓글입니다

  3. 익명 2017.07.05 11:06

    비밀댓글입니다

    • 익명 2017.07.05 12:16

      비밀댓글입니다

  4. 초급개발자 2017.07.24 14:21

    오픈소스 잘 사용하고 있습니다.
    궁금한점이 있는데 다이얼로그 스타일 및 애니메이션을 수정 할 수 있나요?

    • Mommoo 2017.07.24 15:52 신고

      사용해주셔서 감사합니다. 수정 할 수 있게 추가하겠습니다.

  5. 비전공개발자 2017.08.15 12:55

    좋아요도 누르고 깃허브가서 내려받아서 테스트도 해봤는데 잘 되네요 정말 좋은거 같아요
    그런데 제 프로젝트에다가 사용하려고 하니까 분리된? permission을 못 찾네요...ㅠ 제가 깃허브도 처음이고 안드로이드도
    오로지 구글링과 책만으로 개발하는 비전공자라서 어디 물어볼곳이 없네요.. 깃허브도 처음해보고
    app폴더에 있는 TestActivity는 C모양이 뜨는데 permission에 있는 자바파일들은 문서모양에 j로만 표시가 되있습니다.
    그리고 다른 build.grandle 파일들에 있는 compile은 다 복사해줬는데 유독 permission 에 있는 manifest가 빨간줄이 갑니다..

    무엇이 문제일까요?

    • 비전공개발자 2017.08.15 13:13

      모듈 다운받아서 사용했더니 되네요
      성급하게 댓글달아 죄송합니다

      유용하게 사용하겠습니다 감사합니다

    • Mommoo 2017.08.16 14:39 신고

      사용해주셔서 감사합니다!!

  6. 자바린이 2018.04.25 13:11

    좋은 공부가 됐습니다 감사합니다(_ _)

  7. kellyk 2020.11.24 00:33

    재미있는 내용 되게 잘 배우고 가요

오늘 포스팅할 내용은 안드로이드 statusbar에


색상을 넣는 방법이다.


statusbar는 안드로이드 화면 최상단에 존재하며,


앱의 푸쉬나, 배터리 상태, 시간등을 알려주는 역할을 한다.


롤리팝 버전부터 toolbar ( 또는 actionBar )의 색상과 통일감을 주기 위해


statusBar의 색상을 바꿀 수 있도록 API 개편했다.


따라서 머터리얼 디자인을 준수한다면, statusbar 색상을 고려하지 않을 수 없다.


하지만, 5.0 이상 API 에선 바꾸기 수훨하지만, 5.0 미만 API 에서는


statusbar 색상을 바꾸기가 쉽지 않다.


본 포스팅은 롤리팝 이후 API를 이용한 statusbar 색상 넣기와


롤리팝 이전 API를 이용한 statusbar 색상 넣기


두가지를 다룰 예정이다.



아래는 롤리팝 버전 이후 API를 이용한 statusbar 색상 넣기이다. 


롤리팝 보다 낮은 버전은 동작하지 않으니 주의하자.




왼쪽의 이미지를 보면,


각 view 영역에서의 색상태그를 알려준다.


statusbar 색상을 바꾸기 위해


res 디렉토리 안에 있는 


value 디렉토리에 접근후 color.xml에


왼쪽과 같이 태그를 작성한다.


<color name= "colorPrimaryDark">원하는색상</color>


왼쪽에서 보이는 view 영역중 바꾸고 싶은 색상이 있다면,


위와 같이 태그이름과 벨류를 정해주면 된다.
















위의 방법은 xml셋팅으로 statusbar를 바꾸는 방법이다.


아래의 방법은 코드적으로 바꿀 수 있는 방법이다.


액티비티 안이라면 아래와 같이 똑같이 작성하면 되지만, ( window = getWindow() )


액티비티가 아니라면 액티비티 참조값에서 메서드를 호출해야한다. ( window = activity.getWindow() )


Window window = getWindow();


window.clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUENT_STATUS);


window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUND);


window.setStatusBarColor(자신이 바꾸고 싶은 컬러);


롤리팝 API부터 statusbar는 TRANSLUENT를 적용하면 투명 상태가 아니라, 반투명 상태의 색상이 입혀진다.


따라서 TRANSLUENT 속성을 지우고, statusbar 영역의 view를 칠할 수 있게 셋팅을 한다.


셋팅이후 자신의 원하는 색상을 바꿀 수 있다.



다음 포스팅엔 롤리팝 하위 버전에서 상태바 색상을 바꾸는 방법을 포스팅 하겠다.

 





포스팅이 도움 되셨다면, 커피 한잔 후원해주세요!
더 좋은 포스팅 작성에 큰 힘이 됩니다.

Buy me a coffeeBuy me a coffee
  1. 유혼 2017.07.06 12:27

    왜 2번째는 포스팅 안해주시나여 ㅠㅠ

    • Mommoo 2017.07.06 17:52 신고

      조만간 하겠습니다. 관심 가져주셔서 감사합니다 :)

  2. 익명 2017.08.04 09:59

    비밀댓글입니다

  3. 안드 2017.08.23 11:12

    하위버전 바꾸는법 포스팅 부탁 드립니다.

  4. 김기효 2018.09.04 11:33

    안녕하세요. 혹시 STATUS BAR에 있는 항목들의 색상을 변경하려면 어떻게 해야 하는지 알려주실 수 있나요?

오늘은 안드로이드 최신 위젯에 속하는


플로팅액션버튼(FloatingActionButton)에 대해 포스팅 한다.


먼저 아래의 유알엘(안드로이드 디벨로퍼) 에서 발췌한 


플로팅액션버튼에 관한 내용이다.


https://developer.android.com/reference/android/support/design/widget/FloatingActionButton.html


Floating action buttons are used for a special type of promoted action. They are distinguished by a circled icon floating above the UI and have special motion behaviors related to morphing, launching, and the transferring anchor point.

Floating action buttons come in two sizes: the default and the mini. The size can be controlled with the fabSize attribute.

As this class descends from ImageView, you can control the icon which is displayed via setImageDrawable(Drawable).

The background color of this view defaults to the your theme's colorAccent. If you wish to change this at runtime then you can do so viasetBackgroundTintList(ColorStateList).



간단하게 해석해보자면, 


플로팅액션버튼은 다른 View 들과 구별되게끔 공중에 떠있는듯한(그림자 효과를 이용- 엘리베이션 효과) UI이며,


또한 이버튼은 특정 지점으로 부터 애니메이션적 특징을 가질 수 있다고 한다.


xml 속성인 fabSize를 이용하여 크기를 정할 수 있고, 크기를 정하지 않을시 default 사이즈로 셋팅이 된다.


이미지뷰(ImageView) 위젯을 상속 받았기 때문에 이미지뷰 와 똑같이 이미지를 셋팅할 수 있고,


버튼의 색상은 theme 색깔인 colorAccent 기준으로 정해진다. 만약 코드로 바꾸고 싶다면,


setBackgroundTintList( 컬러값 ) 으로 바꿀 수 있다.



해당 위젯을 쓰기위해선 그래들에 아래와 같이 적어 sync 해야한다.


compile 'com.android.support:design:22.2.0'



이제, 코드를 통해 사용해보자. 중요한 점은 플로팅액션버튼의 에니메이션 특징이라던가


다른 View와 적절하게 이용되는 것들은( ex) snackBar가 나타날때 플로팅액션버튼이 스낵바위로 올라가는 애니메이션 발동,


리사이클러뷰를 스크롤할때 플로팅액션버튼이 숨겨지는 애니메이션 발동등)


CoordinatorLayout이랑 같이 사용해야 한다는 점이다. CoordinatorLayout이 여러 위젯,View들을 통솔하고 관리하는 역할을 하기 때문이다.


CoordinatorLayout 과 같이 사용할시 anchor (특정지점) 을 정할 수 있는것도 특징이다. 아래의 예시를 보자.




acitvity_mainxml

<?xml version="1.0" encoding="utf-8"?>
<android.support.design.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="true">

<android.support.design.widget.AppBarLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">

<android.support.v7.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="?attr/colorPrimary"/>

</android.support.design.widget.AppBarLayout>

<FrameLayout
android:id="@+id/frameLayout"
android:background="#ffff0000"
android:layout_width="match_parent"
android:layout_height="300dp"/>

<android.support.design.widget.FloatingActionButton
android:id="@+id/fab"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="bottom|end"
android:layout_margin="@dimen/fab_margin"
android:src="@android:drawable/ic_dialog_email" />

</android.support.design.widget.CoordinatorLayout>

위와 같이 코딩 했을 경우 FrameLayout(빨간배경)과 상관없이 FloatingActionButton의 위치는 아래의 그림과 같이 배치된다.




위에서 말한 anchor 속성을 적용해 FrameLayout(빨간배경) 에 위치시켜보자.

<?xml version="1.0" encoding="utf-8"?>
<android.support.design.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="true">

<android.support.design.widget.AppBarLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">

<android.support.v7.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="?attr/colorPrimary"/>

</android.support.design.widget.AppBarLayout>

<FrameLayout
android:id="@+id/frameLayout"
android:background="#ffff0000"
android:layout_width="match_parent"
android:layout_height="300dp"/>

<android.support.design.widget.FloatingActionButton
android:id="@+id/fab"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="@dimen/fab_margin"
android:src="@android:drawable/ic_dialog_email"
app:layout_anchor="@id/frameLayout"
app:layout_anchorGravity="right|bottom"/>

</android.support.design.widget.CoordinatorLayout>

위와 같이 FloatingActionButton 안에 app:layout_anchor="~" , app:layout_anchorGravity="~" 를 추가했다.


해당 속성은 CoordinatorLayout이 가지고 있다. 특정지점을 frameLayout이란 id값을 통해 알려주고,


레이아웃 그래비티를 적절히 사용해 배치시켜 봤다. 아래의 그림이 결과물이다.



중요한점은 플로팅액션버튼, 즉 자기자신의 위젯의 그래비티와는 무관하다는 점이다. 


만약 위젯 레이아웃 즉, layout_gravity 속성을 적을시 그에 맞게 anchor 지점에서 이동한다.


anchor 지점을 정하는것은 CoordinatorLayout이 담당하는 것이므로, 당연히 다른 위젯들도 anchor를 정할 수 있다.


또한 anchor 을 지정해줌으로써, 특정 애니메이션의 행위를 보장 해줄 수 도 있다.


포스팅이 도움 되셨다면, 커피 한잔 후원해주세요!
더 좋은 포스팅 작성에 큰 힘이 됩니다.

Buy me a coffeeBuy me a coffee

오늘은 안드로이드 SharedPreference에 대해 포스팅 합니다.


안드로이드 에서는 데이터 관리를 어떻게 할까?


크게 4가지 방법이 있다.


첫째는 SQLITE,


둘째는 I/O,


셋째는 서버를 거친 DB,


넷째는 오늘 포스팅 할 SharedPreference가 있다.


SharedPreference는 XML 방식을 채택하기 때문에,


데이터 로딩이 느릴 수 있다. XML 방식이 왜 느린지


알고 싶다면 아래 URL을 참고하라.


http://mommoo.tistory.com/17


기본적으로 쉐어프리퍼런스는 Map 과 같은 형태의 자료구조라 생각하면 편하다.


아래의 예제를 보자.


Preference 생성 (this 는 액티비티 정보. 액티비티 안이 아닐때는 context. 으로 접근해야함)


SharedPreferences shared

1 . sharedPreferences = getSharedPreference("name",0);


2.  sharedPreferences = getSharedPreference(0);


3.  sharedPreferences = PreferenceManager.getDefaultSharedPreference(this);



1번 방법은 쉐어프리퍼런스 저장 폴더에 name.xml 이름의 파일로 저장이 된다. 다른 이름도 얼마든지 가능하다.


2번 방법은 현재 액티비티의 이름으로 xml 파일이 저장된다.


3번 방법은 환경설정에 저장된 값으로 쉐어프리퍼런스를 가져오는 방법이다.


가져올 Preference가 없다면 새로 생성해준다.



Preference 값 호출


SharedPreferences.Edit editor = sharedPreference.edit();


editor.getInt(key값,디폴트값);

editor.getLong(key값,디폴트값);

editor.getBoolean(key값,디폴트값);

editor.getString(key값,디폴트값);

editor.getFloat(key값,디폴트값);


key값은 무조건 String 이다. 또한,


get하는 데이터형에 맞게 디폴트값을 설정해야 한다.


예를들어 getInt는 getInt("key",0); 이런식으로 말이다.



Preference 값 저장


SharedPreferences.Edit editor = sharedPreference.edit();


editor.putInt(key값, 디폴트값);

editor.putLong(key값, 디폴트값);

editor.putBoolean(key값, 디폴트값);

editor.putString(key값, 디폴트값);

editor.putFloat(key값, 디폴트값);

editor.commit();


중요한 것은 commit() 메서드를 써야 xml에 저장이 된다는 점이다.


값 호출 처럼 key값은 String이고 디폴트값은 자료형에 맞게 써야한다.



Preference 값 삭제


SharedPreferences.Edit editor = sharedPreference.edit();


1. editor.remove("key");


2. editor.clear();


editor.commit(); 


값 저장과 같이 commit(); 메서드를 사용해야 적용된다.


1번 방법은 key값에 맞는 value가 지워지고


2번 방법은 xml 데이터 전체가 지워진다.





포스팅이 도움 되셨다면, 커피 한잔 후원해주세요!
더 좋은 포스팅 작성에 큰 힘이 됩니다.

Buy me a coffeeBuy me a coffee
  1. 카페모카 2019.12.18 09:36

    SharedPreferences.Edit editor = sharedPreference.edit();



    editor.getInt(key값,디폴트값);

    editor.getLong(key값,디폴트값);

    editor.getBoolean(key값,디폴트값);

    editor.getString(key값,디폴트값);

    editor.getFloat(key값,디폴트값);

    이게 맞나요? 잘못되신거 같은데...

오늘 포스팅은 많이 쓰이는 View 중 하나인


WebView에 대해서다.


인터넷을 볼 수 있는 브라우저를


안드로이드 view 형태로 본다고 생각하면 된다.


간단하게 쓰는법에 대해 알아볼 것 이다.


View 공간을 먼저 정해야 하므로 아래와 같이 Xml을 코딩한다.


notice_webView_layout.xml

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:paddingLeft="50dp"
android:paddingRight="50dp"
android:paddingTop="100dp"
android:paddingBottom="100dp"
android:layout_width="match_parent"
android:layout_height="match_parent">
<WebView
android:id="@+id/webView"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
</FrameLayout>

FrameLayout의 패딩값으로 webView의 크기를 정했다.


아래는 java 코딩이다.


MainActivity.java

WebView webView = findViewById(R.id.webView);
webView.setWebViewClient(new WebViewClient());
webView.loadUrl("http://www.naver.com");

간단한 코딩으로 네이버를 열어볼 수 있다.



포스팅이 도움 되셨다면, 커피 한잔 후원해주세요!
더 좋은 포스팅 작성에 큰 힘이 됩니다.

Buy me a coffeeBuy me a coffee

안드로이드 SQLite 2편이다.


1편은 아래의 링크로 가면 된다.


http://mommoo.tistory.com/20


1편에서는 SQLite를 쓰기위한 준비사항 과 개념에


관하여 다루었다.


이번 포스팅은 직접 사용 해볼 것이다.


내가 필요한 Column은 id와 pass 값이다.


테이블의 이름은 Info 로 할것이다.


해당 테이블을 만들기 위해


아래와 같이 코딩한다.


public class MommooDB extends SQLiteOpenHelper {
public MommooDB(Context context, String name, SQLiteDatabase.CursorFactory factory, int version) {
super(context, name, factory, version);
}
@Override
public void onCreate(SQLiteDatabase db) {
String sql = "CREATE TABLE Info"+"(id Text primary key, pass Text);";
db.execSQL(sql);
}
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
// TODO Auto-generated method stub
onCreate(db);
}
}


Info라는 db파일을 처음 접근할때 onCreate 메서드가 실행되고,


그 결과로 Info 테이블이 생성 될 것이다.


해당 쿼리문은 id 와 pass 를 문자열로 저장하고 id를 primary key로 지정하는 쿼리문이다.


사용하는 방법은 아래와 같다. 간단하게 insert 와 select를 해볼 것 이다.


ex ) insert



MommooDB mommooDB = new MommooDB(this,"Info",null,1);
SQLiteDatabase database = mommooDB.getWritableDatabase();
String id = "Mommoo";
String pass = "mm";
String sql = "insert into Info values('"+id+"','"+pass+"')";
database.execSQL(sql);



혹시, 버전을 바꾸고 싶으면 생성자안 4번째 인자만 바꿔주면 된다. ( 버전은 1이상만 가능)


ex) select


select를 할때는 일반적으로 아래와 같이 커서 클래스를 쓴다. 


MommooDB mommooDB = new MommooDB(this,"Info",null,1);
SQLiteDatabase database = mommooDB.getReadableDatabase();
String sql = "select * from Info where id='Mommoo'";
Cursor cursor = database.rawQuery(sql,null);
while(cursor.moveToNext()){
String id = cursor.getString(0);
String pass = cursor.getString(1);
}


cursor.moveToNext() 메서드를 통해, 테이블의 튜플 갯수만큼 루프를 돈다.


상황에 맞게 코딩하면 되겠다.





insert문은 mommooDB.getWritableDatabase(); 를 사용했고 select 문은 mommooDB.getReadableDatabase(); 를 사용했는데,


메서드명 에서도 알 수 있드시, 쓰기모드 읽기모드 이다. insert는 db값을 입력시켜야 하므로 쓰기모드를 사용했고,


select는 디비 튜플값만 불러오면 되므로 읽기모드를 사용한것이다.


위와같이 데이터베이스를 사요한 후 에는 꼭 아래와 같이 close를 해준다.



mommooDB.close();
database.close();
cursor.close();



포스팅이 도움 되셨다면, 커피 한잔 후원해주세요!
더 좋은 포스팅 작성에 큰 힘이 됩니다.

Buy me a coffeeBuy me a coffee

+ Recent posts