84일차(1)/Android App(47) : Content Provider
새 모듈 생성- Empty Activity
step20contentprovider
activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout 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"
tools:context=".MainActivity"
android:orientation="vertical">
<EditText
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/inputName"
android:hint="검색할 이름 입력..."/>
<Button
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="연락처 정보 얻어오기"
android:id="@+id/getBtn"/>
<EditText
android:layout_width="match_parent"
android:layout_height="match_parent"
android:focusable="false"
android:gravity="top|left"
android:id="@+id/console"/>
</LinearLayout>
- 이름을 입력하고 버튼을 누르면 휴대폰의 연락처 앱(contacts) 에서 연락처 불러오도록 하기
- 우리 앱에서 그 정보들을 가지고 올 수 있는지 권한 체크!
AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<!-- 연락처 정보를 접근하겠다는 퍼미션 -->
<uses-permission android:name="android.permission.READ_CONTACTS"/>
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:supportsRtl="true"
android:theme="@style/Theme.MyAndroid">
<activity
android:name=".MainActivity"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
- manifest에 권한 관련 내용 추가 (permission)
- 하지만 이것만 있다고 승인되는건 아니고, 퍼미션을 유도하는 코딩이 필요하다.(사용자에게 권한 옵션 보여주기)
- 하지만 설정에 들어가보면 권한이 없다고 나온다.(Off 상태)
- 앱이 처음 설치되었을 때는 이 값이 '허용되지 않음'으로 나온다.
- 그래서 이 권한을 확인하는 과정이 필요하다.
- 권한을 허용하게 만들어서 연락처 정보를 읽어와야 한다.
- 초반에는 manifest에 표기하기만 하면 권한이 승인됐는데, 사람들이 안읽고 승인하니까
안전을 위해 운영체제에서 한번 더 승인하는 과정을 거치도록 한 것!
- 그럼 연락처는 어떻게 알아올 수 있을까?
- 컨텐츠 제공자로부터 데이터를 어떻게 얻어내는지 익힐 예정
→ Content Provider 의 사용방법을 익히는 것
** Content Provider
- 기기의 앱이 가지고 있는 정보(연락처 등) 을 어떤 앱에서 요청하면 제공해주는 역할
- Content Resolver 를 사용해서 Contacts App이 가지고 있는 데이터를 어떻게 얻어내는지 익히기
- 우리가 직접 Content Provider 를 만들 일은 거의 없다.
- 이것을 사용하는 사용자 입장에서 앱을 만들면 된다. Content Resolver를 잘 사용하면 된다!
- Content Provider를 하나의 DB처럼 사용할 수 있다.
- Content resolver를 사용해서 select 로 데이터를 뽑아오듯이 사용할 수 있다.
- 테스트를 위해 주소록에 연락처를 추가해둔다.
- Contacts 주소록 어플에 들어가서,
- 이렇게 여러개 만들어서 저장해준다.
MainActivity
package com.example.step20contentprovider;
import android.Manifest;
import android.content.ContentResolver;
import android.content.pm.PackageManager;
import android.database.Cursor;
import android.net.Uri;
import android.os.Bundle;
import android.provider.ContactsContract;
import android.widget.Button;
import android.widget.EditText;
import android.widget.Toast;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat;
public class MainActivity extends AppCompatActivity {
//필요한 필드 정의하기
EditText inputName, console;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//EditText 객체의 참조값 얻어내서 필드에 저장하기
inputName=findViewById(R.id.inputName);
console=findViewById(R.id.console);
Button getBtn=findViewById(R.id.getBtn);
//연락처 정보 얻어오기 버튼을 눌렀을 때
getBtn.setOnClickListener(v -> {
//연락처 정보 얻어오기 권한이 체크되었는지 상수값 얻어오기
int permissionCheck= ContextCompat.checkSelfPermission(this, Manifest.permission.READ_CONTACTS);
if(permissionCheck != PackageManager.PERMISSION_GRANTED){
//권한이 필요한 목록을 배열에 담는다.
String[] permissions={Manifest.permission.READ_CONTACTS};
//배열을 전달해서 해당 권한을 부여하도록 요청한다.
ActivityCompat.requestPermissions(this,
permissions,
0); //요청의 아이디
return; //메소드는 여기서 종료
}
//연락처 정보 얻어오기
getContacts();
});
}
//퍼미션 요청의 결과가 전달되는 메소드 재정의하기
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
switch (requestCode){
case 0: //0번 요청인 경우
//권한을 부여했다면
if(grantResults[0] == PackageManager.PERMISSION_GRANTED){
//연락처 정보 얻어오기
getContacts();
}else{//권한을 부여 하지 않았다면
Toast.makeText(this, "연락처 접근 권한이 필요합니다.", Toast.LENGTH_SHORT).show();
}
break;
}
}
//ContentProvider 로부터 ContentResolver 객체를 이용해서 연락처 정보를 얻어내는 메소드
public void getContacts(){
//입력한 검색어
String keyword=inputName.getText().toString();
//ContentResolver 객체의 참조값을 얻어오기
ContentResolver resolver=getContentResolver();
//연락처 정보를 지칭할 수 있는 Uri 객체 얻어내기
Uri contactUri= ContactsContract.CommonDataKinds.Phone.CONTENT_URI;
//select할 칼럼
String[] columns={
ContactsContract.CommonDataKinds.Phone.CONTACT_ID,
ContactsContract.CommonDataKinds.Phone.NUMBER,
ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME,
};
//where display_name like '%키워드%'
String where=ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME+" LIKE ? ";
//order by contact_id asc
String order=ContactsContract.CommonDataKinds.Phone.CONTACT_ID+" ASC";
//?에 바인딩할 인자
String[] args = {"%"+keyword+"%"};
//원하는 정보를 얻어낸다.
Cursor cursor=resolver.query(contactUri, //table name
columns, //column name
where, //where
args, //selection args
order); //order by
while (cursor.moveToNext()){
int id=(int)cursor.getLong(0);
String phoneNumber=cursor.getString(1);
String name=cursor.getString(2);
//결과를 한줄의 문자열로 구성해서
String result=id+" | "+phoneNumber+" | "+name;
console.append(result+"\n");
}
}
}
- step19에서 권한을 얻어오는 작업에서, 전화거는 것으로 되어있는 상수를 바꾸어주면 된다.
(CALL_PHONE → READ_CONTACTS)
- 아래 이 메소드에서 반응을 본다. 이것은 액티비티가 가지고있는 메소드이다.
- 사용자가 어떤 권한을 켰는지 또는 켜지 않았는지 여기서 조사할 수 있다.
- 요청의 코드 번호를 아래에 인자로 전달하고, 인자로 배열(string[ ], int[ ])도 전달한다.
- 이 권한이 허가된 것의 값은 0이다. static final 상수로 정의되어 있다.(PERMISSION_GRANTED)
- 결과값이 0이 나오면 permission이 승인된 것이다.
- contacts의 배열을 가져올 예정이다.
- java에서 배열은 { } 이므로 저렇게 표시한다!
- permission 승인 얻은 경우(초록) / 얻지 못한 경우(빨강)
- if문 대신 switch 코드로 작성해보았다.
- if문 안에 if문을 또 넣어서 사용하면 코드가 지저분해질 수 있어서... 이런식으로 활용하면 좋다!
- getContentResolver() 를 쓰면 리턴타입으로 ContentResolver 타입이 나온다.
이렇게 액티비티의 메소드로부터 받아내면 된다.
- Intent 객체처럼 우리가 직접 객체를 생성할 필요는 없다.
- 쿼리 메소드 사용! 인자를 5개 받는다.
- SELECT 문을 작성한다고 생각하면 된다.
- 어디서 가지고 올 것인지(파랑),
무엇을 가지고 올 것인지(분홍),
어떤 조건으로 가지고 올 것인지(초록)
- 조건에 들어가는 값은 배열로 전달한다.( String[ ] )
- 정렬은 마지막 인자로 넣어주면 된다.
- SELECT 문이라고 생각하고 사용하면 된다.
ContactsContract.CommonDataKinds.Phone.CONTENT_URI;
- 연락처 정보를 저장할 Uri 객체를 얻어낸다.
- 여기 작성한 이 코드는 이 sql문과 같다.
- 이 ?에 값을 바인딩하려면 '%xxx%' 으로 넣어주면 된다.
SELECT contact_id, number, display_name
FROM CONTENT_URI
WHERE dispaly_name LIKE '%xxx%'
ORDER BY contact_id ASC
- 최종적으로 이런 sql문으로 작성된다고 볼 수 있다.
- content privider를 사용해서 마치 SELECT문을 수행하듯이 작성한다.
- query() 메소드 안에 5개의 정보를 전달한다.
- WHERE 절을 명시하지 않으면 모든 내용을 다 가져온다.
- 값이 동적으로 들어간다면 args 인자에 전달한다.
(만약 ?가 여러개라면 string 배열에 순서대로 넣는다.)
- Cursor 객체에서 데이터 빼내기
- Cursor는 ResultSet이라고 생각하면 된다.
- 아래 columnIndex에 들어간 0 1 2 는 각각 이것이다
- ID primary key는 long타입인데, int타입으로 받으면 된다.
- EditText에 console 필드 추가
- cursor로 가져온 값을 개행기호와 함께 console에 출력해주기!
- checkPermission 창이 뜬다
- 허용하면 이렇게 연락처가 출력된다.
- where 조건과 selection 인자를 모두 null로 넣어주면
가지고 있는 모든 연락처 목록이 출력된다.
- 이메일 값은 이렇게 따로 얻어내야 한다. phone과 같이 얻어낼 수는 없다.
'국비교육(22-23)' 카테고리의 다른 글
85일차(1)/Android App(49) : Notification(2) (0) | 2023.02.09 |
---|---|
84일차(2)/Android App(48) : Notification(1) (0) | 2023.02.09 |
83일차(1)/Android App(46) : 전화걸기 기능 구현 (0) | 2023.02.07 |
[Android Studio] 모듈 의존성 제거 (모듈 삭제) (0) | 2023.02.07 |
82일차(2)/Android App(45) : Interceptor 활용하기 (0) | 2023.02.07 |