Android SMS 보내기 - Android SMS bonaegi

메시지 전송, 수신, 연결 문제 해결하기

메시지를 전송 또는 수신할 수 없거나 웹에서 메시지에 연결하는 데 문제가 발생한다면 아래 권장된 조치를 확인하세요.

메시지 전송/수신 문제 해결하기

  • 최신 버전의 메시지가 설치되어 있는지 확인합니다.
  • SIM 카드를 사용 중인 경우 제대로 삽입되었는지 확인합니다.
  • Fi를 사용 중인 경우 Project Fi 앱에 로그인되어 있는지 확인합니다.
  • 메시지가 기본 문자 메시지 앱으로 설정되어 있는지 확인합니다. 기본 문자 메시지 앱 변경 방법 알아보기
  • 이용 중인 이동통신사에서 SMS, MMS 또는 RCS 메시지를 지원하는지 확인합니다.
  • 사용 중인 요금제나 통화 잔액이 메시지를 수신하고 전송하는 데 충분한지 확인합니다.
  • 통신 신호가 충분히 강한지 확인합니다.
  • 비행기 모드가 사용 설정되어 있는지 확인합니다. 사용 설정된 경우 비행기 모드를 사용 중지합니다.
  • iPhone에서 Android 휴대전화로 전환하는 경우 내 번호에 iMessage가 비활성화되어 있는지 확인합니다.
  • 휴대전화에서 메시지 앱을 변경한 후 메시지를 받지 못하는 경우 채팅 기능을 사용 중지해야 할 수도 있습니다.

특정 연락처와의 SMS 전송/수신 문제 해결하기

  • 연락처를 삭제한 다음 다시 추가합니다.
  • 연락처가 차단되어 있지 않은지 확인합니다.
  • 전화번호가 올바른지 확인합니다.
  • 국가 번호(예: 미국의 경우 +1)를 지정해야 하는지 확인합니다.

MMS 메시지 전송 문제 해결하기

  • 데이터 연결 상태가 정상인지 확인합니다.
  • 기기의 설정 앱에서 APN 설정을 기본값으로 재설정합니다.

다른 문제 해결 단계 시도

  • 기기 공간이 부족함: 기기 공간을 확보할 때까지 메시지를 보내거나 받을 수 없다는 알림을 받았으면 이 도움말에 따라 여유 공간을 확보합니다.
  • 읽을 수 없는 SMS를 받음: 시각적 음성사서함을 지원하는 휴대전화에서 지원하지 않는 휴대전화로 바꾼 경우 이동통신사로부터 읽을 수 없는 문자 메시지가 수신됩니다. 음성사서함 알림 관련 문제를 해결하세요.
  • iPhone에서 Android 휴대전화로 바꿈: iPhone에서 Android 휴대전화로 바꾼 경우 iMessage를 사용 중지해야 합니다.

도움이 되었나요?

어떻게 하면 개선할 수 있을까요?

아무래도 휴대폰에 앱을 개발하는 일이다 보니, 문자 전송 같은 기본 기능을 이용하는 앱을 개발하는 일이 많기는 하다.  요새는 카카오톡등을 이용하거나, FCM 등을 이용해서 알림을 보내는 기능등을 구현해 보지만, 예전 방식 처럼 SMS 을 전송해 보는 것도 오랜만 이기는 하다.

구글이 계정 정책등으로 인해 앱을 등록할 때 기본앱으로 사용할 수 없는 SMS 앱은 등록에 무리가 있다. 그래서 그냥 개인적으로 사용하기 위해서 기본앱 기능은 아니지만, 필요에 의한 문자 전송을 구현하는 앱을 만들어 볼 까 싶다.

먼저 앱을 구동하기 위해서는 Manifest 파일에 권한 설정을 등록해 준다. 

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.smssend0823">

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

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/Theme.SMSSend0823">
        
        ......
        
    </application>

</manifest>

SMS 발송 권한을 설정했으니, 앱을 실행하면서 실제 동작이 발생 하기 전에 사용자에게 권한을 획득 하는 코드를 activity 에 넣어 보자.

