Thursday, March 21, 2013

Android - CookieSyncManager to prevent Webview lost session after Pause Resume

By adding CookieSyncManager onCreate of Activity and also on corresponding events like onPause and onResume, taking care my session ID for the PHP server when the Activity/WebView reloads.

public class MainActivity extends Activity {
    @Override
     protected void onCreate(Bundle savedInstanceState) {
           super.onCreate(savedInstanceState);
           setContentView(R.layout.activity_main);

           CookieSyncManager.createInstance(this); 
           CookieSyncManager.getInstance().startSync();

           WebView myWebView = ((WebView)findViewById(R.id.webview));
           myWebView.setWebViewClient(new WebViewClient());
    }
   @Override
    protected void onResume() {
           super.onResume();
           CookieSyncManager.getInstance().stopSync();
    }
    @Override
     protected void onPause() {
           super.onPause();
           CookieSyncManager.getInstance().sync();
    }
}


Android - prevent webview reload if screen rotate

I found there are some method to prevent this happen, may be it's all about the API level to work with.
Anyway, I'm working on Android 3.2 + and API level 13. Adding the following declaration in Manifest works prefect.


<activity android:configChanges="orientation|screenSize">
http://developer.android.com/guide/topics/resources/runtime-changes.html

Tuesday, March 19, 2013

Android - passing WebView cookie to HttpURLConnection

Since I needed to use WebView and session was created in the server side by PHP when login by WebView, cookie is required to pass into the HttpURLconnection for getting the InputStream later, which I mentioned in my previous posts, download PDF in WebView.

The code finally is not too complicated, however, it used me up almost a whole day to figure out how it works.

Before downloading the PDF, get the cookie by CookieManger as below:

private class DownloadPDF extends AsyncTask<String, Integer, String> {

