2017年7月20日 星期四

How to check if system is 12 or 24 hour time format

Reference:

https://stackoverflow.com/questions/4392636/how-do-i-check-if-system-is-12-or-24-hour
https://stackoverflow.com/questions/23222199/12-24-hour-mode-conflict
https://stackoverflow.com/questions/8834452/androidchanging-time-format-as-per-the-devices-current-time-format

Code snippet:

private boolean mIs24HourFormat = false;
private TextView mTime;

MainActivity.java
@Overrideprotected void onCreate(Bundle savedInstanceState) {
    ......
    mIs24HourFormat = android.text.format.DateFormat.is24HourFormat(mContext);
    ....
    mTime.setText(getTime());
}

private String getTime() {
    DateFormat df =
null;
   
if(mIs24HourFormat){
        df =
new SimpleDateFormat(mContext.getResources().getString(R.string.time_format_24hr), Locale.getDefault());
    }
else{
        df =
new SimpleDateFormat(mContext.getResources().getString(R.string.time_format_12hr), Locale.getDefault());
    }
   
return df.format(Calendar.getInstance().getTime());
}


strings.xml
<string name="time_format_24hr" translatable="false">HH\uee01mm</string>
<
string name="time_format_12hr" translatable="false">h\uee01mm</string>



Note:

If you want to show the time format followed by system, you can use android widget TextClock.

2017年6月7日 星期三

Crash when set TYPE_DISPLAY_OVERLAY type by inflection

Error:
android.view.WindowManager$BadTokenException: Unable to add window android.view.ViewRootImpl$W@71dc214 -- permission denied for window type 2026

Solution:
TYPE_DISPLAY_OVERLAY需要
android.Manifest.permission.INTERNAL_SYSTEM_WINDOW

AndroidManifest.xml
<permission android:name="android.permission.INTERNAL_SYSTEM_WINDOW" android:protectionLevel="signature" />

app要 sign platform key才可使用


Call stack:
java.lang.RuntimeException: Unable to start activity ComponentInfo{com.example.hueijheyu.reflectiondemo/com.example.hueijheyu.reflectiondemo.MainActivity}: android.view.WindowManager$BadTokenException: Unable to add window android.view.ViewRootImpl$W@71dc214 -- permission denied for window type 2026
                                                       at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2665)
                                                       at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2726)
                                                       at android.app.ActivityThread.-wrap12(ActivityThread.java)
                                                       at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1477)
                                                       at android.os.Handler.dispatchMessage(Handler.java:102)
                                                       at android.os.Looper.loop(Looper.java:154)
                                                       at android.app.ActivityThread.main(ActivityThread.java:6119)
                                                       at java.lang.reflect.Method.invoke(Native Method)
                                                       at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:886)
                                                       at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:776)
                                                    Caused by: android.view.WindowManager$BadTokenException: Unable to add window android.view.ViewRootImpl$W@71dc214 -- permission denied for window type 2026
                                                       at android.view.ViewRootImpl.setView(ViewRootImpl.java:703)
                                                       at android.view.WindowManagerGlobal.addView(WindowManagerGlobal.java:342)
                                                       at android.view.WindowManagerImpl.addView(WindowManagerImpl.java:94)
                                                       at android.app.Dialog.show(Dialog.java:322)
                                                       at com.example.hueijheyu.reflectiondemo.MainActivity.onCreate(MainActivity.java:39)
                                                       at android.app.Activity.performCreate(Activity.java:6723)
                                                       at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1119)
                                                       at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2618)
                                                       at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2726)
                                                       at android.app.ActivityThread.-wrap12(ActivityThread.java)
                                                       at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1477)
                                                       at android.os.Handler.dispatchMessage(Handler.java:102)
                                                       at android.os.Looper.loop(Looper.java:154)
                                                       at android.app.ActivityThread.main(ActivityThread.java:6119)
                                                       at java.lang.reflect.Method.invoke(Native Method)
                                                       at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:886)
                                                       at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:776)

2017年5月17日 星期三

Create a md5 hash from a string

Reference:

https://dzone.com/articles/android-snippet-making-md5

Code snippet:

public static String encryptByMd5Hash(String in) {
    String md5 = "";
    MessageDigest digest;
    try {
        digest = MessageDigest.getInstance("MD5");
        digest.reset();
        digest.update(in.getBytes());
        byte[] a = digest.digest();
        int len = a.length;
        StringBuilder sb = new StringBuilder(len << 1);
        for (int i = 0; i < len; i++) {
            sb.append(Character.forDigit((a[i] & 0xf0) >> 4, 16));
            sb.append(Character.forDigit(a[i] & 0x0f, 16));
        }
        md5 = sb.toString();
    } catch (NoSuchAlgorithmException e){
        e.printStackTrace();
    }
    return md5;
}

2017年5月9日 星期二

Fix "SecurityException: Binder invocation to an incorrect interface using in-app billing" when bind remote AIDL service

Reference:

http://blog.csdn.net/a1031359915/article/details/7785227

Description:

當 client app使用AIDL向 server app請求服務的時後,client app和 server app的 AIDL package name要一模一樣,否則在執行的時後,就會跳出 SecurityException: Binder invocation to an incorrect interface using in-app billing的錯誤訊息