import android.Manifest;
import android.app.AlertDialog;
import android.content.DialogInterface;
import android.content.pm.PackageManager;
import android.database.Cursor;
import android.os.Bundle;
import android.telephony.SmsManager;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.Toast;

import androidx.annotation.NonNull;
import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat;
import androidx.fragment.app.Fragment;
import androidx.navigation.fragment.NavHostFragment;

import com.example.smssend0823.Database.DBHandler;
import com.example.smssend0823.Database.MsgTargetBean;
import com.example.smssend0823.Database.SendMessageAdapter;
import com.example.smssend0823.Database.SendMessageBean;
import com.example.smssend0823.Utils.OnBackPressedListener;
import com.example.smssend0823.databinding.FragmentMsgselectBinding;

import java.util.ArrayList;

public class MsgSelectFragment extends Fragment implements OnBackPressedListener {

    private static final String TAG = "MsgSelectFragment";
    private FragmentMsgselectBinding binding;
    ArrayList<SendMessageBean> beans;
    SendMessageAdapter adapter;
    DBHandler dbHandler ;
    String phoneNo ;
    String message ;
    private static final int PERMISSION_RQST_SEND = 101;

    public static Fragment newInstance() {
        MsgSelectFragment fragment = new MsgSelectFragment();
        return fragment;
    }

    @Override
    public View onCreateView(
            LayoutInflater inflater, ViewGroup container,
            Bundle savedInstanceState
    ) {

        binding = FragmentMsgselectBinding.inflate(inflater, container, false);
        return binding.getRoot();

    }

    public void onViewCreated(@NonNull View view, Bundle savedInstanceState) {
        super.onViewCreated(view, savedInstanceState);

        beans = new ArrayList<>();
        doDisplaySendMessage();
        binding.listSendMessage.setOnItemClickListener(new AdapterView.OnItemClickListener() {
            @Override
            public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
                int iCnt = 0 ;
                for(int i=0; i < beans.size() ;  i++) {
                    if (beans.get(i).isSelectedMsg()) iCnt++;
                }
                if (iCnt > 0) {
                    beans.get(position).setSelectedMsg(false);
                    adapter.notifyDataSetChanged(beans);
                    Toast.makeText(getContext(), iCnt +"개 이상 선택할 수 없습니다.", Toast.LENGTH_SHORT).show();
                    return;
                }
                beans.get(position).setSelectedMsg(!beans.get(position).isSelectedMsg());
                adapter.notifyDataSetChanged(beans);
            }
        });

