顯示具有 Java 標籤的文章。 顯示所有文章
顯示具有 Java 標籤的文章。 顯示所有文章

2013年9月12日 星期四

get/set properties in Android

I tried to use setprop/getprop shell commands to test if I could set/get a defined key to a system's permanent storage, but it didn't do that instead the key is gone after reboot. I then turned to another solution after researching the internet. I create a properties file in the data folder and read it into a Properties object. I can operate on  the object and save back the properties after finish. The following is the snippet I wrote after referring to the red link in the reference links below:

public class HotkeyListAdapter extends ArrayAdapter<Object> {
    int total_hotkey_count = 8;
    Context context;
    static String filePath = "/data/hotkey.properties";
    String hotkey_map[] = {
        "A", "B", "C", "D", "E", "F", "G", "H"
    };
    static String targetApp = "com.android.settings";   
    static String TAG = "HotkeyListAdapter";

    public HotkeyListAdapter(Context context, int resourceId) {
        super(context, resourceId);
        this.context = context;
    }

    @Override
    public int getCount() {
        return total_hotkey_count;
    }

    @Override
    public Object getItem(int position) {
        return position;
    }

    @Override
    public long getItemId(int position) {
        return position;
    }

    static Properties loadPropties() throws IOException {
        Properties prop = new Properties();
        try {
            InputStream fileStream = new BufferedInputStream(new FileInputStream(filePath));
            prop.load(fileStream);
            fileStream.close();
        } 
     catch (FileNotFoundException e) {
            Log.e(TAG , "Got exception " + e);
        }
        return prop;
    }


    static void storePropties(Properties prop) throws IOException {
        try {
            OutputStream fileStream = new BufferedOutputStream(new FileOutputStream(filePath));
            prop.store(fileStream, "Hotkey mappings");
            fileStream.close();
        } 
     catch (FileNotFoundException e) {
            Log.e(TAG , "Got exception " + e);
        }
    }


    static String getProperties(String key) {
     Properties prop;
        try {
            prop = loadPropties();
         return prop.getProperty(key, "NA");
        }
     catch (IOException e) {
            Log.e(TAG, "Exception", e);
        }
     return "NA";
    }

    static void setProperties(String key, String value) {
     Properties prop;   
        try {
         prop = loadPropties();
         prop.setProperty(key, value);
            storePropties(prop);
        }
     catch (IOException e) {
            Log.e(TAG, "Exception", e);
        }
     return;   
    }


    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        ImageView appImage;
     TextView keyName, appName;   
     Intent hotkey_intent;
     List<ResolveInfo> resolveinfo_list;
     PackageManager pm = context.getPackageManager();
        LayoutInflater mInflater = (LayoutInflater) context.getSystemService(context.LAYOUT_INFLATER_SERVICE);

        if (convertView == null) {
            convertView = mInflater.inflate(R.layout.hotkey_list_row, null);    
        }   

        appImage = (ImageView) convertView.findViewById(R.id.hotkey_app_image);
     appName = (TextView) convertView.findViewById(R.id.hotkey_app_name);
     keyName = (TextView) convertView.findViewById(R.id.hotkey_name);

        /* Retrieve APP info */
     targetApp = getProperties("hotkey"+position);   
     Log.d(TAG, targetApp);
   
        hotkey_intent = new Intent("android.intent.action.MAIN");
        hotkey_intent.addCategory("android.intent.category.LAUNCHER");
     resolveinfo_list = context.getPackageManager().queryIntentActivities(hotkey_intent, 0);
        for(ResolveInfo info:resolveinfo_list){
                 if(info.activityInfo.packageName.equalsIgnoreCase(targetApp)){    
                  ApplicationInfo ai;
                     appImage.setImageDrawable(info.activityInfo.loadIcon(pm));
            try {
                         ai = pm.getApplicationInfo(info.activityInfo.packageName, 0);
                     }
            catch (final NameNotFoundException e) {
                         ai = null;
                     }
            appName.setText((ai == null ? "not found" : pm.getApplicationLabel(ai)));
            keyName.setText(hotkey_map[position]);
                     break;
                 }
        }
       
        return convertView;
    }
}


