728x90

안드로이드폰에서 전화수신시 앱에서 수신전화의 상태를 감지하여 전화수신 팝업을 띄우기 위한 기본 코드라고 보면 된다.

전화벨이 울리면 메시지가 2번씩 수신된다. 전화에 대한 정보를 2번씩 보내주나 보다.

그래서 팝업창 구현 등을 하려면 1번만 메시지가 수신되어야 하므로 1번만 띄우도록 처리하는 로직이 포함되었다.


--- AndroidManifest.xml 파일 추가할 사항 ---

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

<receiver
    android:name=".CallStateReceiver">
    <intent-filter>
        <action android:name="android.intent.action.PHONE_STATE"/>
    </intent-filter>
</receiver>

 --- CallStateReceiver.java ---

import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.telephony.PhoneNumberUtils;
import android.telephony.PhoneStateListener;
import android.telephony.TelephonyManager;
import android.util.Log;

public class CallStateReceiver extends BroadcastReceiver {
    static String mLastState;
    static final String TAG = "CallStateListner";
    
    @Override
    public void onReceive(Context context, Intent intent) {
        Log.d(TAG, "CallStateReceiver >>>>>>> 전화 수신");
        
        // 전화 수신 체크
        CallReceivedChk(context, intent);
    }
    
    private void CallReceivedChk(Context context, Intent intent) {
        TelephonyManager telephony = (TelephonyManager)context.getSystemService(Context.TELEPHONY_SERVICE);
        telephony.listen(new PhoneStateListener(){
            @Override
            public void onCallStateChanged(int state, String incomingNumber) {
                String mState = String.valueOf(state);
                if (mState.equals(mLastState)) { // 두번 호출되는 문제 해결 목적
                    return;
                } else {
                    mLastState = mState;
                }
                
                switch(state) {
                    case TelephonyManager.CALL_STATE_IDLE:
                        Log.d(TAG,"전화 수신 상태가 아닙니다 : CALL_IDLE");
                        break;
                    case TelephonyManager.CALL_STATE_OFFHOOK:
                        Log.d(TAG, "전화를 받았습니다 : CALL_OFFHOOK");
                        break;
                    case TelephonyManager.CALL_STATE_RINGING:
                        Log.d(TAG, "CALL_RINGING, 수신 전화번호 : " + PhoneNumberUtils.formatNumber(incomingNumber));
                        // 처리하고자 하는 코드 추가하면 된다.
                        break;            
                }
            }    
        }, PhoneStateListener.LISTEN_CALL_STATE);
    }

}


위 코드를 가지고 활용을 해서 팝업창을 띄우는 코드를 새롭게 만들거나

http://gun0912.tistory.com/46 에 있는 코드를 활용하면 팝업창을 띄울 수 있다.

위 코드를 그대로 사용하면 앱이 죽는 증상이 있다.
코드가 100% 완벽하게 동작되도록 하려면 개발자 본인이 수정 보완해야 한다.

코드에 대한 완벽한 분석과 기초지식이 있어야 가능하더라.


스마트폰 스크린이 잠겨있는 OFF 상태에서 전화가 수신되면 수신 팝업창을 ON 상태로 바꿔 사용자에게 메시지를 알려주려면 어떻게 해야 할까?


안드로이드 시스템에서는 장비가 휴면모드로 들어가게 되면 배터리 소모를 최소화하기 위하여 불필요한 CPU나 와이파이를 포함한 모든 기능들은 정지시키려고 한다.
Service 가 지속적으로 실행이 되고 있는 것을 확실하게 보장하기 위해서 "WAKE Locks"를 사용해야 한다. Wake Lock는 앱에서 특정 기능들을 계속 활성화 시켜놓아야 한다는 것을 시스템에 알려주는 역할을 하는 것이다.


private static PowerManager.WakeLock wakeLock;
private void WakeLock(Context context, Intent intent) {
    // 잠든 화면 깨우기
    if (wakeLock != null) {
        return;
    }

    PowerManager pm = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
    wakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK | PowerManager.ACQUIRE_CAUSES_WAKEUP
            | PowerManager.ON_AFTER_RELEASE, "hi");
    wakeLock.acquire();  
}


이 메소드를 BroadcastReceiver 에 추가한다.


START_STICKY : 재생성과 on StartCommand() 호출(with null intent)
START_NOT_STICKY : 서비스 재 실행하지 않음
START_REDELIVER_INTENT : 재생성과 on StartCommand() 호출(with same intent)
 


읽어볼만한 자료

배터리를 잡아먹는 주원인 WakeLock : http://widzard.tistory.com/36

블로그 이미지

Link2Me

,
728x90

어플 기능 개선이나 버그 수정 등으로 업그레이드를 해야 할 경우가 생긴다.

이때 업그레이드 여부 검사를 하는 로직과 파일 다운로드 기능을 구현했다.


구글링으로 검색하면 파일 다운로드 로직은 많이 나온다.

http://www.androidbegin.com/tutorial/android-download-progress-bar/

약간씩 설명이 다르지만 대부분 유사한 설명이 나온다.


서버의 버전과 어플의 버전을 비교하여 서버의 버전이 높으면 파일을 자동으로 다운로드하는 로직으로 구현했다.

기본적인 개념은 http://link2me.tistory.com/1277 파일을 실행해보면 알 수 있다.

파일을 다운로드하면 상태바가 활성화되고, 다운로드 하지 않을 경우에는 화면에 보이지 않도록 UI 를 선택적으로 처리했다.


 import android.app.Activity;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.pm.PackageManager;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.Build;
import android.os.Bundle;
import android.os.Environment;
import android.support.v7.app.AlertDialog;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.widget.Button;
import android.widget.LinearLayout;
import android.widget.ProgressBar;
import android.widget.TextView;
import android.widget.Toast;

import java.io.BufferedInputStream;
import java.io.BufferedReader;
import java.io.DataOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLConnection;

public class MainActivity extends AppCompatActivity {
    public SharedPreferences settings;
    static final int PERMISSION_REQUEST_CODE = 1;
    String[] PERMISSIONS = {"android.permission.READ_EXTERNAL_STORAGE","android.permission.WRITE_EXTERNAL_STORAGE"};

    LinearLayout linearLayout;
    TextView textView;
    ProgressBar progressBar;
    DownloadFileFromURL downloadFileAsyncTask;
    static boolean isUpgrade = false;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        // 네트워크 연결 검사
        if(NetworkConnection() == false){
            NotConnected_showAlert();
        }

        if (!hasPermissions(PERMISSIONS)) { //퍼미션 허가를 했었는지 여부를 확인
            requestNecessaryPermissions(PERMISSIONS);//퍼미션 허가안되어 있다면 사용자에게 요청
        }

        // Linear layout 정의
        linearLayout = (LinearLayout) findViewById(R.id.downloadprogress_layout);
        textView = (TextView)findViewById(R.id.txtView01);
        progressBar = (ProgressBar)findViewById(R.id.progressBar);

        // 업그레이드 유무 검사
        VersionCheck();

        if(isUpgrade == false){
            linearLayout.setVisibility(View.GONE);
        } else {
            linearLayout.setVisibility(View.VISIBLE);
        }

