WebView File Upload Over Kitkat

Android over kitkat doesn’t work WebChromeClient openFileChoose

  1. Input type file click event call android method that start file select (ex : gallery)
  2. Get Selected file path
  3. Upload file to server return file path that uploaded server
  4. Submit Form with File Upload Sucess message (ex : server uploaded path)

Call Android Method openFileChoose JavascriptInterface

JavascriptInterface.java

public class JavaScriptInterface {
    private final static String TAG = "JavaScriptInterface";
    private Handler mHandler;

    @JavascriptInterface
    public void openFileChoose() {
        Logger.debug(this, "openFileChoose");
        Message msg = new Message();
        msg.what = HandlerOwner.MSG_FILE_CHOOSE;
        mHandler.sendMessage(msg);
    }

    @JavascriptInterface
    public void uploadFileCall() {
        Logger.debug(this, "uploadFileCall");
        Message msg = new Message();
        msg.what = HandlerOwner.MSG_UPLOAD_FILE_CALL;
        mHandler.sendMessage(msg);
    }


}

Receive JavascriptInterface Message on Activity

WebActivity.java

@EActivity(R.layout.activity_web)
public class WebActivity extends BaseActivity implements View.OnClickListener, HandlerOwner, TaskObserver{

    @ViewById(R.id.webView)
    WebView webView;

    File uploadFile;

    private JavaScriptInterface jsInterface;
    private Handler mHandler;
    private String title, webViewUrl;
    private WebChromeClient webChromeClient;
    private WebClient webClient;

