2017年12月25日 星期一

Get the user id (Check if the user is owner)

Reference:
https://stackoverflow.com/questions/14749504/android-usermanager-check-if-user-is-owner-admin
http://blog.csdn.net/zhanglianyu00/article/details/50238447
http://blog.csdn.net/zhanglianyu00/article/details/50253187
http://gityuan.com/2016/11/20/user_manager/


Code snippet:
public static long getUserId(Context context){
    UserHandle uh = Process.myUserHandle();
    UserManager um = (UserManager) context.getSystemService(Context.USER_SERVICE);
    long userSerialNumber = -1;
    if(null != um) {
        userSerialNumber = um.getSerialNumberForUser(uh);
    }
    return userSerialNumber;
}

Get duration of the video by uri

Reference:

https://stackoverflow.com/questions/33770188/how-can-i-get-the-duration-resolution-of-a-video-file-programmatically-in-andro/33770238


Code snippet:

MediaPlayer mp = MediaPlayer.create(this, Uri.parse(uriOfFile));
if(mp == null){
    Log.e(TAG, "The record file is invalid");
     return;
}
int duration = mp.getDuration();

Detect switching between users

Reference:

https://stackoverflow.com/questions/15392126/how-to-detect-switching-between-users


Description:

Listen the ACTION_USER_BACKGROUND and ACTION_USER_FOREGROUND broadcast to detect switching between users.


Code snippet:


IntentFilter filter = new IntentFilter();
filter.addAction(Intent.ACTION_USER_BACKGROUND);
filter.addAction(Intent.ACTION_USER_FOREGROUND);
registerReceiver(mUserSwitchReceiver, filter);



public class UserSwitchReceiver extends BroadcastReceiver {
    private static final String TAG = "UserSwitchReceiver";
    @Override    public void onReceive(Context context, Intent intent) {
        String action = intent.getAction();
        
        if(Intent.ACTION_USER_FOREGROUND.equals(action)){
            // Todo: do something when switch in the user
        } else if(Intent.ACTION_USER_BACKGROUND.equals(action)){
            // Todo: do something when switch out the user
        }
    }
}

2017年12月5日 星期二

當Low memory時,Activity被銷毀了,但是Fragment重複產生

Reference:

http://juniperphoton.net/2017/03/27/jie-jue-viewpager-fragment-de-hui-fu-wen-ti/
http://y-anz-m.blogspot.tw/2013/07/viewpager-fragment.html
http://www.voidcn.com/article/p-weicgtjm-kv.html

Issue description:

當Low memory時,process被系統刪除,Activity被銷毀了,但是Fragment還在,導致Fragment重疊,造成開啟app的時間不斷增加

使用 adb shell dumpsys activity top 指令
可以發現 fragment數量不停累加

Solution:

在Activity被銷毀前,先將正在使用的fragment object儲存起來
下次開啟app後,再將前一次使用的fragment object (現在用不到的) 刪除掉

Code snippet:

    @Override
    protected void onSaveInstanceState(Bundle outState) {
        super.onSaveInstanceState(outState);
        // Store the current used fragments
        if (mFragmentAdapter != null) {
            getSupportFragmentManager().putFragment(outState, KEY_FRAG_NAME1, mFragmentAdapter.getFragment(0));
            getSupportFragmentManager().putFragment(outState, KEY_FRAG_NAME2, mFragmentAdapter.getFragment(1));
        }
    }

    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
.................
        // Remove previous unused fragments
        if (savedInstanceState != null) {
            mFragment1 = getSupportFragmentManager().getFragment(savedInstanceState,
                    KEY_FRAG_NAME1);
            mFragment2 = getSupportFragmentManager().getFragment(savedInstanceState,
                    KEY_FRAG_NAME2);

            if(mFragment1 != null || mFragment2 != null){
                FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
                transaction.remove(mFragment1 );
                transaction.remove(mFragment2 );
                transaction.commit();
            }
        }
...............
    }

2017年11月22日 星期三

Notification icon flash on status bar

Problem:


當需要在短時間內持續更新notification的內容時
在建立Notification時,記得使用同一個Notification Builder物件,並且設定 setOnlyAlertOnce(true)
以免SystemUI一直在update notification,造成status bar UI一直在跳動

Code snippet:


return new Notification.Builder(context, NOTIFICATION_CHANNEL_ID)
            mBuilder
                        .setContentTitle(title)
                        .setContentText(contentText)
                        .setContentIntent(contentIntent)
                        .setSmallIcon(R.drawable.noti_icon)
                        .setLargeIcon(appIcon)
                        .setSubText(appLabel)
                        .setColor(color)
                        .setOnlyAlertOnce(true)
                        .addAction(stopAction)
                        //.addAction(optionsAction)

                        .build();

2017年9月22日 星期五

Change ActionBar text color

Reference:

Code sinppet:
AppCompactActivity
<style name="AppBaseTheme" parent="@style/Theme.AppCompat.Light.DarkActionBar">
    <item name="actionBarStyle">@style/MyActionBar</item>
</style>

<style name="MyActionBar" parent="@style/Widget.AppCompat.ActionBar.Solid">
    <item name="titleTextStyle">@style/MyTitleTextStyle</item>
</style>

<style name="MyTitleTextStyle" parent="@style/TextAppearance.AppCompat.Widget.ActionBar.Title">
    <item name="android:textColor">CHANGE_COLOR_HERE</item>
</style>


Settings Activity
<style name="AppTheme" parent="@android:style/Theme.DeviceDefault.Settings">
        <item name="android:colorAccent">@color/your_color</item>
</style>

2017年8月25日 星期五

How to disable scrolling on ViewPager

Reference:

https://stackoverflow.com/questions/7814017/is-it-possible-to-disable-scrolling-on-a-viewpager

Code snippet:

1. Create a new CustomViewPager class
public class CustomViewPager extends ViewPager {

    private boolean isPagingEnabled = true;

    public CustomViewPager(Context context) {
        super(context);
    }

    public CustomViewPager(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        return this.isPagingEnabled && super.onTouchEvent(event);
    }

    @Override
    public boolean onInterceptTouchEvent(MotionEvent event) {
        return this.isPagingEnabled && super.onInterceptTouchEvent(event);
    }

    public void setPagingEnabled(boolean b) {
        this.isPagingEnabled = b;
    }
}

2. Replace the <com.android.support.V4.ViewPager> tag with <your_package_name.CustomViewPager > tag in layout.xml

3. Call setPagingEnabled(true) when you want to disable Scrolling and call setPagingEnabled(false) to enable it.

How to disable click event on TabLayout

Reference:

https://stackoverflow.com/questions/37809538/how-can-i-disable-click-on-tablayout-in-android

Code snippet:

mTabLayout = (TabLayout) findViewById(R.id.tab_layout);

LinearLayout tabStrip = (LinearLayout) mTabLayout.getChildAt(0);
for(int i = 0; i < tabStrip.getChildCount(); i++) {
    tabStrip.getChildAt(i).setOnTouchListener(new View.OnTouchListener() {
        @Override        public boolean onTouch(View v, MotionEvent event) {
            return true;
        }
    });
}

2017年7月24日 星期一

Rename a directory in a Git repository

Reference:

https://stackoverflow.com/questions/11183788/in-a-git-repository-how-to-properly-rename-a-directory

Command:

git mv <old name> <new name>

Example:
Rename the directory from source to target

git mv source tmp
git mv tmp target

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)