        binding.btnSendMessage.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                int position = 0 ; int iCnt = 0 ;
                for(int i=0; i < beans.size() ;  i++) {
                    if (beans.get(i).isSelectedMsg()) {
                        position = i;
                        iCnt++;
                        break;
                    };
                }
                if (iCnt == 0) {
                    Toast.makeText(getContext(), "전송할 메시지를 선택하세요.", Toast.LENGTH_SHORT).show();
                    return;
                }
                message = beans.get(position).getSendMessage() ;
                AlertDialog.Builder builder = new AlertDialog.Builder(getContext());
                builder.setTitle("메시지 전송")
                        .setMessage("[" + beans.get(position).getSendMessage() + "] 로 전송할까요?")
                        .setPositiveButton("확인", new DialogInterface.OnClickListener() {
                            @Override
                            public void onClick(DialogInterface dialog, int which) {
                                dbHandler = DBHandler.open(getContext());
                                Cursor rs = dbHandler.selectAll();
                                while(rs.moveToNext()) {
                                    MsgTargetBean msgTargetBean = new MsgTargetBean();
                                    msgTargetBean.setId(rs.getInt(rs.getColumnIndex("_id")));
                                    msgTargetBean.setSendSeqNo(rs.getString(rs.getColumnIndex("sendSeqNo")));
                                    msgTargetBean.setReceivePhoneNum(rs.getString(rs.getColumnIndex("receivePhoneNum")));
                                    msgTargetBean.setSendMessage(rs.getString(rs.getColumnIndex("sendMessage")));
                                    msgTargetBean.setSendTy(rs.getString(rs.getColumnIndex("sendTy")));
                                    if("S".equals(msgTargetBean.getSendTy())) {
                                        phoneNo = msgTargetBean.getReceivePhoneNum();
                                        if (sendSMSMessage()) {
                                            msgTargetBean.setSendTy("Y");
                                            msgTargetBean.setSendMessage(message);
                                            dbHandler.update(msgTargetBean);
                                        }
                                    }
                                }
                                dbHandler.close();
                            }
                        })
                        .setNegativeButton("취소", new DialogInterface.OnClickListener() {
                            @Override
                            public void onClick(DialogInterface dialog, int which) {

                            }
                        });
                AlertDialog dialog = builder.create();
                dialog.show();
            }
        });
    }

    private void doDisplaySendMessage() {
        beans.clear();
        dbHandler = DBHandler.open(getContext());
        Cursor rs = dbHandler.selectMsgAll();
        while (rs.moveToNext()) {
            SendMessageBean bean = new SendMessageBean();
            bean.setSendMessage(rs.getString(rs.getColumnIndex("messageString")));
            beans.add(bean);
        }
        adapter = new SendMessageAdapter(beans, getContext());
        adapter.notifyDataSetChanged(beans);
        binding.listSendMessage.setAdapter(adapter);
    }

    protected boolean sendSMSMessage() {
        if (ContextCompat.checkSelfPermission(getContext(), Manifest.permission.SEND_SMS) != PackageManager.PERMISSION_GRANTED) {
            Toast.makeText(getContext(), "SMS 전송 권한에 대한 허가가 필요합니다.", Toast.LENGTH_SHORT).show();
            if (ActivityCompat.shouldShowRequestPermissionRationale(getActivity(),Manifest.permission.SEND_SMS)) {
                Log.e(TAG, "shouldShowRequestPermissionRationale");
            }
            else { ActivityCompat.requestPermissions(getActivity(), new String[]{Manifest.permission.SEND_SMS}, PERMISSION_RQST_SEND);
                Log.e(TAG, "requestPermissions");
            }
        } else {
            SmsManager smsManager = SmsManager.getDefault();
            smsManager.sendTextMessage(phoneNo, null, message, null, null);
            Toast.makeText(getContext(), "메시지가 전송 되었습니다.",Toast.LENGTH_LONG).show();
            return true ;
        }
        return false;
    }
    //Now once the permission is there or not would be checked
    @Override
    public void onRequestPermissionsResult(int requestCode, String permissions[], int[] grantResults) {
        switch (requestCode) {
            case PERMISSION_RQST_SEND: {
                if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {

                } else {Toast.makeText(getContext(), "SMS 발송이 되지 않았습니다. 잠시 뒤에 다시 시도 하세요.", Toast.LENGTH_LONG).show();
                    return;
                }
            }
        }
    }

    @Override
    public void onDestroyView() {
        super.onDestroyView();
        binding = null;
    }

    @Override
    public void onBackPressed() {
        getParentFragmentManager().beginTransaction()
                .replace(R.id.container, MsgSendListFragment.newInstance())
                .commitNow();
    }
}

source code 의 예시와 같이 sendSMSMessage() 함수에는 메시지 발송전에 권한 획득 여부를 확인하고, 권한이 없다면 사용자에게 권한 허가를 요청하는 알림을 띄워 권한을 획득 하게 된다.

다만, 이런 기능을 앱으로 이용하려면 playstore 에는 등록에 제한이 있으므로 다른 스토어를 이용하여야 한다.

SMS을 보내는 다른 방법은 권한 없이 사용할 수 있는 SMS Retriever API 을 활용하는 방법이 있다고 할 수 있는데, 실제 앱을 개발하다 보면 불편한 사항이 생기게 되어 있다.

https://developers.google.com/identity/sms-retriever/overview

SMS Retriever API를 사용한 자동 SMS 확인  |  SMS Verification APIs

이 페이지는 Cloud Translation API를 통해 번역되었습니다. Switch to English SMS Retriever API를 사용한 자동 SMS 확인 SMS Retriever API를 사용하면 사용자가 인증 코드를 수동으로 입력 할 필요없이 추가 앱 권

developers.google.com

Android SMS 보내기 - Android SMS bonaegi

참고해서 보면 좋을 것 깥다.