필자는 phonegap2.4를 기준을 설명합니다.


프로젝트를 생성후 디바이스에서 실행시키면 아래 사진과 같이 에러가 발생한다.



이를 아래 사진을 보면서 수정해 봅시다.



위 사진과 같이Any IOS SDK가 armv7으로 설정돼 있을텐데 위와 같이 armv7과 armv7s를 선택하면 디바이스에서도


잘 실행이 될것입니다.


https://github.com/phonegap/phonegap-plugins


왠만한 phonegap plugin은 다 여기있다고 보시면 됩니다.

phonegap plugin을 사용하면 다양한 네이티브 기능을 가능하게 해준다.


이 플러그인을 직접 만들어보자.


일단 폰갭프로젝트를 생성한다.


http://squll1.tistory.com/entry/phonegap-cordovaios-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-%EC%83%9D%EC%84%B1


우선 알아두어야 할 것은 폰갭 플러그인을 만들기 위해서는 CDVPlugin을 상속받아야만 한다.


이제 간단하게 AlertView를 호출하는 플러그인을 만들어 보겠다.



1. 첫번째로 네이티브 기능을 만들어 보겠습니다.


Plugins폴더에 파일을 만듭니다.



Cocoa Touch에 Objective-C class를 선택하고 next를 누릅니다.



class이름을 입력하고 subclass는 CDVPlugin으로 합니다.


next를 누르고 create버튼을 눌러 생성합니다.


~~.h 파일에 에러가 나는데 다음과 같이 수정합니다.




그리고 네이티브 기능을 작성하시면 됩니다.





2. 두번째로 config.xml파일을 열어 방금 작성한 플러그인을 추가 해야 합니다.



name에는 이 플러그인을 사용할 이름과 value는 생성한 클래스의 이름을 입력합니다.





3. 세번째로 자바스크립트에서 플러그인을 호출합니다.


cordova.exec(<successfunction>, <failfunction>, <service>, <action>, [<args>]);


cordova.exec(function(){console.log('success')},function(err){console.log('err')},"AlertView","customAlertView",["플러그인 테스트"]);




이렇게 추가하고 실행 시켜면 다음과 같이 실행된다.



<success function>도 잘 실행된걸 확인할 수 있습니다.



cordova.exec(function(){console.log('success')},function(err){console.log('err')},"AlertView","customAlertView",["플러그인 테스트"]);


이 부분을 다시 살펴보겠습니다.


우선 세번째  "AlertView"는 config.xml에서 플러그인을 추가 했을때 name의 값을 넣어줍니다. 


그리고 네번째 "customAlertView"는 생성한 클래스에서 사용할 메소드명을 넣어줍니다.


다섯번째는 네이티브쪽으로 전달해줄 정보를 넣어줍니다.


위에서 설정한 정보가 전달되고 함수가 실행되어 네이티브 기능이 실행됩니다.


그리고 이렇게 네이티브 기능이 실행된후 잘 실행됐는지 안됐는지 기타 정보들을 담아서 다시 자바 스크립스 상으로 정보를 전달합니다.


코드를 다시 살펴보면



위 코드의 pluginResult와 resultJS를 통해 플러그인이 잘 실행됐는지 잘못실행됐는지 정보를 담아


자바스크립트 쪽으로 정보를 전달합니다.


cordova.exec(function(){console.log('success')},function(err){console.log('err')},"AlertView","customAlertView",["플러그인 테스트"]);


에서 첫번째나 두번째 함수가 실행됩니다.


이 자바스크립트로 전달할때 다른 정보를 담아서 전달 할수도 있습니다.


문자열을 전달할수도 있고 배열에 정보를 담아 전달 할 수 있습니다.


다음과 같이 정보를 담아 전달해 보자 



다음과 같이 정보가 담기는걸 확인 할 수 있다.



이 콜백되는 정보를 받아 적절히 사용해 개발할 경우도 생긴다.


첫번째 <successfunction>에서 정보를 받아온다.


아래와 같이 정보를 받아와 콘솔창에 출력해 본다.



