83일차(1)/Android App(46) : 전화걸기 기능 구현
- 이전 코드 복습 (Interceptor)
- 안드로이드에서 logincheck 경로로 원격지 서버에 로그인여부를 요청한다.
- 그러면 spring 서버에서 세션에 id라는 키값으로 저장된 값이 있는지 확인해서 map으로 응답한다.
- builder로 가져온 문자열을 사용해서 JSONObject 객체를 사용해서 읽어온다.
- 가상기기가 아닌 실 기기를 사용하면 이렇게 wifi를 사용한다.
- 저 인터넷 망에 컴퓨터가 들어온다. 와이파이 중계기도 있다.
- 이 컴퓨터에서 tomcat 웹서버를 돌린다. 이 서버의 ip 주소가 있다.(가상 ip)
- 가상 ip란 이 주소가 이 집 안에서만 통용되는 ip라는 의미이다.
- 가상기기가 아닌 진짜 핸드폰은 wifi에 연결되어 있다.
- wifi 를 사용해서 이 망을 통해서 서버에 요청하는 것
- 가상 기기 폰은 pc와 같은 ip 안에 있으므로 바로 요청이 가능하지만,
실제 폰으로 실험해보려면 wifi에 연결한 다음 요청해야 서버가 응답할 수 있다.
- 웹서버가 항상 응답을 해야하는 것은 아니다. 어떤 경우에는 인증된 사용자의 요청에만 응답한다!
- 해당 요청을 처리하기전에 실행순서가 이쪽으로 들어온다.
- 이 안에서 인증, 검증 작업을 한다.
- 사용자가 인증되지 않았다면 401 응답, 인증되었다면 개입하지 않고 흐름을 진행한다.
- WebConfig 안에 어떤 페이지에 제약을 걸어둘지 정한다
- 이전에 Spring에서 사용하던 레거시 프로젝트의 servlet-context.xml 과 같은 기능이다.
- 인터셉터 추가
- MobileLoginInterceptor 를 bean으로 만들어놓고, 이 bean을 여기에서 주입받는다.
- autowired로 DI(의존성 주입) 받아서 아래에서 사용한다.
- 그러면 안드로이드에서 galleryTask를 실행한다.
- api/gallery/ 의 하위요청에 대해 비동기 작업으로 로그인을 확인!
- 로그인되지 않은 상태에서 갤러리 목록보기를 누르면 이렇게 로그가 출력된다.
- 에러가 발생하면서 실행의 흐름이 catch로 넘어간 것이다.
새 모듈 생성
step19callphone
MainActivity
package com.example.step19callphone;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.net.Uri;
import android.os.Bundle;
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 inputPhone;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//필요한 객체의 참조값 얻어오기
inputPhone=findViewById(R.id.inputPhone);
Button dialBtn=findViewById(R.id.dialBtn);
//클릭 리스너 등록
dialBtn.setOnClickListener(v -> {
//입력한 전화번호를 읽어온다.
String phoneNumber=inputPhone.getText().toString();
//전화를 걸겠다는 intent(의도) 객체 생성하기
Intent intent = new Intent();
intent.setAction(Intent.ACTION_DIAL); //전화를 걸고 싶다는 의도를 Intent 객체에 담고
//전화번호를 Uri 객체에 포장을 한다.
Uri uri=Uri.parse("tel:"+phoneNumber);
//Intent 객체에 담는다.
intent.setData(uri);
//해당 Intent를 받아주는 액티비티를 실행해주세요 라고 운영체제에 요청을 한다.
startActivity(intent);
});
Button callBtn=findViewById(R.id.callBtn);
callBtn.setOnClickListener(v -> {
//전화를 걸기전에 전화걸기 허용을 했는지 확인
//전화걸기 권한이 체크되었는지 상수값 얻어오기
int permissionCheck= ContextCompat.checkSelfPermission(MainActivity.this,
android.Manifest.permission.CALL_PHONE);
//만일 권한이 허용되지 않았다면
if(permissionCheck != PackageManager.PERMISSION_GRANTED){
//권한을 허용하도록 유도한다.
//권한이 필요한 목록을 배열에 담는다.
String[] permissions={android.Manifest.permission.CALL_PHONE};
//배열을 전달해서 해당 권한을 부여하도록 요청한다.
ActivityCompat.requestPermissions(MainActivity.this,
permissions,
0); //요청의 아이디
return; //메소드는 여기서 종료
}
//전화걸기
call();
});
}
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
//만일 권한을 부여받았다면
if(grantResults[0] == PackageManager.PERMISSION_GRANTED){
//전화 걸기
call();
}else{
Toast.makeText(this, "전화를 거는 권한이 필요합니다.",
Toast.LENGTH_SHORT).show();
}
}
//전화 거는 메소드
public void call(){
//입력한 전화번호
String phoneNumber=inputPhone.getText().toString().trim();
//전화를 걸겠다는 Intent 객체 생성하기
Intent intent=new Intent();
intent.setAction(Intent.ACTION_CALL);
//전화번호를 Uri 로 얻어낸다.
Uri uri=Uri.parse("tel:"+phoneNumber);
//Intent 객체에 담는다.
intent.setData(uri);
//전화를 걸수 있는 액티비티를 실행 시킨다.
startActivity(intent);
}
}
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:hint="전화번호 입력..."
android:inputType="phone"
android:id="@+id/inputPhone"/>
<Button
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/dialBtn"
android:text="전화걸기"/>
<Button
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/callBtn"
android:text="전화걸기2"/>
</LinearLayout>
- 전화걸기 버튼을 누르면 실제로 전화가 걸리도록 하는 앱을 만들 예정!
** 우리가 만든 App에서
1. 전화를 거는 기능이 필요하다면? 전화 거는 기능을 우리가 직접 구현해야 할까?
(전화를 걸 수 있는 앱을 찾아서 실행시키고, 전화번호만 전달해서 전화를 걸게 하고 싶다.)
→ 전화를 걸 수 있는 app의 특정 Activity가 실행되어야 하는 조건이다. 이 액티비티에 전화번호만 전달한다!
2. 사진을 찍는 기능이 필요하다면? 카메라를 사용하는 기능을 직접 구현해야 할까?
(사진을 찍을 수 있는 앱을 찾아서 실행시키고, 찍은 사진 데이터만 가져오고 싶다.)
→ 사진을 찍을 수 있는 app의 특정 Activity가 실행되어야 하는 조건
→ 결과값도 받아와야 되는 상황이다.
- 모든 기능을 직접 만드는 대신 폰에 해당 어플리케이션이 있다면 그 어플리케이션을 실행해주기만 하면 된다.
- 사진을 찍고싶다면 이미 있는 카메라 어플리케이션을 사용하면 된다!
→ 있는 카메라어플로 사진을 찍고 나서 결과 데이터만 받아올 수 있으면 된다.
- 1번과 2번은 좀 상황이 다르다.
1은 전화만 걸면 되고(동작), 2번은 결과값을 받아오는것도 중요하다.(리턴값)
** 4대 컴포넌트
1. Activity
2. Service
3. BroadcastReceiver
4. ContentProvider
- 1~3은 intent를 통해서 활성화시킨다.
- 우리의 폰이라고 하면, 폰에는 여러가지 app이 있다.
ex) 구글 맵, 게임, 카메라, 전화 앱, ...
- 이곳에 들어갈 우리의 앱을 만들고 있는 것이다.
- 지금 할 작업은 폰의 다른 잠자고 있는 앱에 intent를 전달해주어야 하는 상황!
- 내가 전화를 걸고 싶으니까 전화를 걸 수 있는 activity를 알려줘~ 라고 요청을 보내는 것이다.
- 이 intent를 받아줄 수 있는 대상은 폰 안에 여러개일 수도 있고, 없을 수도 있다.
- 이전 예제에서 intent 객체를 생성해서 활용할 때는
new Intent(this, xxActivity.class) 로 전달했는데, 지금은 이렇게 할 수 없다.
- 폰 안에 있는 것이 어떤 액티비티인지 모르기 때문에!
- 다른 방식으로 intent를 전달하면 운영체제가 알아서 찾아서 실행까지 시켜 준다.
- 인텐트 객체를 생성한 후, intent.setAction() 을 사용!
- 이렇게 상수로 만들어진 ACTION_DIAL 을 사용하면 전화를 걸 수 있다.
- uri 문자열을 넣어준다.
- html 링크(a 요소)를 이렇게 작성한 것과 같다.
- 웹브라우저가 전화를 걸 수 있는 앱을 운영체제의 도움을 받아서 찾을 수 있게 해준다.
만약 핸드폰이라면 전화를 걸 수 있는 어플을 찾아서 실행해준다.
- Uri.parse() 안에 적으면 된다.
- 만약 메일주소를 넣으면 메일을 보낼 수 있는 어플리케이션을 찾아준다.
- uri data 타입을 넣어준다. uri 객체에 포장한다.
- 전화번호를 담아서 uri를 넘겨준다.
- 윈도우에서 어떤 파일을 열 때 열 수 있는 연결 프로그램을 찾듯이,
핸드폰 안에서도 이 전화 기능을 가진 액티비티를 찾아준다.
- 전화가 걸리는 어플리케이션으로 이동한다.
- ACTION_DIAL 기능을 받을 수 있는 액티비티를 실행해준 것이다.
- 이 상수는 전화가 바로 걸리지는 않는다. 전화를 거는 책임은 전화 어플에 있다.
- 이화면에서 전화버튼을 누르면 실제로 전화가 걸린다.
- 백버튼으로 돌아와보면, 우리 액티비티는 stop에 머물러있다가 다시 activate 된다.
- 전화를 걸고자 한다면(어떤 액티비티를 활성화시켜 동작시키고자 한다면) 의도 객체만 잘 작성하면 된다!
- 하지만 허용된 어플리케이션만 전화를 걸 수 있도록 해야 한다.
- 해당 데이터는 Uri 객체에 담아서 전달한다는 것을 기억하기!!
- Intent 객체 안에 많은 intent 로 할 수 있는 작업들이 있다.
- 웹 검색(구글 검색 등을 실행해줄 것), 연락처 불러오기 등등...
- 레이아웃에 버튼 추가
- 이 화면에서는 기본적으로 전화걸기에 적합한 키보드가 나온다.
- 저런 자판이 뜨는 이유는 inputType 을 "phone" 으로 작성해주었기 때문이다.
- manifest는 안드로이드 패키지 안의 설정을 담당한다.
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.CALL_PHONE"/>
<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>
- uses-permission에 PHONE 권한 설정을 넣어준다.
- 전화가 바로 걸린다.
- 이 작동방식은 운영체제에 대한 이해가 필요하다.
- 핸드폰 가상기기의 settings 에 들어가본다.
- Apps의 callphone 앱에 들어가보면
- 이렇게 permission이 있다. 권한을 켜는 창을 자동으로 띄워주는 역할을 한다.
- 그 권한이 활성화되어있는지 체크한다.
- manifest에 적어두었다고 해당 권한을 바로 쓸 수 있는 것은 아니고,
사용자가 켜주어야 그때 쓸 수 있다.
- setting의 권한에 '전화'가 있는 것은 manifest에서 이렇게 표기해주었기 때문이다!
- 하지만 자동으로 권한이 켜지는것은 아니다. 단 어떤 권한이 켜지도록 유도할 수 있다.
- 이것은 사용자에게 켜달라고 요청하는 코드이다!
- 이 퍼미션을 requestPermissions() 메소드에 전달하면 전화기능을 켜는 것을 요청하게 된다.
- onRequestPermissionsResult 메소드 오버라이드
- 이렇게 값을 전달받아서 if~else문으로 분기한다.
- 처음에 권한 체크를 하지 않았을 경우
→ 전화버튼을 눌렀을 때 권한이 허용되지 않았으면 메소드가 끝나고, 권한을 체크하는 창이 열릴 것이다.
- 권한 체크를 하면
→ 이 메소드가 알아서 호출되고 if 문 안으로 들어간다.
- 권한 체크 창에서 체크하지 않았다면?
→ else로 들어간다. 토스트 메시지를 띄운다.
- 현재 코드에서 전화를 거는 상황은 2가지이다.
1) 처음에 권한이 체크된 경우
2) 권한 승인 유도 창을 띄워서 거기서 체크한 경우
- inputPhone 변수를 필드로 만들기
public void call(){
//입력한 전화번호
String phoneNumber=inputPhone.getText().toString().trim();
//전화를 걸겠다는 Intent 객체 생성하기
Intent intent=new Intent();
intent.setAction(Intent.ACTION_CALL);
//전화번호를 Uri 로 얻어낸다.
Uri uri=Uri.parse("tel:"+phoneNumber);
//Intent 객체에 담는다.
intent.setData(uri);
//전화를 걸수 있는 액티비티를 실행 시킨다.
startActivity(intent);
}
- 전화거는 메소드 call 을 아래 따로 만들어놓고, 위의 메소드안에서 호출해 쓰는 것으로 함
- 이 화면을 자동으로 보여주는 역할이 저 requestPermissions() 메소드이다.
- 각각 권한을 허용했을 때의 흐름(초록) / 허용하지 않았을 때의 흐름(빨강)
- 허용하지 않으면 이런 토스트 메시지가 나온다.
- 허용되면 이렇게 바로 전화가 걸린다.
- grantResult[ ] 배열에서 여러개의 결과를 받아볼 수 있다.
- step18login 같은 경우에는 따로 지정된 권한이 없어 나오는 것이 없다.
- manifest 파일에서 권한 관련해 넣어놓은 것이 이 설정 창에서 나타나는 것!
- 이 permission 부분은 안드로이드 운영체제에 대한 이해가 좀 필요하다.
- internet 같은 경우에는 전화와는 다르게 manifest에 명시된 permission만 있으면 사용 가능하다.
- 그러나 어떤 permission은 manifest 명시만으로는 사용 불가하고, 사용자가 직접 켜주어야 한다.(전화)
'국비교육(22-23)' 카테고리의 다른 글
84일차(2)/Android App(48) : Notification(1) (0) | 2023.02.09 |
---|---|
84일차(1)/Android App(47) : Content Provider (0) | 2023.02.08 |
[Android Studio] 모듈 의존성 제거 (모듈 삭제) (0) | 2023.02.07 |
82일차(2)/Android App(45) : Interceptor 활용하기 (0) | 2023.02.07 |
82일차(1)/Android App(44) : 로그아웃 기능 구현 (0) | 2023.02.06 |