        //취소 버튼 이벤트 처리 및 리스너 등록
        Button btn_Cancel = (Button)findViewById(R.id.btnCancel);
        btn_Cancel.setOnClickListener(new View.OnClickListener(){
            public void onClick(View v){
                downloadFileAsyncTask.cancel(true);
            }
        });

    }

    private void VersionCheck(){
        settings = getSharedPreferences("settings", Activity.MODE_PRIVATE);
        Uri.Builder builder = new Uri.Builder()
                .appendQueryParameter("idx", "2"); // settings.getString("idx","" )
        String postParams = builder.build().getEncodedQuery();

        new isUpgradeTask().execute(Value.IPADDRESS + "/file/UpgradeChk.php", postParams);
    }

    private void UpgradeChk(final String serverVersion){

        try {
            String Appversion = Value.VERSION;
            Appversion = Appversion.replaceAll("[^0-9]", ""); // 버전에서 숫자만 추출
            System.out.println("App Version : " + Appversion + " Server Version : " + serverVersion);

            if (Integer.parseInt(Appversion) < Integer.parseInt(serverVersion)) { // 서버 버전이 더 높으면
                AlertDialog.Builder builder = new AlertDialog.Builder(MainActivity.this);
                builder.setTitle("버전 업그레이드");
                builder.setMessage("버전 업그레이드 되었습니다. 새로운 버전으로 다시 설치해주십시오.");
                builder.setPositiveButton("업데이트", new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface dialog, int which) {
                        Toast.makeText(getApplicationContext(), "파일 다운로드 중입니다.", Toast.LENGTH_SHORT).show();
                        isUpgrade = true;
                        linearLayout.setVisibility(View.VISIBLE);
                        // 백그라운드 객체를 만들어 줌
                        downloadFileAsyncTask = new DownloadFileFromURL(MainActivity.this);
                        downloadFileAsyncTask.execute(Value.IPADDRESS + "/file/apkDownload.php");
                    }
                });
                builder.setNegativeButton("취소", new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface dialog, int which) {
                        // 업데이트 취소시 어플을 완전 종료시킴
                        moveTaskToBack(true);
                        finish();
                        android.os.Process.killProcess(android.os.Process.myPid());
                    }
                });
                builder.create();
                builder.show();
            }
        } catch (Exception e) {
            e.printStackTrace();
        }

    }

    class isUpgradeTask extends AsyncTask<String,String,String>{
        @Override
        protected String doInBackground(String... params) {
            BufferedReader bufferedReader = null;
            String Response = null;
            try {
                URL url = new URL(params[0]);
                HttpURLConnection conn = (HttpURLConnection) url.openConnection();

                if(conn != null){ // 연결되었으면
                    //add request header
                    conn.setRequestMethod("POST");
                    conn.setRequestProperty("USER-AGENT", "Mozilla/5.0");
                    conn.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
                    conn.setRequestProperty("Accept-Language", "en-US,en;q=0.5");
                    conn.setConnectTimeout(10000);
                    conn.setReadTimeout(10000);
                    conn.setUseCaches(false);
                    conn.setDefaultUseCaches(false);
                    conn.setDoOutput(true); // POST 로 데이터를 넘겨주겠다는 옵션
                    conn.setDoInput(true);

                    // Send post request
                    DataOutputStream wr = new DataOutputStream(conn.getOutputStream());
                    wr.writeBytes(params[1]);
                    wr.flush();
                    wr.close();

                    int responseCode = conn.getResponseCode();
                    System.out.println("GET Response Code : " + responseCode);
                    if(responseCode == HttpURLConnection.HTTP_OK){ // 연결 코드가 리턴되면
                        bufferedReader = new BufferedReader(new InputStreamReader(conn.getInputStream()));
                        Response = bufferedReader.readLine();
                        Response = Response.replaceAll("[^0-9]", ""); // 버전에서 숫자만 추출
                    }
                    bufferedReader.close();
                }
                return Response;
                // 수행이 끝나고 리턴하는 값은 다음에 수행될 onProgressUpdate 의 파라미터가 된다
            } catch(Exception e){
                return new String("Exception: " + e.getMessage());
            }
        }

        protected void onPostExecute(String result) {
            UpgradeChk(result);
        }

    }

    class DownloadFileFromURL extends AsyncTask<String,Integer,String>{
        private Context context;
        private String savePath;
        private File file, dir;
        int count;

        public DownloadFileFromURL(Context context) {
            this.context = context;
        }

        protected void onPreExecute() {
            super.onPreExecute();
            progressBar.setProgress(0);
        }

        protected String doInBackground(String... params) {
            URLConnection connection = null;
            InputStream input = null;
            OutputStream output = null;
            int lenghtOfFile =0;

            savePath = "/Android/data/" + getPackageName();
            dir = new File(Environment.getExternalStorageDirectory().getAbsolutePath(), savePath);
            if (!dir.exists()) dir.mkdirs(); // make dir
            file = new File(dir, Value.APKNAME);  // APK 이름 변경

            try {
                URL url = new URL(params[0]);
                try {
                    connection = url.openConnection();
                    connection.connect();
                } catch (IOException e) {
                    e.printStackTrace();
                }

                lenghtOfFile  = connection.getContentLength(); //파일 크기를 가져옴
                if (file.exists()) {
                    file.delete(); // 파일이 존재하면 기존 파일을 삭제
                }

                try {
                    input = new BufferedInputStream(url.openStream());
                    output = new FileOutputStream(file);
                    byte data[] = new byte[1024];
                    long total = 0;

                    while ((count = input.read(data)) != -1) {
                        if (isCancelled()) {
                            input.close();
                            return String.valueOf(-1);
                        }
                        total = total + count;
                        if (lenghtOfFile > 0) { // 파일 총 크기가 0 보다 크면
                            publishProgress((int)(total * 100 / lenghtOfFile));
                        }
                        System.out.println("Download Percent === " + (int)(total * 100 / lenghtOfFile));
                        System.out.println("Total === " + total);
                        output.write(data, 0, count); //파일에 데이터를 기록
                    }
                    System.out.println("Download total Size === " + lenghtOfFile);

                    output.flush();
                    output.close();
                    input.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }

            } catch (MalformedURLException e) {
                e.printStackTrace();
            }
            return null;
        }

        @Override
        protected void onProgressUpdate(Integer... progress) {
            super.onProgressUpdate(progress);
            //백그라운드 작업의 진행상태를 표시하기 위해서 호출하는 메소드
            progressBar.setProgress(progress[0]);
            textView.setText("진행정도 : "+progress[0]+"%");
        }

        protected void onPostExecute(String result) {
            //백그라운 작업이 끝난후에 호출되는 메소드
            System.out.println("result === " + result);
            linearLayout.setVisibility(View.GONE);
            if ( result == null ) {
                Toast.makeText(getApplicationContext(), "다운로드 완료되었습니다.", Toast.LENGTH_LONG).show();
                Intent intent = new Intent(Intent.ACTION_VIEW);
                intent.setDataAndType(Uri.fromFile(file), "application/vnd.android.package-archive");
                context.startActivity(intent);
            } else {
                Toast.makeText(getApplicationContext(), "다운로드 에러", Toast.LENGTH_LONG).show();
            }
        }

        protected void onCancelled(){
            // cancel메소드를 호출하면 자동으로 호출되는 메소드
            // 작업을 취소했을 경우에 사용되는 메소드
            progressBar.setProgress(0);
            textView.setText("다운로드 진행 취소됨");
        }
    }

    private boolean hasPermissions(String[] permissions) {
        int res = 0;
        //스트링 배열에 있는 퍼미션들의 허가 상태 여부 확인
        for (String perms : permissions){
            res = checkCallingOrSelfPermission(perms);
            if (!(res == PackageManager.PERMISSION_GRANTED)){
                //퍼미션 허가 안된 경우
                return false;
            }
        }
        return true;  //퍼미션이 허가된 경우
    }

    private void requestNecessaryPermissions(String[] permissions) {
        //마시멜로( API 23 )이상에서 런타임 퍼미션(Runtime Permission) 요청
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
            requestPermissions(permissions, PERMISSION_REQUEST_CODE);
        }
    }

    @Override
    public void onRequestPermissionsResult(int permsRequestCode, String[] permissions, int[] grantResults){
        switch(permsRequestCode){
            case PERMISSION_REQUEST_CODE:
                if (grantResults.length > 0) {
                    boolean readAccepted = grantResults[0] == PackageManager.PERMISSION_GRANTED;
                    boolean writeAccepted = grantResults[1] == PackageManager.PERMISSION_GRANTED;

                    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
                        if( !readAccepted || !writeAccepted  ) {
                            showDialogforPermission("앱을 실행하려면 퍼미션을 허가하셔야합니다.");
                            return;
                        }
                    }
                }
                break;
        }
    }

    private void showDialogforPermission(String msg) {

        final AlertDialog.Builder myDialog = new AlertDialog.Builder(  MainActivity.this);
        myDialog.setTitle("알림");
        myDialog.setMessage(msg);
        myDialog.setCancelable(false);
        myDialog.setPositiveButton("예", new DialogInterface.OnClickListener() {
            public void onClick(DialogInterface arg0, int arg1) {
                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
                    requestPermissions(PERMISSIONS, PERMISSION_REQUEST_CODE);
                }

            }
        });
        myDialog.setNegativeButton("아니오", new DialogInterface.OnClickListener() {
            public void onClick(DialogInterface arg0, int arg1) {
                finish();
            }
        });
        myDialog.show();
    }

    public void NotConnected_showAlert() {
        AlertDialog.Builder builder = new AlertDialog.Builder(MainActivity.this);
        builder.setTitle("네트워크 연결 오류");
        builder.setMessage("사용 가능한 무선네트워크가 없습니다.\n" + "먼저 무선네트워크 연결상태를 확인해 주세요.")
                .setCancelable(false)
                .setPositiveButton("확인", new DialogInterface.OnClickListener() {
                    public void onClick(DialogInterface dialog, int id) {
                        finish(); // exit
                        //application 프로세스를 강제 종료
                        android.os.Process.killProcess(android.os.Process.myPid() );
                    }
                });
        AlertDialog alert = builder.create();
        alert.show();
    }

    public boolean NetworkConnection() {
        int[] networkTypes = {ConnectivityManager.TYPE_MOBILE, ConnectivityManager.TYPE_WIFI};
        try {
            ConnectivityManager manager = (ConnectivityManager) getSystemService (Context.CONNECTIVITY_SERVICE);
            for (int networkType : networkTypes) {
                NetworkInfo activeNetwork = manager.getActiveNetworkInfo();
                if(activeNetwork != null && activeNetwork.getType() == networkType){
                    return true;
                }
            }
        } catch (Exception e) {
            return false;
        }
        return false;
    }
}

 import android.support.v7.app.AppCompatActivity;