정보를 잘 받아오는걸 확인할 수 있다.



현재 이 글을 작성하는 시점에서 phonegap plugin인 childbrowser를 android에 적용시키면 작동되지 않는다.


플러그인이 이전 1.x대 버전으로 업로드 돼 있어 정상적으로 실행이 안된다.


단순히 페이지만 보여주는 것이라면 


2.3 이상부터는 InAppBrowser가 생겨서 childbrowser를 사용할 필요가 없지만


childbrowser를 통한 oauth인증을 위해서라면 아직 childbrowser가 필요하다.


ChildBrowser.java파일을 다음과 같이 수정하자


/*

 * PhoneGap is available under *either* the terms of the modified BSD license *or* the

 * MIT License (2008). See http://opensource.org/licenses/alphabetical for full text.

 *

 * Copyright (c) 2005-2011, Nitobi Software Inc.

 * Copyright (c) 2010-2011, IBM Corporation

 */

package com.phonegap.plugins.childBrowser;


import java.io.IOException;

import java.io.InputStream;


import org.apache.cordova.api.CordovaPlugin; //수정

import org.apache.cordova.api.PluginResult;

import org.apache.cordova.api.CallbackContext; //추가

import org.json.JSONArray;

import org.json.JSONException;

import org.json.JSONObject;


import android.app.Dialog;

import android.content.Context;

import android.content.DialogInterface;

import android.content.Intent;

import android.graphics.Bitmap;

import android.graphics.BitmapFactory;

import android.net.Uri;

import android.text.InputType;

import android.util.Log;

import android.util.TypedValue;

import android.view.Gravity;

import android.view.KeyEvent;

import android.view.View;

import android.view.Window;

import android.view.WindowManager;

import android.view.WindowManager.LayoutParams;

import android.view.inputmethod.EditorInfo;

import android.view.inputmethod.InputMethodManager;

import android.webkit.WebChromeClient;

import android.webkit.WebSettings;

import android.webkit.WebView;

import android.webkit.WebViewClient;

import android.widget.EditText;

import android.widget.ImageButton;

import android.widget.LinearLayout;

import android.widget.RelativeLayout;


public class ChildBrowser extends CordovaPlugin { //수정


    protected static final String LOG_TAG = "ChildBrowser";

    private static int CLOSE_EVENT = 0;

    private static int LOCATION_CHANGED_EVENT = 1;


//    private String browserCallbackId = null; //삭제

    private CallbackContext context; //추가


    private Dialog dialog;

    private WebView webview;

    private EditText edittext;

    private boolean showLocationBar = true;


    /**

     * Executes the request and returns PluginResult.

     *

     * @param action        The action to execute.

     * @param args          JSONArry of arguments for the plugin.

     * @param callbackId    The callback id used when calling back into JavaScript.

     * @return              A PluginResult object with a status and message.

     */

//    public PluginResult execute(String action, JSONArray args, String callbackId) { //삭제

    @Override