2017年3月21日 星期二

Set window flag by reflection

Reference:

http://stackoverflow.com/questions/10555407/android-how-to-get-an-instance-of-a-member-field-via-reflection
http://stackoverflow.com/questions/32218419/how-i-can-access-inner-class-variable-value-using-reflection-in-java
http://www.qingpingshan.com/m/view.php?aid=125698

Description:

為了讓 system dialog可以在 multi user裡顯示,必須要設定 PRIVATE_FLAG_SHOW_FOR_ALL_USERS flag,但是因為 setPrivateFlags是 Window.java的 hide API,PRIVATE_FLAG_SHOW_FOR_ALL_USERS是 WindowManager的 hide variable,除了直接 import framework.jar之外,還可以透過 reflection的機制

Code Snippet:

public static final int FRAMEWORK_PRIVATE_FLAG_SHOW_FOR_ALL_USERS = 0x00000010;
public static void setShowForAllUsers(Window window) {
    LogTool.d(TAG, "setShowForAllUsers");
    Class<?> windowClass = null;
    Class<?> windowManagerClass = null;
    try {
        try {
            windowClass = Class.forName("android.view.Window");
            windowManagerClass = Class.forName("android.view.WindowManager");
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
        try {
            Method method = windowClass.getDeclaredMethod("setPrivateFlags", int.class, int.class);
            method.setAccessible(true);

            try {
                try {
                    int allUsersFlag = FRAMEWORK_PRIVATE_FLAG_SHOW_FOR_ALL_USERS;
                    Class<?>[] classes = windowManagerClass.getDeclaredClasses();
                    for(Class innerClass: classes){
                        if(innerClass.getName().contains("LayoutParams")){
                            Field field = innerClass.getDeclaredField("PRIVATE_FLAG_SHOW_FOR_ALL_USERS");
                            field.setAccessible(true);
                            allUsersFlag = field.getInt(window.getAttributes());
                            break;
                        }
                    }
                    LogTool.d(TAG, "allUsersFlag: " + allUsersFlag + " ; hex: " + Integer.toHexString(allUsersFlag));

                    method.invoke(window, allUsersFlag, allUsersFlag);
                } catch (InvocationTargetException e) {
                    e.printStackTrace();
                }
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            }
        } catch (NullPointerException e1) {
            e1.printStackTrace();
        }
    } catch (NoSuchMethodException e) {
        e.printStackTrace();
    } catch(NoSuchFieldException nsfe){
        nsfe.printStackTrace();
    }
}

2017年2月20日 星期一

Android 6.0+ 請求 SYSTEM_ALERT_WINDOW權限

Reference:

http://stackoverflow.com/questions/32652533/android-system-overlay-window
http://caiyao.name/2016/03/02/Android-6-0%E8%BF%90%E8%A1%8C%E6%97%B6%E6%9D%83%E9%99%90%E5%B0%8F%E7%BB%93/

Error message:

Unable to add window android.view.ViewRoot$W@XXXXXXXX — permission denied for this window type


Code snippet:

在 Android 6.0+ (SDK 23+) 用 TYPE_SYSTEM_ALERT,即使在 Manifest裡有宣告 permission
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
依舊會出現上面的 Error message.
這時候要先透過 Settings.canDrawOverlays檢查 app是否有權限,如果沒有的話,發出 Settings.ACTION_MANAGE_OVERLAY_PERMISSION 的 Intent,讓使用者打開權限

@Overrideprotected void onResume() {
    super.onResume();

    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
        if(!Settings.canDrawOverlays(this)){
            requestAlertWindowPermission();
        } else{
            // do something
        }
    } else{
        // do something
    }
}


private static final int REQUEST_CODE = 1;
private  void requestAlertWindowPermission() {
    Intent intent = new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION);
    intent.setData(Uri.parse("package:" + getPackageName()));
    startActivityForResult(intent, REQUEST_CODE);
}

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
    super.onActivityResult(requestCode, resultCode, data);
    if (requestCode == REQUEST_CODE) {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
            if (Settings.canDrawOverlays(this)) {
                Log.i(TAG, "onActivityResult granted");
                // do something
            } else{
                Log.i(TAG, "onActivityResult not granted");
            }
        }
    }
}


2017年2月6日 星期一

Turning on screen and keep screen lon

Reference:

http://stackoverflow.com/questions/2891337/turning-on-screen-programmatically

Advance:
Turn on screen and dismiss keyguard
http://stackoverflow.com/questions/30246425/turning-on-screen-from-receiver-service

Code snippet:

在需要打開螢幕的地方加入下面的 code
WakeLock wl = ((PowerManager)getSystemService(POWER_SERVICE)).newWakeLock(
     PowerManager.SCREEN_BRIGHT_WAKE_LOCK | PowerManager.ACQUIRE_CAUSES_WAKEUP, "TAG");
wl.acquire();

在離開前要記得釋放 wake lock,以免螢幕保持開啟,造成耗電的問題
wl.release();

Note:
釋放 (release)的 wake lock要和獲取 (acquire)的是同一個,不要再重新建立一個新的wake lock,否則釋放 wake lock時會不能正常運作