public class HotkeyAppListDialog extends DialogFragment {
    private AlertDialog mDialog;
    private static HotkeyListAdapter hotkey_list_adapter;
    private static int hotkey_num;
   
    public static HotkeyAppListDialog newInstance(int position, HotkeyListAdapter adapter) {
        HotkeyAppListDialog frag = new HotkeyAppListDialog();
     hotkey_list_adapter = adapter;
     hotkey_num = position;
        return frag;
    }
   
    @Override
    public Dialog onCreateDialog(Bundle savedInstanceState) {
        AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
     HotkeyAppListAdapter adapter = new HotkeyAppListAdapter(getActivity(), R.layout.hotkey_app_list_row);
     ListView appList = new ListView(getActivity());   
     appList.setAdapter(adapter);
     appList.setLayoutParams(new ViewGroup.LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT));
     appList.setOnItemClickListener(new OnItemClickListener() {   
            public void onItemClick(AdapterView<?> parent,View view,int position,long id) {
          HotkeyListAdapter.setProperties("hotkey"+hotkey_num, (String)view.getTag());
          hotkey_list_adapter.notifyDataSetChanged();
                mDialog.cancel();
            }
        });
        builder.setView(appList);
        builder.setTitle(R.string.hotkey_app_list_title);  
     mDialog = builder.create();
        return mDialog;
    }
}


Reference links
http://stackoverflow.com/questions/14450714/how-do-i-read-a-property-from-project-properties-or-local-properties
Architecture
http://rxwen.blogspot.tw/2010/01/android-property-system.html
worth reading
http://hi.baidu.com/seucrcr/item/2b988c570cb63c9208be1778
http://bininda.com/blog/tag/android/


2013年7月28日 星期日

new JS2ANRD interface for javascript-only-semantics coding within a JS file

The JS files provided by the server use custom client interface that is beyond what Android can support, such as function pointer assignment and accessing array using string index. Thus an additional interface has to be included to form the following Android<->Javascript communication:

                                             Client                                             |                Server
local Android interface <---> local custom Javascript interface <---> remote Javascript code
        (gtkTV)                                            (atv)                                     

The following is the sample code:

[local tmp.html]

<HTML>
<head>
<script>
function atvclass() {
    this.loadXML = function(doc) {
        gtkTV.loadXML(doc);
    };
    this.parseXML = function(xml) {
        return gtkTV.parseXML(xml);
    };
    this.sessionStorage = new Array();
}

var atv = new atvclass();
</script>
</head>
<body>
</body>
</HTML>


[executing JS function]
...
       WebView myWebView = new WebView(this);
        myWebView.getSettings().setJavaScriptEnabled(true);
        myWebView.getSettings().setBuiltInZoomControls(true);
        System.out.println(android.os.Build.VERSION.SDK_INT);
        if(android.os.Build.VERSION.SDK_INT > android.os.Build.VERSION_CODES.ICE_CREAM_SANDWICH_MR1) {
            myWebView.getSettings().setAllowUniversalAccessFromFileURLs(true);
        }
        myWebView.setWebChromeClient(new WebChromeClient());
        myWebView.setWebViewClient(new WebViewClient() {
            @Override
            public void onPageFinished(WebView view, String url) {
                view.loadUrl("javascript:(function() { "
                        + " document.getElementByTagName = function(id) {"
                        + "     return document.getElementsByTagName(id);"
                        + " };"
                        + js_url_string
<--composed of creating script nodes to include multiple JS files
                        + "})()");
            }
        });
        myWebView.addJavascriptInterface(new MyAndroidJSInterface(this, SecondActivity.class), "gtkTV");
        myWebView.loadUrl("file:///android_asset/tmp.html");


[sample server JS code]

...
atv.Element.prototype.getElementByTagName = function(name)
    {
        var elements = this.getElementsByTagName(name);
        return(elements && elements.length > 0) ? elements[0] : undefined;
    };

...
atv.sessionStorage['srtlist'] = srtlist;
atv.sessionStorage['filelist'] = filelist;
...

Reference links
http://www.phpied.com/3-ways-to-define-a-javascript-class/

function pointer usage in Android/Java

Java does not support function pointer assignment, but there's an alternative to achieve the similar goal:

interface JS2ANRD_Prototype {
        public Element getElementById();
        public Element getElementsByTagName();

}

 kkk(new JS2ANRD_Prototype() {
            public Element getElementById() {
                return (Element)null;
            }
            public Element getElementsByTagName() {
                return (Element)null;
            }
        });


public void kkk(JS2ANRD_Prototype p) {...}

Reference links
http://www.ozzysun.com/2011/07/androidcallback.html
http://stackoverflow.com/questions/1073358/function-pointers-in-java

2013年7月24日 星期三

Create Document elements using javascript

js_url_string = js_url_string +
                    " var script=document.createElement('script'); "
                    + " script.setAttribute('type','text/javascript'); "
                    + " script.setAttribute('src', '"
                    + js_url[i]
                    + "'); "
                    + " script.onload = function() {"
                    + funcPtr + ";"
                    + "};"
                    + " document.getElementsByTagName('head')[0].appendChild(script); ";


myWebView.setWebViewClient(new WebViewClient() {
            @Override
            public void onPageFinished(WebView view, String url) {
                view.loadUrl("javascript:(function() { "
                        + js_url_string
                        + "})()");
            }
        });


Reference links
http://stackoverflow.com/questions/8227612/how-to-create-document-objects-with-javascript
http://www.javascriptkit.com/javatutors/dom2.shtml

2013年7月22日 星期一

HTTP retrieved content into String/InputStream

public String getURL(String url) {
        String result = null;

        try {
            // defaultHttpClient
            DefaultHttpClient httpClient = new DefaultHttpClient();
            HttpPost httpPost = new HttpPost(url);

            HttpResponse httpResponse = httpClient.execute(httpPost);
            HttpEntity httpEntity = httpResponse.getEntity();
            result = EntityUtils.toString(httpEntity);

        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        } catch (ClientProtocolException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }

        return result;
    }
   
    public InputStream getURLAsInputStream(String url) {
        InputStream result = null;

        try {
            // defaultHttpClient
            DefaultHttpClient httpClient = new DefaultHttpClient();
            HttpPost httpPost = new HttpPost(url);

            HttpResponse httpResponse = httpClient.execute(httpPost);
            HttpEntity httpEntity = httpResponse.getEntity();
            result = httpEntity.getContent();

        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        } catch (ClientProtocolException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }

        return result;
    }

2013年7月21日 星期日

2013年7月16日 星期二

Resolve cross-domain error

The scheme is that I call a JS function after loading a JS file from web, and in the JS function it in the end sends out a HTTP GET request to obtain a web page which I use for further tasks. I run into a problem when the HTTP GET request is sent and the error message shows:

XmlHttpRequest error: Origin null is not allowed by Access-Control-Allow-Origin

This is a common CORS issue, which is short for Cross-Origin Resource Sharing. I surfed the internet a lot and found some possible answers to this question. One of them is to get the server side to set up a "Access-Control-Allow-Origin: *" text in the header to tell clients that it allows CORS. However, in my case I have not got to the point of completing sending out a request, but rather got rejected before it. This could be a local check for origin, so I checked the development guide for possible web settings and found this one:

void setAllowUniversalAccessFromFileURLs(boolean flag)
Sets whether JavaScript running in the context of a file scheme URL should be allowed to access content from any origin.

And it works ! The following is part of my code which points out my use of it:

notice: The setting is true by default under API level 16 and false by default equivalent or above API level 16. The API is only defined after API level 16.

[JavaScriptHandler activity]
in pptv.js it uses the following Android interface, so I create them in this activity to save the result:
atv.loadXML(atv.parseXML(doc));

public class JavaScriptHandler {
    Webview_test parentActivity;
   
    public JavaScriptHandler(Webview_test activity) {
        parentActivity = activity;
    }

    @JavascriptInterface
    public String parseXML(String val){
        System.out.println("parseXML...");
        return val;
    }
   
    @JavascriptInterface
    public void loadXML(String val){
        System.out.println("loadXML...");
        this.parentActivity.javascriptCallFinished(val);
    }
}