    public boolean execute(String action, JSONArray args, CallbackContext callbackContext) throws JSONException{  //추가

        PluginResult.Status status = PluginResult.Status.OK;

        this.context=callbackContext;//추가

        String result = "";


        try {

            if (action.equals("showWebPage")) {

//                this.browserCallbackId = callbackId;  //삭제


                // If the ChildBrowser is already open then throw an error

                if (dialog != null && dialog.isShowing()) {

//                    return new PluginResult(PluginResult.Status.ERROR, "ChildBrowser is already open");  //삭제

                this.context.sendPluginResult(new PluginResult(PluginResult.Status.ERROR, "ChlildBrowser is already open"));  //추가

                return true; //추가

                }


                result = this.showWebPage(args.getString(0), args.optJSONObject(1));


                if (result.length() > 0) {

                    status = PluginResult.Status.ERROR;

//                    return new PluginResult(status, result); //삭제

                    this.context.sendPluginResult(new PluginResult(status, result));  //추가

                } else {

                    PluginResult pluginResult = new PluginResult(status, result);

                    pluginResult.setKeepCallback(true);

//                    return pluginResult;  //삭제

                    this.context.sendPluginResult(pluginResult);  //추가

                    return true;  //추가

                }

            }

            else if (action.equals("close")) {

                closeDialog();


                JSONObject obj = new JSONObject();

                obj.put("type", CLOSE_EVENT);


                PluginResult pluginResult = new PluginResult(status, obj);

                pluginResult.setKeepCallback(false);

//                return pluginResult;  //삭제

                this.context.sendPluginResult(pluginResult);  //추가

                return true;  //추가

            }

            else if (action.equals("openExternal")) {

                result = this.openExternal(args.getString(0), args.optBoolean(1));

                if (result.length() > 0) {

                    status = PluginResult.Status.ERROR;

                }

            }

            else {

                status = PluginResult.Status.INVALID_ACTION;

            }

//            return new PluginResult(status, result);  //삭제

            this.context.sendPluginResult(new PluginResult(status, result));  //추가

            return true;  //추가

        } catch (JSONException e) {

//            return new PluginResult(PluginResult.Status.JSON_EXCEPTION);  //삭제

        this.context.sendPluginResult(new PluginResult(PluginResult.Status.JSON_EXCEPTION));  //추가

        return true;  //추가

        }

    }


    /**

     * Display a new browser with the specified URL.

     *

     * @param url           The url to load.

     * @param usePhoneGap   Load url in PhoneGap webview

     * @return              "" if ok, or error message.

     */

    public String openExternal(String url, boolean usePhoneGap) {

        try {

            Intent intent = null;

            if (usePhoneGap) {

                intent = new Intent().setClass(this.cordova.getActivity(), org.apache.cordova.DroidGap.class);

                intent.setData(Uri.parse(url)); // This line will be removed in future.

                intent.putExtra("url", url);


                // Timeout parameter: 60 sec max - May be less if http device timeout is less.

                intent.putExtra("loadUrlTimeoutValue", 60000);


                // These parameters can be configured if you want to show the loading dialog

                intent.putExtra("loadingDialog", "Wait,Loading web page...");   // show loading dialog

                intent.putExtra("hideLoadingDialogOnPageLoad", true);           // hide it once page has completely loaded

            }

            else {

                intent = new Intent(Intent.ACTION_VIEW);

                intent.setData(Uri.parse(url));

            }

            this.cordova.getActivity().startActivity(intent);

            return "";

        } catch (android.content.ActivityNotFoundException e) {

            Log.d(LOG_TAG, "ChildBrowser: Error loading url "+url+":"+ e.toString());

            return e.toString();

        }

    }


    /**

     * Closes the dialog

     */

    private void closeDialog() {

        if (dialog != null) {

            dialog.dismiss();

        }

    }


    /**

     * Checks to see if it is possible to go back one page in history, then does so.

     */

    private void goBack() {

        if (this.webview.canGoBack()) {

            this.webview.goBack();

        }

    }


    /**

     * Checks to see if it is possible to go forward one page in history, then does so.

     */

    private void goForward() {

        if (this.webview.canGoForward()) {

            this.webview.goForward();

        }

    }


    /**

     * Navigate to the new page

     *

     * @param url to load

     */

    private void navigate(String url) {

        InputMethodManager imm = (InputMethodManager)this.cordova.getActivity().getSystemService(Context.INPUT_METHOD_SERVICE);

        imm.hideSoftInputFromWindow(edittext.getWindowToken(), 0);


        if (!url.startsWith("http") && !url.startsWith("file:")) {

            this.webview.loadUrl("http://" + url);

        } else {

            this.webview.loadUrl(url);

        }

        this.webview.requestFocus();

    }



    /**

     * Should we show the location bar?

     *

     * @return boolean

     */

    private boolean getShowLocationBar() {

        return this.showLocationBar;

    }


    /**

     * Display a new browser with the specified URL.

     *

     * @param url           The url to load.

     * @param jsonObject

     */