public class Value extends AppCompatActivity {

    public static final String IPADDRESS = "http://192.168.0.2"; // SERVER IP
    public static final String VERSION = "1.0.0"; // App Version
    public static final String APKNAME = "AddressBook.apk" ; //APK name

}


<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    tools:context="com.tistory.link2me.filedownload.MainActivity">

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center"
        android:padding="10dp"
        android:text="업그레이드 체크"  />

    <LinearLayout
        android:id="@+id/downloadprogress_layout"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="vertical">

        <TextView
            android:id="@+id/txtView01"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:textSize="18dp"
            android:padding="10dp"
            android:text="Progress Status"/>

        <LinearLayout
            android:orientation="horizontal"
            android:layout_width="match_parent"
            android:layout_height="wrap_content">

            <ProgressBar
                android:id="@+id/progressBar"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                style="@style/Base.Widget.AppCompat.ProgressBar.Horizontal"
                android:layout_marginTop="5dp"
                android:max="100"
                android:padding="10dp"
                android:layout_weight="8" />
            <Button
                android:id="@+id/btnCancel"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_weight="1"
                android:layout_marginTop="5dp"
                android:layout_marginLeft="10dp"
                android:text="취소"
                android:textSize="12dp"
                />
        </LinearLayout>

    </LinearLayout>

</LinearLayout>


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

    <uses-permission android:name="android.permission.INTERNET" />
    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
    <uses-permission android:name="android.permission.CHANGE_NETWORK_STATE" />
    <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
    <uses-permission android:name="android.permission.CHANGE_WIFI_STATE" />
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
    <uses-permission android:name="android.permission.WAKE_LOCK" />

    <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/AppTheme">
        <activity android:name=".MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>

</manifest>


블로그 이미지

Link2Me

,
728x90

Eclipse 에서 만든 코드를 Android Studio 로 변환하고 나서 신규로 코드를 추가하면 문제가 생긴다.

FCM 을 이용한 메시지 발송시 메시지 수신하는 걸 해결하지 못한 상태다.

FCM 메시지 발송을 위한 TokenID 를 회원DB에 저장하는 방법을 Login.java 코드를 이용하여 구현했다.


Login.java 파일에서 로그인 정보를 입력하고 확인을 누르면,  POST 방식으로 서버에 있는 loginChk.php 파일로 전송되어 내용을 확인하고 정보를 기록하고 결과값을 반환한다.

서버에서 결과값은 echo 문을 다시 안드로드이드폰에서 받아서 그 결과값을 기준으로 로그인 성공, 로그인 실패, 인증된 단말인지 여부 체크 등을 한다.


사용자 기기 토큰 정보를 획득하는 코드는 http://blog.naver.com/PostView.nhn?blogId=cosmosjs&logNo=220739141098&categoryNo=0&parentCategoryNo=56&viewDate=&currentPage=1&postListTopCurrentPage=1&from=search 를 참조하면서 해보는 중이다.


=== Login.java ====

package com.tistory.link2me.addresschart;