[WebView activity]
public class Webview_test extends Activity {
    WebView myWebView;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_webview_test);
        // Show the Up button in the action bar.
        setupActionBar();
        Intent intent = getIntent();
        String url = intent.getStringExtra("xml_url");
        myWebView = (WebView) findViewById(R.id.webview);
        myWebView.getSettings().setJavaScriptEnabled(true);
        myWebView.getSettings().setBuiltInZoomControls(true);

        if(android.os.Build.VERSION.SDK_INT > android.os.Build.VERSION_CODES.ICE_CREAM_SANDWICH_MR1) {
           
myWebView.getSettings().setAllowUniversalAccessFromFileURLs(true);

        }
        myWebView.setWebChromeClient(new WebChromeClient() {
            @Override
            public boolean onJsAlert(WebView view, String url, String message, JsResult result) {
                        System.out.println("got here!");
                        return super.onJsAlert(view, url, message, result);
               }
        });  
        myWebView.setWebViewClient(new WebViewClient() {
            @Override
            public void onPageFinished(WebView view, String url) {
                view.loadUrl("javascript:(function() { "
                        + " document.bgColor='#FF0000';" //turns to red the background color
                        + " var script=document.createElement('script'); "
                        + " script.setAttribute('type','text/javascript'); "
                        + " script.setAttribute('src', 'http://trailers.apple.com/js/pptv.js'); "
                        + " script.onload = function() {"
                        + "       pptvLoad();"
                        + "};"
                        + " document.getElementsByTagName('head')[0].appendChild(script); "
                        + "})()");
            }
        });
        myWebView.addJavascriptInterface(new JavaScriptHandler(this), "atv");
    }
...
    public void javascriptCallFinished(String val){
         System.out.println(val);
    }
...   
    public void testAndroid2JS(View view) throws IOException
    {
        myWebView.loadUrl("file:///android_asset/testalert2.html");
    }
}


Reference links
http://stackoverflow.com/questions/14825943/setallowuniversalaccessfromfileurls
cross-site HTTP request (CORS)
https://developer.mozilla.org/en-US/docs/HTTP/Access_control_CORS?redirectlocale=en-US&redirectslug=HTTP_access_control
Introduction to XMLHttpRequest class in Javascript
http://ajaxpatterns.org/XMLHttpRequest_Call
development guide
http://developer.android.com/reference/android/webkit/WebSettings.html

2013年7月15日 星期一

Retrieve JS function result in Android application

Thanks to a blogger at the reference link for a awesome explanation !

The details can be referenced at the link at the bottom. The following is a recording of what I've tested in my workspace:

[JavascriptHandler.java]
import android.webkit.JavascriptInterface;

public class JavaScriptHandler {
    Webview_test parentActivity;
   
    public JavaScriptHandler(Webview_test activity) {
        parentActivity = activity;
    }

    @JavascriptInterface
<-- need to have this since API lvl 17 for any methods that could be used from JS function
    public void setResult(int val){
        this.parentActivity.javascriptCallFinished(val);
    }
}


[webview_test.java]
public class Webview_test extends Activity {
    WebView myWebView;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_webview_test);
        // Show the Up button in the action bar.
        setupActionBar();
        Intent intent = getIntent();
        String url = intent.getStringExtra("xml_url");
        myWebView = (WebView) findViewById(R.id.webview);
        myWebView.getSettings().setJavaScriptEnabled(true);
        myWebView.getSettings().setBuiltInZoomControls(true);
        myWebView.setWebChromeClient(new WebChromeClient());   
        myWebView.setWebViewClient(new WebViewClient());
        myWebView.loadUrl("file:///android_asset/testRet.html");
        myWebView.addJavascriptInterface(new JavaScriptHandler(this), "MyHandler");
    }

    public void javascriptCallFinished(int val){
         System.out.println(val);
    }  
    

...

    public void testAndroid2JS(View view) throws IOException
    {
        myWebView.loadUrl("javascript:window.MyHandler.setResult(ret_something());");
    }


[testRet.html]
<HTML>
<center><b><u>JavaScript</u></b></center>
<script language="JavaScript">
function ret_something() {
return 999;
}
</script>
</head>
<body>
<p> Enter your name here <input type="text" id="namefromjs" />
<p> <input type="button" onclick= "setData()" value="Call Android sayHelloFromAndroid() method from JS">
<p> Data pass from Android</p>
<p> <input type="button" onclick= "getData()" value="Get Name From Android EditText">
<p> Name from Android is <input type="text" id="namefromAndroid" />
</body>
</HTML>