    public String showWebPage(final String url, JSONObject options) {

        // Determine if we should hide the location bar.

        if (options != null) {

            showLocationBar = options.optBoolean("showLocationBar", true);

        }


        // Create dialog in new thread

        Runnable runnable = new Runnable() {

            /**

             * Convert our DIP units to Pixels

             *

             * @return int

             */

            private int dpToPixels(int dipValue) {

                int value = (int) TypedValue.applyDimension( TypedValue.COMPLEX_UNIT_DIP,

                                                            (float) dipValue,

                                                            cordova.getActivity().getResources().getDisplayMetrics()

                );


                return value;

            }


            public void run() {

                // Let's create the main dialog

                dialog = new Dialog(cordova.getActivity(), android.R.style.Theme_NoTitleBar);

                dialog.getWindow().getAttributes().windowAnimations = android.R.style.Animation_Dialog;

                dialog.requestWindowFeature(Window.FEATURE_NO_TITLE);

                dialog.setCancelable(true);

                dialog.setOnDismissListener(new DialogInterface.OnDismissListener() {

                        public void onDismiss(DialogInterface dialog) {

                            try {

                                JSONObject obj = new JSONObject();

                                obj.put("type", CLOSE_EVENT);


                                sendUpdate(obj, false);

                            } catch (JSONException e) {

                                Log.d(LOG_TAG, "Should never happen");

                            }

                        }

                });


                // Main container layout

                LinearLayout main = new LinearLayout(cordova.getActivity());

                main.setOrientation(LinearLayout.VERTICAL);


                // Toolbar layout

                RelativeLayout toolbar = new RelativeLayout(cordova.getActivity());

                toolbar.setLayoutParams(new RelativeLayout.LayoutParams(LayoutParams.FILL_PARENT, this.dpToPixels(44)));

                toolbar.setPadding(this.dpToPixels(2), this.dpToPixels(2), this.dpToPixels(2), this.dpToPixels(2));

                toolbar.setHorizontalGravity(Gravity.LEFT);

                toolbar.setVerticalGravity(Gravity.TOP);


                // Action Button Container layout

                RelativeLayout actionButtonContainer = new RelativeLayout(cordova.getActivity());

                actionButtonContainer.setLayoutParams(new RelativeLayout.LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT));

                actionButtonContainer.setHorizontalGravity(Gravity.LEFT);

                actionButtonContainer.setVerticalGravity(Gravity.CENTER_VERTICAL);

                actionButtonContainer.setId(1);


                // Back button

                ImageButton back = new ImageButton(cordova.getActivity());

                RelativeLayout.LayoutParams backLayoutParams = new RelativeLayout.LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.FILL_PARENT);

                backLayoutParams.addRule(RelativeLayout.ALIGN_LEFT);

                back.setLayoutParams(backLayoutParams);

                back.setContentDescription("Back Button");

                back.setId(2);

                try {

                    back.setImageBitmap(loadDrawable("www/childbrowser/icon_arrow_left.png"));

                } catch (IOException e) {

                    Log.e(LOG_TAG, e.getMessage(), e);

                }

                back.setOnClickListener(new View.OnClickListener() {

                    public void onClick(View v) {

                        goBack();

                    }

                });


                // Forward button

                ImageButton forward = new ImageButton(cordova.getActivity());

                RelativeLayout.LayoutParams forwardLayoutParams = new RelativeLayout.LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.FILL_PARENT);

                forwardLayoutParams.addRule(RelativeLayout.RIGHT_OF, 2);

                forward.setLayoutParams(forwardLayoutParams);

                forward.setContentDescription("Forward Button");

                forward.setId(3);

                try {

                    forward.setImageBitmap(loadDrawable("www/childbrowser/icon_arrow_right.png"));

                } catch (IOException e) {

                    Log.e(LOG_TAG, e.getMessage(), e);

                }

                forward.setOnClickListener(new View.OnClickListener() {

                    public void onClick(View v) {

                        goForward();

                    }

                });


                // Edit Text Box

                edittext = new EditText(cordova.getActivity());

                RelativeLayout.LayoutParams textLayoutParams = new RelativeLayout.LayoutParams(LayoutParams.FILL_PARENT, LayoutParams.FILL_PARENT);