 @Override
        protected String doInBackground(String... sUrl) {  
            try {
                 CookieManager cookieManager = CookieManager.getInstance();
                 String cookie = cookieManager.getCookie(new URL(sUrl[0]).getHost());
                 //or String cookie = cookieManager.getCookie("www.example.com");

                URL url = new URL(sUrl[0]);
             
                File myDir = new File(Environment.getExternalStoragePublicDirectory(
                        Environment.DIRECTORY_DOWNLOADS).toString()+"/myPDF");
             
                if (!myDir.exists()) myDir.mkdirs();          

                HttpURLConnection connection = (HttpURLConnection) url.openConnection();
             
                //Then, set cookie to the connection by setRequestProperty
                connection.setRequestProperty("Cookie",cookie);

                connection.setDoOutput(true);        

                connection.connect();
                ...
                //rest of code mentioned in previous post for downloading a PDF file which is generated by TCPDF on server side.
                ...
}

So, basically, only 3 lines of code and get this done!

Android - open PDF after WebView downloaded it.

In the previous post, I handled WebView to download of PDF file from server to an Android device. To open the PDF by creating a new instant was not too difficult.
(p.s. This is not open within the WebView, I didn't see a single successful case on the web that is able to render the PDF within WebView; however, people do the tick by using google Doc Viewer, but this is not necessary for me)  

By adding a function in the Activity as below, and this will be called right after the download is completed.


public void displayPdf(String filename)
    {
        try
        {          
            File file = new file(Environment.getExternalStoragePublicDirectory( Environment.DIRECTORY_DOWNLOADS ) + "/myPDF/" + filename);
           
            Intent intent = new Intent();
            intent.setAction(Intent.ACTION_VIEW);
            Uri uri = Uri.fromFile(file);
            intent.setDataAndType(uri, "application/pdf");
            startActivity(intent);
        }
        catch (Exception e)
        {
            Log.i("TAG",e.getMessage());
        }
    }

   

Android - WebView download/open PDF generated by TCPDF url

Recently, I came across a request to put a mobile website into an app, so people won't need to navigate the browser to access the pages.

WebView is a very nice API to serve this purpose, and this doesn't need to invoke the original mobile website design and coding.

The first challenging thing is I need to successfully download the PDF files that are generated by TCPDF in run time. Since WebView does not handle this kind of request automatically like the typically desktop browsers do, such as chrome or firefox etc. I got to adding code to WebVeiw how to handle the PDF which is generated by TCPDF.

I found that there were not even a Q&A like stackoverflow mentioned WebView with TCPDF. Anyway, there were still some Q&A about WebView and PDF/file download. But, most of the examples and questions are about downloading a physical file on server which is not exactly the same as working with TCPDF. But those materials are still helpful to me, a newbie of Android, a lot to figure out a solution.

So, now I want to share how this can be achieved, even the code may be raw, but this works for me.


First, initialing the download in setDownloadListener in onCreate Activity, after WebView instant is created, like the following:

        WebView myWebView = (WebView) findViewById(R.id.webview);
        myWebView.setWebViewClient(new WebViewClient());            
     
        myWebView.setDownloadListener(new DownloadListener() {
            @Override
            public void onDownloadStart(String url, String userAgent, String contentDisposition, String mimetype, long contentLength) {            
                //start download
                DownloadPDF downloadPDF = new DownloadPDF();
                downloadPDF.execute(url,userAgent,contentDisposition);
            }                    
        });
     
        WebSettings webSettings = myWebView.getSettings();
        webSettings.setJavaScriptEnabled(true);
        myWebView.loadUrl("http://www.examlple.com");                  
 

And then, adding a private class DownloadPDF() which extends Asynctask like below:

private class DownloadPDF extends AsyncTask<String, Integer, String> {                      
        @Override
        protected String doInBackground(String... sUrl) {  
            try {
            URL url = new URL(sUrl[0]);            
         
                File myDir = new File(Environment.getExternalStoragePublicDirectory(
                        Environment.DIRECTORY_DOWNLOADS).toString()+"/myPDF");
             
                // create the directory if it does not exist
                if (!myDir.exists()) myDir.mkdirs();          

                HttpURLConnection connection = (HttpURLConnection) url.openConnection();
                connection.setDoOutput(true);        
                connection.connect();          

                //get filename from the contentDisposition
                String filename = null;
                Pattern p = Pattern.compile("\"([^\"]*)\"");
Matcher m = p.matcher(sUrl[2]);
                while (m.find()) {
                filename = m.group(1);
                }      

                File outputFile = new File(myDir, filename);

                InputStream input   = new BufferedInputStream(connection.getInputStream());
                OutputStream output = new FileOutputStream(outputFile);
                           
                byte data[] = new byte[1024];
                long total = 0;
                int count;
                while ((count = input.read(data)) != -1) {
                    total += count;                  
                    output.write(data, 0, count);
                }              
                connection.disconnect();
                output.flush();
                output.close();
                input.close();
             
                // displayPdf();  a function to open the PDF file automatically
           
            } catch (MalformedURLException e) {
                e.printStackTrace();
            } catch (IOException e) {
                 e.printStackTrace();
            }
            return null;
        }
    }

Lastly, remember to add permission in Mainifest.xml:


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



Done. The next thing to do is "Open the PDF file automatically".




Friday, March 15, 2013

jQuery mobile v1.3.0 simple facebook-like/ slide panel example


css:

.ui-responsive-panel {
    z-index: 1;
    border-right: none;
    width:180px;
}

.ui-panel-animate.ui-panel-content-fixed-toolbar-position-left.ui-panel-content-fixed-toolbar-open.ui-panel-content-fixed-toolbar-display-reveal, .ui-panel-animate.ui-panel-content-fixed-toolbar-position-left.ui-panel-content-fixed-toolbar-open.ui-panel-content-fixed-toolbar-display-push, .ui-panel-animate.ui-panel-content-wrap-position-left.ui-panel-content-wrap-open.ui-panel-content-wrap-display-reveal, .ui-panel-animate.ui-panel-content-wrap-position-left.ui-panel-content-wrap-open.ui-panel-content-wrap-display-push {
    left: 0;
    right: 0;
    margin-left:-100px;
}


html:
<div data-role="page" >
        <div data-role="panel" id="left-panel" data-theme="a" data-position="left" data-position-fixed="false" data-display="push" class="ui-responsive-panel">
        </div>

        <div data-role="header" data-theme="b" data-position="fixed">

             <h1>my header</h1>
             <a href="#left-panel" data-icon="bars" data-iconpos="notext" data-shadow="false" data-iconshadow="false" class="ui-icon-nodisc">Open left panel</a>

        </div>

        <div data-role="content" ></div>  
        <div data-role="footer" data-theme="b" data-position="fixed"></div>
</div>

jQuery mobile v1.3.0, quick custom icon solution

css:

.ui-icon-mycustom  //ui-icon- is a prefix
{
    background: url(images/download.ico); //your url of the image e.g ico/png
    background-repeat: no-repeat;
    background-size: 18px 18px;
}

html:
data-icon="mycustom" //which is the name in css file without the prefix, ui-icon-



jQuery mobile 1.3.0 - date input problem/bug on Android (quick fix)

I found that the date will be append to the end of the input (an input on Panel). When you pick a date 2nd time from the calendar, the old date will not be replaced and appended the new date at the end. This does not occur in Chrome.

The adhoc solution is clear the input manually before the user pick a new date.
e.g.

$('#date-input') .on('click',function(){
      $(this).val('');
});

Tuesday, March 5, 2013

PHP - export uf-8 Chinese string to excel/csv encoding


A conversion is required before exporting it to output.

<?php

header('Content-type: application/vnd.ms-excel');
header("Content-Disposition: attachment; filename=file.xls");
header("Pragma: no-cache");

$buffer = $_POST['csvBuffer'];

echo chr(255) . chr(254) . mb_convert_encoding($buffer, 'UTF-16LE', 'UTF-8');

?>

here is the reference, http://php.net/manual/en/ref.mbstring.php