국비교육(22-23)

83일차(1)/Android App(46) : 전화걸기 기능 구현

서리/Seori 2023. 2. 7. 23:43

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 명시만으로는 사용 불가하고, 사용자가 직접 켜주어야 한다.(전화)