                textLayoutParams.addRule(RelativeLayout.RIGHT_OF, 1);

                textLayoutParams.addRule(RelativeLayout.LEFT_OF, 5);

                edittext.setLayoutParams(textLayoutParams);

                edittext.setId(4);

                edittext.setSingleLine(true);

                edittext.setText(url);

                edittext.setInputType(InputType.TYPE_TEXT_VARIATION_URI);

                edittext.setImeOptions(EditorInfo.IME_ACTION_GO);

                edittext.setInputType(InputType.TYPE_NULL); // Will not except input... Makes the text NON-EDITABLE

                edittext.setOnKeyListener(new View.OnKeyListener() {

                    public boolean onKey(View v, int keyCode, KeyEvent event) {

                        // If the event is a key-down event on the "enter" button

                        if ((event.getAction() == KeyEvent.ACTION_DOWN) && (keyCode == KeyEvent.KEYCODE_ENTER)) {

                          navigate(edittext.getText().toString());

                          return true;

                        }

                        return false;

                    }

                });


                // Close button

                ImageButton close = new ImageButton(cordova.getActivity());

                RelativeLayout.LayoutParams closeLayoutParams = new RelativeLayout.LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.FILL_PARENT);

                closeLayoutParams.addRule(RelativeLayout.ALIGN_PARENT_RIGHT);

                close.setLayoutParams(closeLayoutParams);

                forward.setContentDescription("Close Button");

                close.setId(5);

                try {

                    close.setImageBitmap(loadDrawable("www/childbrowser/icon_close.png"));

                } catch (IOException e) {

                    Log.e(LOG_TAG, e.getMessage(), e);

                }

                close.setOnClickListener(new View.OnClickListener() {

                    public void onClick(View v) {

                        closeDialog();

                    }

                });


                // WebView

                webview = new WebView(cordova.getActivity());

                webview.setLayoutParams(new LinearLayout.LayoutParams(LayoutParams.FILL_PARENT, LayoutParams.FILL_PARENT));

                webview.setWebChromeClient(new WebChromeClient());

                WebViewClient client = new ChildBrowserClient(edittext);

                webview.setWebViewClient(client);

                WebSettings settings = webview.getSettings();

                settings.setJavaScriptEnabled(true);

                settings.setJavaScriptCanOpenWindowsAutomatically(true);

                settings.setBuiltInZoomControls(true);

                settings.setPluginsEnabled(true);

                settings.setDomStorageEnabled(true);

                webview.loadUrl(url);

                webview.setId(6);

                webview.getSettings().setLoadWithOverviewMode(true);

                webview.getSettings().setUseWideViewPort(true);

                webview.requestFocus();

                webview.requestFocusFromTouch();


                // Add the back and forward buttons to our action button container layout

                actionButtonContainer.addView(back);

                actionButtonContainer.addView(forward);


                // Add the views to our toolbar

                toolbar.addView(actionButtonContainer);

                toolbar.addView(edittext);

                toolbar.addView(close);


                // Don't add the toolbar if its been disabled

                if (getShowLocationBar()) {

                    // Add our toolbar to our main view/layout

                    main.addView(toolbar);

                }


                // Add our webview to our main view/layout

                main.addView(webview);


                WindowManager.LayoutParams lp = new WindowManager.LayoutParams();

                lp.copyFrom(dialog.getWindow().getAttributes());

                lp.width = WindowManager.LayoutParams.FILL_PARENT;

                lp.height = WindowManager.LayoutParams.FILL_PARENT;


                dialog.setContentView(main);

                dialog.show();

                dialog.getWindow().setAttributes(lp);

            }


          private Bitmap loadDrawable(String filename) throws java.io.IOException {

              InputStream input = cordova.getActivity().getAssets().open(filename);

              return BitmapFactory.decodeStream(input);

          }

        };

        this.cordova.getActivity().runOnUiThread(runnable);

        return "";

    }


    /**

     * Create a new plugin result and send it back to JavaScript

     *

     * @param obj a JSONObject contain event payload information

     */

    private void sendUpdate(JSONObject obj, boolean keepCallback) {

//        if (this.browserCallbackId != null) {  //삭제

    if(this.context!=null){  //추가

            PluginResult result = new PluginResult(PluginResult.Status.OK, obj);

            result.setKeepCallback(keepCallback);

//            this.success(result, this.browserCallbackId);  //삭제

            this.context.sendPluginResult(result);  //추가

        }

    }


    /**

     * The webview client receives notifications about appView

     */

    public class ChildBrowserClient extends WebViewClient {

        EditText edittext;


        /**

         * Constructor.

         *

         * @param mContext

         * @param edittext

         */

        public ChildBrowserClient(EditText mEditText) {

            this.edittext = mEditText;

        }


        /**

         * Notify the host application that a page has started loading.

         *

         * @param view          The webview initiating the callback.

         * @param url           The url of the page.

         */

        @Override

        public void onPageStarted(WebView view, String url,  Bitmap favicon) {

            super.onPageStarted(view, url, favicon);

            String newloc;

            if (url.startsWith("http:") || url.startsWith("https:") || url.startsWith("file:")) {

                newloc = url;

            } else {

                newloc = "http://" + url;

            }


            if (!newloc.equals(edittext.getText().toString())) {

                edittext.setText(newloc);

            }


            try {

                JSONObject obj = new JSONObject();

                obj.put("type", LOCATION_CHANGED_EVENT);

                obj.put("location", url);


                sendUpdate(obj, true);

            } catch (JSONException e) {

                Log.d("ChildBrowser", "This should never happen");

            }

        }

    }

}