import android.app.ActionBar;
import android.app.Activity;
import android.app.AlertDialog;
import android.app.ProgressDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.SharedPreferences;
import android.net.ConnectivityManager;
import android.os.Bundle;
import android.provider.Settings.Secure;
import android.telephony.TelephonyManager;
import android.util.Log;
import android.view.KeyEvent;
import android.view.View;
import android.webkit.CookieManager;
import android.webkit.CookieSyncManager;
import android.widget.Button;
import android.widget.CheckBox;
import android.widget.EditText;
import android.widget.Toast;

import com.google.firebase.iid.FirebaseInstanceId;

import org.apache.http.NameValuePair;
import org.apache.http.client.HttpClient;
import org.apache.http.client.ResponseHandler;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.cookie.Cookie;
import org.apache.http.impl.client.BasicResponseHandler;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.protocol.HTTP;

import java.util.ArrayList;
import java.util.List;

public class Login extends Activity {

    String getDeviceID; // 스마트기기의 장치 고유값
    ProgressDialog dialog = null;
    EditText etId;
    EditText etPw;

    String loginID;
    String loginPW;
    CheckBox autologin;
    Boolean loginChecked;
    List<NameValuePair> params;
    public SharedPreferences settings;
    CookieManager cookieManager;

    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.login);

        // ActionBar 제거하기
        ActionBar actionbar = getActionBar();
        actionbar.hide();

        // 네트워크 연결상태 체크
        if(NetworkConnection() == false){
            NotConnected_showAlert();
        }

        etId = (EditText) findViewById(R.id.login_id_edit);
        etPw = (EditText) findViewById(R.id.login_pw_edit);
        autologin = (CheckBox) findViewById(R.id.autologinchk);

        settings = getSharedPreferences("settings", Activity.MODE_PRIVATE);
        loginChecked = settings.getBoolean("LoginChecked", false);
        if (loginChecked) {
            etId.setText(settings.getString("loginID", ""));
            etPw.setText(settings.getString("loginPW", ""));
            autologin.setChecked(true);
        }

        if(!settings.getString("loginID", "").equals("")) etPw.requestFocus();

        CookieSyncManager.createInstance(this);
        cookieManager = CookieManager.getInstance();
        CookieSyncManager.getInstance().startSync();

        Button submit = (Button) findViewById(R.id.login_btn);
        submit.setOnClickListener(new Button.OnClickListener(){

            @Override
            public void onClick(View v) {
                dialog = ProgressDialog.show(Login.this, "", "Validating user...", true);
                 new Thread(new Runnable() {
                        public void run() {
                            login();
                        }

                      }).start();
            }

        });

    }

    void login() {
        try {
            loginID = etId.getText().toString().trim();
            loginPW = etPw.getText().toString().trim();

            // 단말기의 ID 정보를 얻기 위해서는 READ_PHONE_STATE 권한이 필요
            TelephonyManager mTelephony = (TelephonyManager) getSystemService(Context.TELEPHONY_SERVICE);
            if (mTelephony.getDeviceId() != null){
                getDeviceID = mTelephony.getDeviceId();  // 스마트폰 기기 정보
            } else {
                getDeviceID = Secure.getString(getApplicationContext().getContentResolver(), Secure.ANDROID_ID);
            }

            // 사용자 기기의 고유 토큰 정보를 획득
            String getToken = FirebaseInstanceId.getInstance().getToken();

            String postURL = Value.IPADDRESS + "/loginChk.php";
            HttpPost post = new HttpPost(postURL);

            // 전달할 인자들
            params = new ArrayList<NameValuePair>();
            params.add(new BasicNameValuePair("loginID", loginID));
            params.add(new BasicNameValuePair("loginPW", loginPW));
            params.add(new BasicNameValuePair("deviceID", getDeviceID));
            params.add(new BasicNameValuePair("tokenID", getToken));

            UrlEncodedFormEntity ent = new UrlEncodedFormEntity(params,HTTP.UTF_8);
            post.setEntity(ent);

            HttpClient httpclient = new DefaultHttpClient();

            ResponseHandler<String> responseHandler = new BasicResponseHandler();
            final String responsePost = httpclient.execute(post, responseHandler);

            System.out.println("DeviceID : " + getDeviceID);
            System.out.println("Login Token : " + getToken);
            System.out.println("Response : " + responsePost);

            runOnUiThread(new Runnable() {
                public void run() {
                    dialog.dismiss();
                }
            });

            if(responsePost.equalsIgnoreCase("1")){ // 로그인 정보 일치
                runOnUiThread(new Runnable() {
                    public void run() {
                        Toast.makeText(Login.this,"Login Success", Toast.LENGTH_SHORT).show();
                    }
                });

                // 쿠키 정보 생성을 통한 PHP 세션 접속 유지 처리
                List<Cookie> cookies = ((DefaultHttpClient)httpclient).getCookieStore().getCookies();
                if (!cookies.isEmpty()) {
                    for (int i = 0; i < cookies.size(); i++) {
                        String cookieString = cookies.get(i).getName() + "="
                                    + cookies.get(i).getValue();
                        Log.e("PHP_setCookie", cookieString);
                        cookieManager.setCookie(Value.IPADDRESS, cookieString);
                    }
                }
                Thread.sleep(300);

                startActivity(new Intent(this.getApplicationContext(), MainActivity.class));
                finish(); // finish()를 호출해서 Activity를 없애줌

            } else if(responsePost.equalsIgnoreCase("-1")){ // 등록된 단말기와 불일치
                deviceDismatch_showAlert();
            } else {
                showAlert();
            }
        } catch(Exception e) {
            dialog.dismiss();
            System.out.println("Exception : " + e.getMessage());
        }

    }

    public void onStop(){
        // 어플리케이션이 화면에서 사라질때
        super.onStop();
        // 자동 로그인이 체크되어 있고, 로그인에 성공했으면 폰에 자동로그인 정보 저장
        if (autologin.isChecked()) {
            settings = getSharedPreferences("settings",Activity.MODE_PRIVATE);
            SharedPreferences.Editor editor = settings.edit();

             editor.putString("loginID", loginID);
             editor.putString("loginPW", loginPW);
             editor.putBoolean("LoginChecked", true);

             editor.commit();
        } else {
            // 자동 로그인 체크가 해제되면 폰에 저장된 정보 모두 삭제
            settings = getSharedPreferences("settings", Activity.MODE_PRIVATE);
            SharedPreferences.Editor editor = settings.edit();
            editor.clear(); // 모든 정보 삭제
            editor.commit();
        }

    }

    public void deviceDismatch_showAlert(){
        Login.this.runOnUiThread(new Runnable() {
            public void run() {
                AlertDialog.Builder builder = new AlertDialog.Builder(Login.this);
                builder.setTitle("등록단말 불일치");
                builder.setMessage("최초 등록된 단말기가 아닙니다.\n" + "관리자에게 문의하여 단말기 변경신청을 하시기 바랍니다.")
                       .setCancelable(false)
                       .setPositiveButton("OK", new DialogInterface.OnClickListener() {
                           public void onClick(DialogInterface dialog, int id) {
                           }
                       });
                AlertDialog alert = builder.create();
                alert.show();
            }
        });
    }

    public void showAlert(){
        Login.this.runOnUiThread(new Runnable() {
            public void run() {
                AlertDialog.Builder builder = new AlertDialog.Builder(Login.this);
                builder.setTitle("로그인 에러");
                builder.setMessage("로그인 정보가 일치하지 않습니다.")
                       .setCancelable(false)
                       .setPositiveButton("OK", new DialogInterface.OnClickListener() {
                           public void onClick(DialogInterface dialog, int id) {
                           }
                       });
                AlertDialog alert = builder.create();
                alert.show();
            }
        });
    }

    private void NotConnected_showAlert() {
        AlertDialog.Builder builder = new AlertDialog.Builder(Login.this);
        builder.setTitle("네트워크 연결 오류");
        builder.setMessage("사용 가능한 무선네트워크가 없습니다.\n" + "먼저 무선네트워크 연결상태를 확인해 주세요.")
               .setCancelable(false)
               .setPositiveButton("확인", new DialogInterface.OnClickListener() {
                   public void onClick(DialogInterface dialog, int id) {
                       finish(); // exit
                        //application 프로세스를 강제 종료
                        android.os.Process.killProcess(android.os.Process.myPid() );
                   }
               });
        AlertDialog alert = builder.create();
        alert.show();

    }

    private boolean NetworkConnection() {
        ConnectivityManager manager = (ConnectivityManager) getSystemService (Context.CONNECTIVITY_SERVICE);
        boolean isMobileAvailable = manager.getNetworkInfo(ConnectivityManager.TYPE_MOBILE).isAvailable();
        boolean isMobileConnect = manager.getNetworkInfo(ConnectivityManager.TYPE_MOBILE).isConnectedOrConnecting();
        boolean isWifiAvailable = manager.getNetworkInfo(ConnectivityManager.TYPE_WIFI).isAvailable();
        boolean isWifiConnect = manager.getNetworkInfo(ConnectivityManager.TYPE_WIFI).isConnectedOrConnecting();

        if ((isWifiAvailable && isWifiConnect) || (isMobileAvailable && isMobileConnect)){
            return true;
        }else{
            return false;
        }
    }

    // Back 버튼을 누르면 어플 종료여부 확인 처리
    @Override
    public boolean onKeyDown(int keyCode, KeyEvent event) {
        if( keyCode == KeyEvent.KEYCODE_BACK ) {
            new AlertDialog.Builder(this).setIcon(android.R.drawable.ic_dialog_alert).setTitle("Quit").setMessage("어플을 종료하시겠습니까?").setPositiveButton("Yes", new DialogInterface.OnClickListener() {
                @Override
                public void onClick( DialogInterface dialog, int which) {
                    moveTaskToBack(true); // 본Activity finish후 다른 Activity가 뜨는 걸 방지.
                    finish();
                    //application 프로세스를 강제 종료
                    android.os.Process.killProcess(android.os.Process.myPid() );
                }
        }).setNegativeButton( "No", null ).show();

        return true;
      }

      return super.onKeyDown(keyCode, event);
     }

    @Override
    protected void onResume()
    {
        super.onResume();
        CookieSyncManager.getInstance().startSync();
    }

    @Override
    protected void onPause()
    {
        super.onPause();
        CookieSyncManager.getInstance().stopSync(); // 동기화 종료
    }
}
 