    @AfterViews
    void initView() {
        webClient = new WebClient(this);
        webView.setWebViewClient(webClient);
        webChromeClient = new WebChromeClient(this);
        webView.setWebChromeClient(webChromeClient);
        WebSettings set = webView.getSettings();
        set.setJavaScriptEnabled(true);
        set.setBuiltInZoomControls(true);
        set.setDomStorageEnabled(true);

        Intent intent = getIntent();
        title = intent.getStringExtra("title");
        webViewUrl = intent.getStringExtra("url");

        if(!webViewUrl.equals("")) {
            webView.loadUrl(webViewUrl);
        }
    }

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_web);
        Logger.debug(this, ON_CREATE_START);

        Logger.debug(this, ON_CREATE_END);
    }

    @Override
    protected void onResume() {
        super.onResume();
        Logger.debug(this, ON_RESUME_START);

        if(mHandler == null) {
            mHandler = new Handler(this);
        }

        jsInterface = new JavaScriptInterface(mHandler);
        webView.addJavascriptInterface(jsInterface, "Android");

        Logger.debug(this, ON_RESUME_END);
    }
    @Override
    protected void onPause() {
        super.onPause();
        Logger.debug(this, ON_PAUSE_START);

        Logger.debug(this, ON_PAUSE_END);
    }

    @Override
    public boolean onKeyDown(int keyCode, KeyEvent event) {
        Logger.debug(this, "keyCode : "+keyCode);
        switch (keyCode) {
            case KeyEvent.KEYCODE_BACK:
                if(webView.canGoBack()) {
                    webView.goBack();
                    return true;
                }
        }
        return super.onKeyDown(keyCode, event);
    }
    
    @Override
    public void onMessage(Message message) {
        Logger.debug(this, ON_MESSAGE_START);
        Logger.debug(this, "message.what : "+ message.what);
        switch (message.what) {
            case MSG_FILE_CHOOSE:
                Intent i = new Intent(Intent.ACTION_GET_CONTENT);
                i.addCategory(Intent.CATEGORY_OPENABLE);
                i.setType("image/*");
                startActivityForResult(Intent.createChooser(i, "File Chooser"), WebChromeClient.FILECHOOSER_RESULTCODE);
                break;
            case MSG_UPLOAD_FILE_CALL:
                FileUploadAsyncTask fileUploadAsyncTask = new FileUploadAsyncTask(this, null);
                fileUploadAsyncTask.execute(uploadFile.getAbsolutePath());
                break;
        }

        Logger.debug(this, ON_MESSAGE_END);
    }
    //Select File onActivityResult getFilePath
    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent intent) {
//        super.onActivityResult(requestCode, resultCode, data);

        if(requestCode== WebChromeClient.FILECHOOSER_RESULTCODE)
        {
            //if (null == webChromeClient.getmUploadMessage()) return;
            if(resultCode == RESULT_OK) {
                Uri result = intent == null || resultCode != RESULT_OK ? null : intent.getData();
                Logger.debug(this, "image Uri : " +result.toString());
                Logger.debug(this, "file name : " +uriToFile(result).getName());
                webView.loadUrl("javascript:getFileName('"+uriToFile(result).getName()+"')");
                uploadFile = uriToFile(result);
            }

        }
    }

    @TargetApi(19)
    private File uriToFile ( Uri uri ) {

        String filePath = "";
        if ( uri.getPath().contains(":") ) {
            String wholeID = DocumentsContract.getDocumentId(uri);
            // Split at colon, use second item in the array
            String id = wholeID.split(":")[1];
            String[] column = { MediaStore.Images.Media.DATA };
            // where id is equal to
            String sel = MediaStore.Images.Media._ID + "=?";
            Cursor cursor = this.getContentResolver().query(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, column, sel, new String[]{ id }, null);

            int columnIndex = cursor.getColumnIndex(column[0]);
            if (cursor.moveToFirst()) {
                filePath = cursor.getString(columnIndex);
            }
            cursor.close();

        } else {
            String id = uri.getLastPathSegment();
            final String[] imageColumns = {MediaStore.Images.Media.DATA };
            final String imageOrderBy = null;

            String selectedImagePath = "path";
            String scheme = uri.getScheme();
            if ( scheme.equalsIgnoreCase("content") ) {
                Cursor imageCursor = this.getContentResolver().query(uri, imageColumns, null, null, null);

                if (imageCursor.moveToFirst()) {
                    filePath = imageCursor.getString(imageCursor.getColumnIndex(MediaStore.Images.Media.DATA));
                }
            } else {
                filePath = uri.getPath();
            }
        }
        File file = new File( filePath );
        return file;
    }
    @Override
    public void update(Class cls, Object returnObj) {
        Logger.debug(this, TASK_OBSERVER_UPDATE_START);
        Logger.debug(this, cls.getSimpleName());
        if(cls.equals(FileUploadAsyncTask.class)) {
            FileUploadRes uploadRes = (FileUploadRes)returnObj;
            if(uploadRes != null) {
                String atchFilePth = uploadRes.getAtchFilePth();
                String atchFileMg = uploadRes.getAtchFileMg();
                Logger.debug(this, "atchFilePth : "+ atchFilePth+ " / atchFileMg : "+atchFileMg);
                // Receive AsynTaskResult Ok
                webView.loadUrl("javascript:getUploadFilePath('"+atchFilePth+"', '"+atchFileMg+"')");
            }

        }
        Logger.debug(this, TASK_OBSERVER_UPDATE_END);
    }

    public class WebClient extends WebViewClient {

        Context context;

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

        public boolean shouldOverrideUrlLoading(WebView view, String url) {
            Logger.debug(WebActivity.this, "shouldOverrideUrlLoading : "+ url);
            view.loadUrl(url);
            return true;
        }

        @Override
        public void onPageFinished(WebView view, String url) {
            super.onPageFinished(view, url);
            Logger.debug(this, "onPageFinished : "+url);
        }
    }
}

Get File Upload to Server

FileUploadAsyncTask.java

public class FileUploadAsyncTask extends BaseAsyncTask implements UrlConstant {
    private static AsyncTaskResult asyncTaskResult = null;

    public FileUploadAsyncTask(TaskObserver caller, String progressMsg) {
        super(caller, progressMsg);
    }