android localnotification을 사용하면


알람 후 알림창에 알림을 선택하면 앱이 실행되지 않는다.


알림창의 알림을 선택하면 앱이 실행하게 하려면


플러그인 파일중


AlarmReceiver.java 파일을 수정해야 한다.


파일을 열고


final PendingIntent contentIntent = PendingIntent.getActivity(context, 0, new Intent(), 0);  -->이부분을


final PendingIntent contentIntent = PendingIntent.getActivity(context, 0, new Intent(context, 프로젝트이름.class), 0);


다음과 같이 고치면 된다.

http://docs.phonegap.com/en/2.7.0/cordova_inappbrowser_inappbrowser.md.html#InAppBrowser

위 주소로 가면 모든 내용을 확인할 수 있다.


cordova 2.3 이상부터 플러그인인 childbrowser를 쓸 필요가 없어졌다.


phonegap에서 childbrowser를 InAppBrowser라는 이름으로 지원해 준다.


사용방법은 다음과 같다.


안드로이드는


app->res->xml->config.xml 에 다음과 같이 추가한다.


<plugin name="InAppBrowser" value="org.apache.cordova.InAppBrowser" />


ios는


config.xml에 다음과 같이 추가한다.


<plugin name="InAppBrowser" value="CDVInAppBrowser" />

그리고 javascript상에 다음과 같이 입력하면

window.open('http://apache.org', '_blank', 'location=yes');


InAppBrowser가 실행된다.

일단 이 생성 방법은 2.1버전 이상 방법입니다.


폰갭 홈페이지로 가서 phonegap을 다운 받습니다.


http://phonegap.com/


받은 파일을 압축을고 lib -> ios 이동합니다.


bin 폴더를 드레그하여 터미널 창에 넣으면 새로운 터미널창이 뜨게 됩니다.


그리고 다음과 같이 입력합니다.


./create <프로잭트가 생성될폴더> <패키지이름> <프로잭트이름> 



이렇게 입력하면 프로젝트가 자동으로 생성됩니다.



xcode로 실행 후


보통 어플리케이션처럼 화면이 고정되야 하는데 드레그하면 화면이 상하로 움직이는 현상이 발생한다


이를 막기 위해서


cordova 2.4 이상부터는 config.xml파일의


<preference name="UIWebViewBounce" value="false" />로 수정해 준다.


이전 버전은


Cordova.plist의 UIWebViewBounce를 NO로 수정한다.

+ Recent posts