------------------------------------------------------------------------------------------------------
Sometimes you want to call JS functions after loading a web page. Or, you just simply feel like using some JS funtions to do something. The following code snippet shows a way to achieve this:

[Webview_test activity]
public class Webview_test extends Activity {
...
        myWebView.setWebViewClient(new WebViewClient() {
            @Override
            public void onPageFinished(WebView view, String url) {
                view.loadUrl("javascript:(function() { "
                        + " document.bgColor='#FF0000';" //turns to red the background color
                        + " var script=document.createElement('script'); "
                        + " script.setAttribute('type','text/javascript'); "
                        + " script.setAttribute('src', 'file:///android_asset/pptv.js'); "
                        + " script.onload = function(){ "
                        + "     window.MyHandler.setResult(test()); "
                        + " }; "
                        + " document.getElementsByTagName('head')[0].appendChild(script); "
                        + "})()");
            }

        });
 ...
    }

...
    public void testAndroid2JS(View view) throws IOException
    {
        myWebView.loadUrl("file:///android_asset/testalert2.html");
    }

}

[pptv.js]
function test()
{
    return "test...";
}


[testalert2.html]
<HTML>
<head>
<center><b><u>JavaScript</u></b></center>
</head>
<body>
<p> Enter your name here <input type="text" id="namefromjs" />
<p> <input type="button" onclick= "setData()" value="Call Android sayHelloFromAndroid() method from JS">
<p> Data pass from Android</p>
<p> <input type="button" onclick= "getData()" value="Get Name From Android EditText">
<p> Name from Android is <input type="text" id="namefromAndroid" />
</body>
</HTML>


Reference links
http://blog.objectgraph.com/index.php/2012/03/16/android-development-javascript-bridge-example-fully-explained/
http://stackoverflow.com/questions/5649111/android-webview-loading-javascript-file-in-assets-folder

2013年7月11日 星期四

Read a asset file into a String

private String assetFile2Str(String path) throws IOException {
InputStream is = getAssets().open(path);
int size = is.available();

byte[] buffer = new byte[size];
is.read(buffer);
is.close();

String str = new String(buffer);
return str;
}

where path should be a relative path to the base URL, for example, if there is a target file located at:

file:///android_asset/www/test.html

then you should put "www/test.html" as path.

Reference links
http://stackoverflow.com/questions/11157876/read-html-file-from-assets

2013年7月8日 星期一

Java basics for newbie

Common keyword clarification:
1. Static
Two types of static members are static field and static method. The value of static field is the same across all instances of the class and that means there's only one copy of the field. One good example is to use a static field to keep track of the amount of created instances of a class. Similarly, to use a static method, one doesn't have to create an instance before using it. One basic rule is that it's invalid to use non-static members in a static method, because a static method does not even know those non-static members exist !

The below code snippet will cause compiler error:

public class ABC() {
    public String data = "123"
    
    public static void main() {
        System.out.println(data);
    }
}

instead, use the following way to access a non-static member from static method:

public class ABC() {
    public String data = "123"
    
    public static void main() {
        ABC n = new ABC();
        System.out.println(n.data);
    }
}

2. Visibility keywords - public, private, protected, no modifier(default)

Access Levels
Modifier Class Package Subclass World
public Y Y Y Y
protected Y Y Y N
no modifier Y Y N N
private Y N N N

Class: within the same class
Package: within the same package
no modifier(default): package-private, within the same package
World: among different packages

Tips about choosing access level on a class/member:
1. Use the most restrictive level that makes sense for a particular class/member. Use private unless you have a good reason not to.
2. Avoid using public except for constants.

3. final
final class: once declared as final, the class can not be extended by other classes
final method: a final method can not be overridden by subclasses
final member: a final variable can only be assigned once when either in a initializer or through an assignment statement before constructor is done with its job.

Reference
http://www.dummies.com/how-to/content/what-is-the-static-keyword-in-java.html
http://www.javacoffeebreak.com/articles/toptenerrors.html
http://docs.oracle.com/javase/tutorial/java/javaOO/accesscontrol.html
http://en.wikipedia.org/wiki/Final_%28Java%29