    @Override
    protected AsyncTaskResult doInBackground(Object... params) {
        try {
            String path = (String)params[0];
            AsyncHttpPost post = new AsyncHttpPost(HTTP_URL+CONTEXT+FILE_UPLOAD);
            MultipartFormDataBody body = new MultipartFormDataBody();
            // parameters are name, value
            body.addFilePart("atchFile", new File(path));
            post.setBody(body);

            AsyncHttpClient.getDefaultInstance().executeJSONObject(post, new AsyncHttpClient.JSONObjectCallback(){
                @Override
                public void onCompleted(Exception e, AsyncHttpResponse asyncHttpResponse, JSONObject result) {
                    if (e != null) {
                        e.printStackTrace();
                        asyncTaskResult = new AsyncTaskResult(AsyncTaskResultType.RESULT_TIMEOUT, e);
                        return;
                    }
                    if (result != null) {
                        try {
                            Logger.debug(this, "result\n" + result.toString());
                            Gson gsonResult = new Gson();
                            FileUploadRes fileUploadRes = gsonResult.fromJson(result.toString(), FileUploadRes.class);
                            asyncTaskResult = new AsyncTaskResult(AsyncTaskResultType.RESULT_SUCCESS, fileUploadRes);
                        } catch (Exception e1) {
                            e1.printStackTrace();
                            asyncTaskResult = new AsyncTaskResult(AsyncTaskResultType.RESULT_FAIL);
                        }

                    } else {
                        asyncTaskResult = new AsyncTaskResult(AsyncTaskResultType.RESULT_FAIL);
                    }
                }
            });
            while (asyncTaskResult == null) {
                Thread.sleep(100);
            }

        } catch (Exception e) {
            Logger.error(this, e.getLocalizedMessage());
            asyncTaskResult = new AsyncTaskResult(AsyncTaskResultType.RESULT_FAIL, e);
        }
        return asyncTaskResult;
    }
}

File upload Sucess call webview javascript method upload success

    // Receive AsynTaskResult Ok
    webView.loadUrl("javascript:getUploadFilePath('"+atchFilePth+"', '"+atchFileMg+"')");

Submit Form with Server UploadFilePath that after AsyncTask success

Conclusion

Android

  1. Call Image Pick Activity
  2. Get File Url
  3. File upload to server

Server

  1. input type file click call andorid javascript method that file pick
  2. fill the file path input type file
  3. Receive File upload
  4. submit with get uploaded file path

Reference
Android WebView File Upload
File Upload in WebView (Android)
[Android, Hybrid]openFileChooser 킷캣에서 동작하지 않는 문제( openFileChooser Kitkat bug )

Setup PPTP Server on CentOS

Setup PPTP VPN Server on CentOS7 with Firewalld

Setup pptp, pptpd

rpm -i http://poptop.sourceforge.net/yum/stable/rhel7/pptp-release-current.noarch.rpm
yum -y install pptpd

VPN IP config
vi /etc/pptpd.conf

localip 192.168.0.1
remoteip 192.168.0.234-238,192.168.0.245

Add VPN User
vi /etc/ppp/chap-secrets

# client        server  secret                  IP addresses
userName       pptpd   userPassword       *

Add DNS Server and disable mppe-128
vi /etc/ppp/options.pptpd

#require-mppe-128
ms-dns 8.8.8.8

Service pptpd restart

service pptpd restart

Setup forwarding

net.ipv4.ip_forward = 1

Firewalld configuration

firewall-cmd --permanent --direct --add-rule ipv4 filter INPUT 0 -i eth0 -p tcp --dport 1723 -j ACCEPT 
firewall-cmd --permanent --direct --add-rule ipv4 filter INPUT 0 -p gre -j ACCEPT 
firewall-cmd --permanent --direct --add-rule ipv4 filter POSTROUTING 0 -t nat -o eth0 -j MASQUERADE 
firewall-cmd --permanent --direct --add-rule ipv4 filter FORWARD 0 -i ppp+ -o eth0 -j ACCEPT 
firewall-cmd --permanent --direct --add-rule ipv4 filter FORWARD 0 -i eth0 -o ppp+ -j ACCEPT
firewall-cmd --reload

Add Service

systemctl start pptpd
systemctl enable pptpd.service