이제 MySQL DB 및 PHP 코드를 구현해야 한다.

MySQL DB에는 tokenID 칼럼을 추가한다. Varchar(200) 으로 길이를 설정해준다. 토콘의 길이가 상당히 길더라.

테스트하는 회원 테이블은 회원ID와 회원 Data 를 분리하여 저장하는 방식으로 만들어보고 있다.


테이블 구조

CREATE TABLE IF NOT EXISTS `member_id` (
  `uid` int(11) NOT NULL AUTO_INCREMENT,
  `site` int(11) NOT NULL DEFAULT '0',
  `id` varchar(50) NOT NULL DEFAULT '',
  `pw` varchar(50) NOT NULL DEFAULT '',
  `code` int(6) NOT NULL DEFAULT '0',
  `admin` int(2) NOT NULL DEFAULT '0',
  PRIMARY KEY (`uid`),
  UNIQUE KEY `id` (`id`),
  KEY `code` (`code`)
) ENGINE=MyISAM  DEFAULT CHARSET=utf8 AUTO_INCREMENT=1 ;


CREATE TABLE IF NOT EXISTS `member_data` (
  `memberuid` int(11) NOT NULL,
  `site` int(11) NOT NULL DEFAULT '0',
  `auth` tinyint(4) NOT NULL DEFAULT '0',
  `level` int(11) NOT NULL DEFAULT '0',
  `admin` tinyint(4) NOT NULL DEFAULT '0',
  `email` varchar(50) NOT NULL DEFAULT '',
  `name` varchar(30) NOT NULL DEFAULT '',
  `nic` varchar(50) NOT NULL DEFAULT '',
  `grade` varchar(20) NOT NULL DEFAULT '',
  `photo` varchar(200) NOT NULL DEFAULT '',
  `sex` tinyint(4) NOT NULL DEFAULT '0',
  `officeNO` varchar(14) NOT NULL DEFAULT '',
  `mobileNO` varchar(14) NOT NULL DEFAULT '',
  `num_login` int(11) NOT NULL DEFAULT '0',
  `d_regis` varchar(14) NOT NULL DEFAULT '',
  `tokenID` varchar(200) DEFAULT NULL,
  `phoneID` varchar(60) DEFAULT NULL,
  PRIMARY KEY (`memberuid`),
  KEY `site` (`site`),
  KEY `auth` (`auth`),
  KEY `level` (`level`),
  KEY `admin` (`admin`),
  KEY `email` (`email`),
  KEY `name` (`name`),
  KEY `nic` (`nic`),
  KEY `sex` (`sex`),
  KEY `d_regis` (`d_regis`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8; 



=== loginChk.php ===

<?php
if(!isset($_SESSION)) {
    session_start();
}

@extract($_POST); // POST 전송으로 전달받은 값 처리
if(isset($loginID) && !empty($loginID) && isset($loginPW) && !empty($loginPW)) {

    $deviceID = $deviceID ? $deviceID : '';

    if(empty($deviceID)){
        require_once 'dbconnect.php'; // db접속
        require_once 'phpclass/loginClass.php';

        $c=new LoginClass();

        $row = $c->WebUserAuthCheck($loginID,$loginPW);
        if(is_array($row)) {
            if($row['code'] > 0) {
                $_SESSION['userID'] = $row['id'];
                $_SESSION['userPW'] = md5($loginPW);
                $_SESSION['code'] = $row['code'];
                $_SESSION['ip'] = $_SERVER['REMOTE_ADDR'];
                $_SESSION['ua'] = $_SERVER['HTTP_USER_AGENT'];

                echo("<meta http-equiv='Refresh' content='0; URL=m/list.php'>");
            } else {
                echo '권한 불가';
            }
        } else if($row == '0'){
            $msg ='정보가 올바르지 않습니다';
            echo "<script>alert('".$msg."');history.go(-1);</script>";
        } else {
            $msg ='정보가 올바르지 않습니다';
            echo "<script>alert('".$msg."');history.go(-1);</script>";
        }

    } else {
        require_once 'db.info.php';
        require_once 'phpclass/dbClass.php';
        $conn=new MySQLiDbClass();
        $dbconn = $conn->isConnectDb($DB); // 안드로이드폰에서는 반드시 객체로 생성해야 정상접속
        require_once 'phpclass/loginClass.php';
        $c=new LoginClass();

        $result = $c->MobileUserAuthCheck($loginID,$loginPW,$deviceID);
        if($result > 0 ) {
            session_save_path('./_tmp/session');

            $_SESSION['userID'] = $loginID;
            $_SESSION['userPW'] = md5($loginPW);
            $_SESSION['ip'] = $_SERVER['REMOTE_ADDR'];
            $_SESSION['ua'] = $_SERVER['HTTP_USER_AGENT'];

            // 토큰 등록 및 갱신
            if(isset($tokenID) && strlen($tokenID) > 63){
                $c->registerToken($tokenID,$loginID,$deviceID); // DB에 토큰 저장
                //$myfile = fopen("tokenfile.txt", "a") or die("파일 쓰기를 할 수 없습니다.");
                //fwrite($myfile, $tokenID);
                //fclose($myfile);
            }
            echo 1;
        } else if($result == 0) {
            echo 0; // 로그인 정보 틀림
        } else {
            echo '-1'; // Phone mismatch
        }
    }

} else {
    echo("<meta http-equiv='Refresh' content='0; URL=loginForm.php'>");
}
?>
 

 // 토큰 등록 및 갱신(2017-03-24) 함수 만들기 (LoginClass.php 파일내에 존재)
    function registerToken($token,$userID,$deviceID){
        global $dbconn;
        $rs = $this->getDeviceChk($userID,$deviceID);
        if($rs > 0 ){
            $gettokenID = $this->getTokenIDChk($rs); // 0 또는 토큰 반환
            if(($gettokenID == '0') || ($gettokenID !== $token)){ // 등록되어 있지 않으면 등록
                $sql ="UPDATE member_data SET tokenID='".$token."'";
                $sql.=" where memberuid='".$rs."'";
                if($result = mysqli_query($dbconn,$sql)){
                    return 1;
                } else {
                    return 0;
                }
            }
        } else { // 장치 ID 와 다르거나 없으면
            // 로그인 금지
        }
    }

    function getTokenIDChk($rs){
        global $dbconn;
        $sql ="select tokenID from member_data where memberuid='".$rs."'";
        if($result = mysqli_query($dbconn,$sql)){
            $row = mysqli_fetch_row($result);
            if($row[0] == NULL){
                return 0;
            } else {
                return $row[0];
            }
        } else {
            return 0;
        }
    }

    function getDeviceChk($userID,$deviceID){
        global $dbconn;
        // 사용자 기준 장치 정보 검사
        $sql ="select count(phoneID),memberuid from member_data";
        $sql.=" where memberuid=(select uid from member_id where id='".$userID."') and phoneID='".$deviceID."'";
        if($result = mysqli_query($dbconn,$sql)){
            $row = mysqli_fetch_row($result);
            if($row[0] == 1){
                return $row[1]; // 있으면 memberuid 반환
            } else {
                return $row[0];
            }
        } else {
            return 0;
        }
    }


토큰을 회원 DB에 저장하는 코드 구현로직이 어떻게 되는지만 참고하면 된다고 본다.


FCM 을 이용한 PUSH 메시지 전송을 성공하면 다시 기록해 두련다.

블로그 이미지

Link2Me

,
728x90

android.telephone.SmsManager class를 이용하여, API 코드 2줄로 SMS를 자동으로 보낼 수 있다.


SMS 메시지 보내는 코드

SmsManager smsManager = SmsManager.getDefault(); // Get the default instance of SmsManager

smsManager.sendTextMessage(phoneNumber, null, smsBody, null, null); // Send a text based SMS


MMS 메시지 보내는 코드

SmsManager sms = SmsManager.getDefault();
ArrayList<String> parts = sms.divideMessage(message);
sms.sendMultipartTextMessage(phoneNumber, null, parts, null, null); 


참고 : http://codetheory.in/android-sms/ 에 설명이 잘 나와있다.

private void SendSMS(String phonenumber, String message) {
    SmsManager smsManager = SmsManager.getDefault();
    if(smsManager == null) {
    return;
    }
    String sendTo = phonenumber;
    ArrayList<string> partMessage = smsManager.divideMessage(message);
    if(partMessage.size() > 1){
        Log.d("SMS", "Sending " + partMessage.size() + " parts");
    smsManager.sendMultipartTextMessage(sendTo, null, partMessage, null, null);
    } else {
    smsManager.sendTextMessage(sendTo, null, message, null, null);
    }


phoneNumber 를 다중(여러전화번호)으로 보낼 수 있는지 확인해보니 1건(한 전화번호)씩 보낸다.

SMS를 분할해서 보내는 코드에 대한 예제코드가 위 사이트에 잘 나와 있다.

이 방식을 이용하면 발송건수만큼 문자 보낸갯수가 생성된다.

문자를 보낸 개수를 지우는 방법을 강구해봐야겠다.


25개씩 잘라서 보내는 방법을 찾아보려고 코드를 구현한 건데 전혀 의미가 없는거 같다.

그냥 for문을 처음부터 끝까지 돌리면 될 일인듯.....


import java.util.Arrays;
import android.annotation.SuppressLint;
import android.app.Activity;
import android.app.PendingIntent;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.net.Uri;
import android.os.Bundle;
import android.telephony.SmsManager;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.Toast;

public class SendSMS extends Activity {

    Context mContext;
    EditText smsTextContext;
    String[] telNumArr;
    int mobileCnt;
    int selectedCnt;
    int limitCnt;
    int mod;
 
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.send_sms);
       
        mContext = this;
        Intent intent = getIntent(); // 값을 받아온다.
        telNumArr = intent.getStringArrayExtra("mobileNOArr");
       
        smsTextContext = (EditText) findViewById(R.id.smsText);
        selectedCnt = telNumArr.length;
        limitCnt = 25;
        mod = (selectedCnt%limitCnt==0)?selectedCnt/limitCnt:selectedCnt/limitCnt+1;
       
    }   
   
    @SuppressLint("NewApi")
    public void sendSMS(View v){
        String smsText = smsTextContext.getText().toString();
       
        if (smsText.length()>0){
            String sumTelNum = "";
            for(int i=0;i < mod;i++){
                int startIndex = i*limitCnt;
                int endIndex = startIndex + limitCnt <= telNumArr.length ? startIndex + limitCnt : telNumArr.length;
                String[] subArray = Arrays.copyOfRange(telNumArr, startIndex, endIndex);
               System.out.println(i + "번째 서브배열"); 
                for (String telNum : subArray) {
                    System.out.println("telNum" + telNum);

                    telNum = telNum.replaceAll("[^0-9]", ""); // 숫자를 제외한 모든 문자 제거

                    if(telNum.length() == 0) continue;       
                    if(telNum.matches("(01[016789]{1})(\\d{3,4})(\\d{4})")){
                        telNum = telNum.replaceAll("(\\d{3})(\\d{3,4})(\\d{4})", "$1-$2-$3");
                    }
                    sumTelNum += telNum + ";";
                }
                   
                sendSMS(sumTelNum, smsText);               
               
                System.out.println("sumTelNum....." + sumTelNum);
                subArray = null;
                sumTelNum = "";
            }
        } else {
            Toast.makeText(this, "모두 입력해 주세요", Toast.LENGTH_SHORT).show();
        }
    }
   
    public void sendSMS(String smsNumber, String smsText){
        PendingIntent sentIntent = PendingIntent.getBroadcast(this, 0, new Intent("SMS_SENT_ACTION"), 0);
        PendingIntent deliveredIntent = PendingIntent.getBroadcast(this, 0, new Intent("SMS_DELIVERED_ACTION"), 0);
       
        // SMS가 발송될때 실행
        registerReceiver(new BroadcastReceiver() {
            @Override
            public void onReceive(Context context, Intent intent) {
                switch(getResultCode()){
                    case Activity.RESULT_OK:
                        // 전송 성공
                        Toast.makeText(mContext, "전송 완료", Toast.LENGTH_SHORT).show();
                        break;
                    case SmsManager.RESULT_ERROR_GENERIC_FAILURE:
                        // 전송 실패
                        Toast.makeText(mContext, "전송 실패", Toast.LENGTH_SHORT).show();
                        break;
                    case SmsManager.RESULT_ERROR_NO_SERVICE:
                        // 서비스 지역 아님
                        Toast.makeText(mContext, "서비스 지역이 아닙니다", Toast.LENGTH_SHORT).show();
                        break;
                    case SmsManager.RESULT_ERROR_RADIO_OFF:
                        // 무선 꺼짐
                        Toast.makeText(mContext, "휴대폰이 꺼져있습니다", Toast.LENGTH_SHORT).show();
                        break;
                    case SmsManager.RESULT_ERROR_NULL_PDU:
                        // PDU 실패
                        Toast.makeText(mContext, "PDU Null", Toast.LENGTH_SHORT).show();
                        break;
                }
            }
        }, new IntentFilter("SMS_SENT_ACTION"));
       
        // SMS가 도착했을때 실행
        registerReceiver(new BroadcastReceiver() {
            @Override
            public void onReceive(Context context, Intent intent) {
                switch (getResultCode()){
                    case Activity.RESULT_OK:
                        // 도착 완료
                        Toast.makeText(mContext, "SMS 도착 완료", Toast.LENGTH_SHORT).show();
                        //finish();
                        break;
                    case Activity.RESULT_CANCELED:
                        // 도착 안됨
                        Toast.makeText(mContext, "SMS 도착 실패", Toast.LENGTH_SHORT).show();
                        break;
                }
            }
        }, new IntentFilter("SMS_DELIVERED_ACTION"));
       
        SmsManager mSmsManager = SmsManager.getDefault();
        mSmsManager.sendTextMessage(smsNumber, null, smsText, sentIntent, deliveredIntent);
    }
}


----------------------------------------------------------------------

MMS 발송코드를  http://stackoverflow.com/questions/6580675/how-to-send-the-sms-more-than-160-character 에서 찾았는데 아직 테스트는 못해봤다. 테스트를 하면 글 내용을 다시 수정하련다.


SmsManager sm = SmsManager.getDefault();
ArrayList<String> parts =sm.divideMessage(LONG_TEXT);
int numParts = parts.size();

ArrayList<PendingIntent> sentIntents = new ArrayList<PendingIntent>();
ArrayList<PendingIntent> deliveryIntents = new ArrayList<PendingIntent>();

for (int i = 0; i < numParts; i++) {
sentIntents.add(PendingIntent.getBroadcast(getContext(), 0, mSendIntent, 0));
deliveryIntents.add(PendingIntent.getBroadcast(getContext(), 0, mDeliveryIntent, 0));
}

sm.sendMultiPartTextMessage(mDestAddr,null, parts, sentIntents, deliveryIntents)

블로그 이미지

Link2Me

,
728x90

Android 어플을 실행하면 문자를 보낼 화면을 제공해준 방법이다.


이런 방식으로 SMS를 발송하는 것은 ...

전화번호를 여러개 등록하는 것은 통신사에게 제공하는 한도만큼 가능하다.

화면이 보이는 상태에서 보낼 메시지를 적어서 보내기 때문에 많은 사용자에게 문자를 발송하는 것은 어렵다.


Uri smsUri = Uri.parse("tel:" + sumTelNum);
Intent intent = new Intent(Intent.ACTION_VIEW, smsUri);
// 보내는 화면이 팝업됨

intent.putExtra("address", sumTelNum); // 받는 번호
intent.putExtra("sms_body", smsText); // 보낼 문자내용
intent.setType("vnd.android-dir/mms-sms");
startActivity(intent);


Intent intent = new Intent( Intent.ACTION_SENDTO );
intent.putExtra("sms_body", smsText);
intent.setData( Uri.parse( "smsto:"+sumTelNum ) );
startActivity(intent);


둘다 같은 화면이 나온다.


참고 : http://gogorchg.tistory.com/entry/Android%ED%8E%8C%EC%95%88%EB%93%9C%EB%A1%9C%EC%9D%B4%EB%93%9C-Intent-%EC%82%AC%EC%9A%A9%EB%B2%95

에 여러가지 설명이 잘 나와있다.

블로그 이미지

Link2Me

,
728x90

필요한 코드를 Part 단위로 기록하기 위해 작성한다.


MySQL DB 테이블에서 전화번호를 전부 -가 들어간 것을 변경했다.

Update Person SET mobile=replace(mobile,'-','');


안드로이드 코드

public String filterPhoneNO(String number){
    final Pattern PAT_COUNTRY_CODE_KOREA = Pattern.compile("^(\\+|\\-)?82\\-?");
    number = number.replaceAll("-", ""); // - 전부 제거
    if(number == null || number.length() == 0) {
        return null;
    }
    if (!(number = PAT_COUNTRY_CODE_KOREA.matcher(number).replaceFirst("")).startsWith("0"))
    {
        number = '0' + number;
    }
    if(number.matches("(01[016789]{1})(\\d{3,4})(\\d{4})")){
        return number.replaceAll("(\\d{3})(\\d{3,4})(\\d{4})", "$1-$2-$3");
    } else if(number.matches("(02)(\\d{3,4})(\\d{4})")){
        return number.replaceAll("(\\d{2})(\\d{3,4})(\\d{4})", "$1-$2-$3");
    } else {
        return number.replaceAll("(\\d{3})(\\d{3,4})(\\d{4})", "$1-$2-$3");
    }
}


서버에서 읽어온 정보를 메모리에 저장

protected void showList() {
    // 서버에서 읽어온 정보를 mAdapter 에 저장하고 화면에 출력
    try { 
        JSONObject jsonObj = new JSONObject(myJSON); 
        peoples = jsonObj.getJSONArray(TAG_RESULTS);
       
        for(int i=0;i<peoples.length();i++){ 
        JSONObject c = peoples.getJSONObject(i); 
        String uid = c.getString(TAG_UID); 
        String name = c.getString(TAG_NAME); 
        String mobile = filterPhoneNO(c.getString(TAG_Mobile));
        Drawable myIcon = getResources().getDrawable( R.drawable.ic_launcher );
       
        mAdapter.addItem(myIcon,uid,name,mobile);
        }
       
        runOnUiThread(new Runnable() {
        @Override
        public void run() {
            mAdapter.notifyDataSetChanged();
        }
        });
       
    } catch (JSONException e) { 
        e.printStackTrace(); 
    }




블로그 이미지

Link2Me

,
728x90

책, 블로그를 탐독하고 실행해보면서 보니까 ListView 를 하나의 Activity 단위로 처리하는 것이 편하고 좋은가 보다.


eclipse 에서 자동으로 class를 생성하면 java 파일 단위로 생성된다.

이렇게 생성된 것은 파일이 여러개로 나뉘어져 있어서 가독성 면에서 좀 떨어지는거 같기도 해서 하나의 파일 단위로 생성한 것으로 수정해보기로 했다.


MainActivity.java 파일을 보면 아래 3개의 함수(자바에서는 메소드라고 칭함) 단위로 보이도록 만들어져 있다.


여기서 하단 2개는 지운다.



이제 필요한 함수를 추가한다.



이런 과정으로 만들어지는 거 같다.


서버에 있는 데이터를 가져와서 처리하는 부분에서 계속 개념이 잡히지 않아서 혼란스러웠다.


서버에 있는 정보를 읽어오는 방법은 JSON 연동과 XML 연동으로 처리한다.


MySQLi 접속방식으로 하는데 화면에 아무것도 나오지 않는다. ㅠㅠㅠ

이거 해결하느라고 5시간 소비했다.


알고봤더니 호스팅업체에서 APM 소스 설치해줄때 UTF8 로 기본세팅을 해야 하는데 EUC-KR로 세팅을 해서 변경하는 작업을 해도 UTF8로 인식이 잘 안되어서 생기는 증상이었다.


한글파일 깨짐증상 방지를 위한 MySQL 세팅 참조글 http://link2me.tistory.com/1029


MySQLi 접속하는 함수에서 고려해야 할 사항

class MySQLiDbClass {
    function isConnectDb($db)
    {
        $conn = mysqli_connect($db['host'],$db['user'],$db['pass'],$db['name'],$db['port']);
        mysqli_set_charset($conn, "utf8"); // DB설정이 잘못되어 euc-kr 로 되어 있으면 문제가 됨
        if (mysqli_connect_errno()) {
           printf("Connect failed: %s\n", mysqli_connect_error());
           exit();
        } else {
          return $conn;   
        }
    }
}


JSON 은 UTF8만 인식한다고 한다. 만약 한글이 EUC-KR로 되어 있으면 화면에 아무것도 반환을 하지 않는다.


첨부파일은 테스트에 사용된 파일입니다.

실행해보시고 도움되시면 공감 꾸욱 눌러주세요


MakeActivity.zip


블로그 이미지

Link2Me

,
728x90

2초 이내에 뒤로 가기 버튼은 두번 눌러서 어플을 종료하는 코드다.

나중에 요긴하게 사용할 거 같아서 적어둔다.


====== BackPressCloseHandler.java =======

import android.app.Activity;
import android.widget.Toast;

public class BackPressCloseHandler {
    private long backKeyPressedTime = 0;
    private Toast toast;

    private Activity activity;

    public BackPressCloseHandler(Activity context) {
        this.activity = context;
    }

    public void onBackPressed() {
        // 2초 이내에 뒤로 가기 버튼을 두번 누르면 종료처리
        if (System.currentTimeMillis() > backKeyPressedTime + 2000) {
            backKeyPressedTime = System.currentTimeMillis();
            showGuide();
            return;
        }
        if (System.currentTimeMillis() <= backKeyPressedTime + 2000) {
            activity.finish();
            toast.cancel();
        }
    }

    public void showGuide() {
        toast = Toast.makeText(activity,"\'뒤로\'버튼을 한번 더 누르시면 종료됩니다.", Toast.LENGTH_SHORT);
        toast.show();
    }
}



====== MainActivity.java 에 추가할 내용 ============

네트워크 연결 상태 확인 및 뒤로가기 관련 코드 추가


public class MainActivity extends Activity {
   
    private BackPressCloseHandler backPressCloseHandler;
   
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        requestWindowFeature(Window.FEATURE_NO_TITLE); // 타이틀바 없애기

        setContentView(R.layout.activity_main);

        if( !isNetworkConnected(this) ){
            new AlertDialog.Builder(this)
            .setIcon(android.R.drawable.ic_dialog_alert)
            .setTitle("네트워크 연결 오류").setMessage("네트워크 연결 상태 확인 후 다시 시도해 주십시요.")
            .setPositiveButton("확인", new DialogInterface.OnClickListener()
            {
                @Override
                public void onClick( DialogInterface dialog, int which )
                {
                    finish();
                }
            }).show();
        }
       
        backPressCloseHandler = new BackPressCloseHandler(this);
    }

    // 어플 실행시 연결상태 확인하기
    public boolean isNetworkConnected(Context context){
        boolean isConnected = false;

        ConnectivityManager manager = (ConnectivityManager)context.getSystemService(Context.CONNECTIVITY_SERVICE);
        NetworkInfo mobile = manager.getNetworkInfo(ConnectivityManager.TYPE_MOBILE);
        NetworkInfo wifi = manager.getNetworkInfo(ConnectivityManager.TYPE_WIFI);

        if (mobile.isConnected() || wifi.isConnected()){
            isConnected = true;
        } else {
            isConnected = false;
        }
        return isConnected;
    }
   
    @Override
    public void onBackPressed() {
        //super.onBackPressed();
        backPressCloseHandler.onBackPressed();
    }
}


onBackPressed() 함수 대신에 아래 코드를 넣으면 백버튼을 누르면 종료할 것인지 여부를 물어본다.

    @Override
    public boolean onKeyDown(int keyCode, KeyEvent event) {
        if( keyCode == KeyEvent.KEYCODE_BACK ) {
            new AlertDialog.Builder(this).setIcon(android.R.drawable.ic_dialog_alert).setTitle("Quit").setMessage("어플을 종료하시겠습니까?").setPositiveButton("Yes", new DialogInterface.OnClickListener() {
                @Override
                public void onClick( DialogInterface dialog, int which) {
                    finish();
                }
        }).setNegativeButton( "No", null ).show();
     
        return true;
      }
       
      return super.onKeyDown(keyCode, event);
     }
   


// 네트워크 타입 확인
private String getNetworkTypeInfo(){
    ConnectivityManager connectivityManager = (ConnectivityManager)getSystemService(Context.CONNECTIVITY_SERVICE);
    NetworkInfo networkInfo = connectivityManager.getActiveNetworkInfo();
    return networkInfo.getTypeName();
}

Toast.makeText(this, getNetworkTypeInfo()+" 네트워크에 연결되었습니다",Toast.LENGTH_LONG).show();


블로그 이미지

Link2Me

,