아무래도 휴대폰에 앱을 개발하는 일이다 보니, 문자 전송 같은 기본 기능을 이용하는 앱을 개발하는 일이 많기는 하다. 요새는 카카오톡등을 이용하거나, 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
참고해서 보면 좋을 것 깥다.
'모바일 앱(안드로이드)' 카테고리의 다른 글
안드로이드 디버깅 을 wifi 로 해 보기 (24) | 2021.09.11 |
---|---|
안드로이드 바코드 스캐너 연동을 활용한 기능 구현 이야기 (13) | 2021.09.08 |
안드로이드 앱 만들기 도전 5일차 구글 플레이스토어 앱 등록 (54) | 2021.09.01 |
안드로이드 앱 만들기 list view 에 checkbox 넣어보기 (2) | 2021.08.29 |
안드로이드 앱 만들기 도전 4일차 admob 달아 보기 (4) | 2021.08.24 |