Reference
How To Setup Your Own VPN With PPTP
How To Install PPTP VPN On Centos 7
Centos 7 安装 pptp vpn
PPTP VPN in CentOS 7
Redhat / Centos 7 Firewalld best practice for pptp or L2TP/IPsec rules

get Url base Url + ContextPath

var url = "${pageContext.request.scheme}://${pageContext.request.serverName}:${pageContext.request.serverPort}${pageContext.request.contextPath}";

${pageContext.request.scheme} : http
${pageContext.request.serverName} : localhost
${pageContext.request.serverPort} : 8080
${pageContext.request.contextPath : contextPath

Reflect invoke getMethods toString

Using java reflect invoke getMethods toString

public String toString() {
    String s = "";
    s += "\n"+this.getClass().getName()+"\n";
    try {
        for(Method m : this.getClass().getMethods()) {
            if(m.getName().startsWith("get") && !m.getName().equals("getClass")) {
                s += m.getName().replace("get","")+"="+m.invoke(this, null)+"\n";
            }
        }
    } catch (Exception e) {
        s = e.getLocalizedMessage();
    }
    return s;
}

jQuery Selector find equal something

Using jQuery each find something

<script type="text/javascript">

var searchItem = "searchItem";
var searchValue;
$("select").find("option").each(function(n){
    if($(this).val() == searchItem) {
        searchValue = $(this).attr("data-searchvalue");
    }
});

</script>

Using jQuery selector attribute find something

<script type="text/javascript">

var searchItem = "searchItem";
var searchValue = $("select").find("option[value='"+searchItem+"']").attr("data-searchvalue");

</script>

CSS Web font

css apply web font

@font-face apply

@charset "utf-8";

@font-face {
	font-family:"moebius"; 
	src:url("../font/Moebius_Regular_kor.eot"); /* IE9 */
	src:local('☺'), /* prevent over load */
		url("../font/Moebius_Regular_kor.eot?iefix")format("embedded-opentype"), /* IE6 ~ IE8 */
		url('../font/Moebius_Regular_kor.wotf') format('woff'), /* IE9+, chrome, safari */
		url('../font/Moebius_Regular_kor.ttf') format('truetype');
		url('~../font/Moebius_Regular_kor.svg#moebius') format('svg');
	font-weight:normal;
	font-style:normal;
}

@font-face {
	font-family:"moebius_bold"; 
	src:url("../font/Moebius_Bold_kor.eot"); 
	src:local('☺'), 
		url("../font/Moebius_Bold_kor.eot?#iefix")format("embedded-opentype"),
		url('../font/Moebius_Bold_kor.wotf') format('wotf'),
		url('../font/Moebius_Bold_kor.ttf') format('truetype');
		url('../font/Moebius_~Bold_kor.svg#moebius_bold') format('svg');
	font-weight:normal;
	font-style:normal;
}

* {font-family:"moebius"; }

Google Web font

@charset "utf-8";

@import url(http://fonts.googleapis.com/earlyaccess/nanumgothic.css);

* {font-family: 'Nanum Gothic'}

Version Control Ignore

How to set property vcs ignore

target
build
bin
*~
*.log
.classpath
.project
*.ipr
*.iws
*.iml
.settings
.git
.svn
*.class
*.bak

IntelliJ

  1. project right click
  2. Subvesion
  3. Set Property
  4. Choose svn:ignore
  5. Add ignore list

Eclipse

  1. Project right click
  2. Team
  3. Set property
  4. Choose svn:ignore
  5. Add ignore list

folder/file right click add ignore

Using javascript userAgent conditional css & javascript

IE 10 버전 이상에서 부터는 IE에서 종전에 지원해주던 conditional comment를 지원하지 않는다.
그렇기 때문에 이에 대한 해결을 script를 통해서 해결을 해야한다.
javascript에서 userAgent를 통해서 이 부분을 해결을 할 수 있다.

UserAgent

자바스크립트에서 사용자의 브라우저 정보 등을 확인 할 수 있는 속성이다.
자세한 정보는 w3schools에서 확인이 가능하다.

Navigator userAgent Property