上色

Code Block

2015年12月14日 星期一

[Android] 偵測方向使用不同layout

若要偵測使用者螢幕方向,並使用對應不同xml的layout,可使用下列方式︰

  1. 設計兩種不同layout的xml檔案。
  2. 在AndroidManifest.xml中要改變的activity中新增屬性︰
  3. android:configChanges="orientation|keyboardHidden|screenSize"
  4. 如此一來當方向或螢幕大小改變的時候,就會觸發︰
    onConfigurationChanged(Configuration newConfig)
  5. 依照類似下面的方式判斷長寬,以決定適用的layout file。
    public void onConfigurationChanged(Configuration newConfig) {
            super.onConfigurationChanged(newConfig);
            Display display = getWindowManager().getDefaultDisplay();
            int width;
            int height;
            Point size = new Point();
            display.getSize(size);
            width=size.x;
            height=size.y;
            if (width>height) { //Landscape
                setContentView(R.layout.activity_main_land);
                initView();
            }
            else{
                setContentView(R.layout.activity_main_port);
                initView();
            }
        }
    

2015年12月11日 星期五

[Android] Shell Tool for root commands


import java.io.DataOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.nio.charset.Charset;
import java.util.Scanner;

import android.content.res.AssetManager;
import android.os.Build;
import android.util.Log;

public class ShellUtils
{
 private static String busyboxPath="/system/xbin/busybox";

 /**
  * Set custom busybox path
  * @param path Path
  */
 public static void setCustomBusybox(String path){
  busyboxPath = path;
 }
 
 public static boolean checkBusyboxPath(){
  try{
   if (!new File("/system/xbin/busybox").exists()){
    if(!new File("/system/bin/busybox").exists())
     return false;
    else busyboxPath="/system/bin/busybox";
   }
            else busyboxPath="/system/xbin/busybox";
  }catch (SecurityException e){
   return false;
  }
  return true;
 }

 /**
  * Try to get root permission
  * @return true means success; false means fail
  */
 public static boolean rootCheck(){
  //List all the files under "/data/data", doing this operation needs root permission
  //CommandResult commandResult = execCommand(false,true, "ls /data/data/");
  //Empty result means there's no root permission
        //return !commandResult.successMsg.isEmpty();
  return execCommand(false,true,"").result==0;
 }

 @SuppressWarnings("deprecation")
 public static boolean initiateBusybox(){
  //Clear old files
  File busybox = new File(GlobalVariable.getContext().getFilesDir()+"/busybox");
  if (busybox.exists())
   busybox.delete();
  //Copy corresponding busybox to internal folder
  String cpuVersion;
  if (android.os.Build.VERSION.SDK_INT <21)
   cpuVersion= Build.CPU_ABI;
  else
   cpuVersion=Build.SUPPORTED_ABIS[0];
  //Determine busybox version
  String filename;
  cpuVersion=cpuVersion.toLowerCase();
  if (cpuVersion.contains("arm"))
   filename="busybox-armv5l";
  else if (cpuVersion.contains("x86"))
   filename="busybox-i686";
  else
   return false;
  AssetManager assetManager = GlobalVariable.getContext().getAssets();
  InputStream in = null;
  OutputStream out = null;
  try {
   in = assetManager.open(filename);
   out = new FileOutputStream(busybox);
   byte[] buffer = new byte[1024];
   int read;
   while ((read = in.read(buffer)) != -1) {
    out.write(buffer, 0, read);
   }
  }
  catch (IOException e) {
   e.printStackTrace();
   return false;
  }
  finally {
   if (in != null) {
    try {
     in.close();
    } catch (IOException e) {
     e.printStackTrace();
    }
   }
   if (out != null) {
    try {
     out.close();
    } catch (IOException e) {
     e.printStackTrace();
    }
   }
  }
  //Make busybox executables
  ShellUtils.execCommand(false, true, "chmod 0755 " + GlobalVariable.getContext().getFilesDir() + "/busybox");
  return true;
 }

 /**
  * Execute commands and get the results
  * @param isBusybox Run cmds via busybox or not
     * @param isRoot Run cmds as root?
  * @param cmds Command(s)
  * @return Results of commands
  */
 public static CommandResult execCommand(boolean isBusybox, boolean isRoot, String...cmds){
        int result=-1;
        StringBuilder successMsg = null;
        StringBuilder errorMsg = null;
        if (cmds==null || cmds.length==0)
            return new CommandResult(result,null,null);
  try{
            Process proc=Runtime.getRuntime().exec(isRoot?"su":"sh");
   DataOutputStream opt = new DataOutputStream(proc.getOutputStream());
   boolean exitCmdFound=false;
   //Write cmds
   for(String cmd:cmds){
                //Using busybox and check its path
    if (isBusybox)
     opt.writeBytes(busyboxPath + " ");
    //Unless make sure cmd is in English, using UTF-8 coding, avoid wrong coding.
    opt.write(cmd.getBytes(Charset.forName("utf-8")));
    if(!exitCmdFound && cmd.equals("exit"))
                    exitCmdFound=true;
    opt.writeBytes("\n");
   }
   if(!exitCmdFound)
                opt.writeBytes("exit\n");
   opt.flush(); 
            //Get results
            result=proc.waitFor();
            successMsg = new StringBuilder();
            errorMsg = new StringBuilder();
            Scanner scanner;
            scanner=new Scanner(new InputStreamReader(proc.getInputStream()));
            while (scanner.hasNextLine())
                successMsg.append(scanner.nextLine());
            scanner=new Scanner(new InputStreamReader(proc.getErrorStream()));
            while (scanner.hasNextLine())
                errorMsg.append(scanner.nextLine());
  } catch (Exception e){
            e.printStackTrace();
        }
        return new CommandResult(result,successMsg==null?null:successMsg.toString(), errorMsg==null?null:errorMsg.toString());
 }

    public static class CommandResult {

        /**
         * Result of command
         * 0: normal
         * else: error
         */
        public int    result;
        public String successMsg;
        public String errorMsg;

        public CommandResult(int result, String successMsg, String errorMsg) {
            this.result = result;
            this.successMsg = successMsg;
            this.errorMsg = errorMsg;
        }
    }
}

2015年7月31日 星期五

[C#] Load custom cursor from resources

Sometime you need a custom cursor for your application, the following step let you use your custom cursor in the resources.


  1. Add cursor files (*.cur) to resources
    • In [Properties] -> [Resources] tab, click [Add Resource] -> [Add Existing File...]
    • Choose your custom cursors and click OK.
  2. Load cursor with following code:
this.Cursor = new Cursor(new System.IO.MemoryStream(Properties.Resources.MyCursor));
, where Mycursor refers to your cursor name.

2015年7月25日 星期六

[C#] 取得picture box在zoom模式時的正確圖片座標

private Point unScale(Point scaledP)
{
 if (picturebox1.SizeMode != PictureBoxSizeMode.Zoom) //only zoom mode need to scale
  return scaledP;
 Point unscaled_p = new Point();
 // image and container dimensions
 int w_i = picturebox1.Image.Width;
 int h_i = picturebox1.Image.Height;
 int w_c = picturebox1.Width;
 int h_c = picturebox1.Height;
 float imageRatio = w_i / (float)h_i; // image W:H ratio
 float containerRatio = w_c / (float)h_c; // container W:H ratio

 if (imageRatio >= containerRatio)
 {
  // horizontal image
  float scaleFactor = w_c / (float)w_i;
  float scaledHeight = h_i * scaleFactor;
  // calculate gap between top of container and top of image
  float filler = Math.Abs(h_c - scaledHeight) / 2;
  unscaled_p.X = (int)(scaledP.X / scaleFactor);
  unscaled_p.Y = (int)((scaledP.Y - filler) / scaleFactor);
 }
 else
 {
  // vertical image
  float scaleFactor = h_c / (float)h_i;
  float scaledWidth = w_i * scaleFactor;
  float filler = Math.Abs(w_c - scaledWidth) / 2;
  unscaled_p.X = (int)((scaledP.X - filler) / scaleFactor);
  unscaled_p.Y = (int)(scaledP.Y / scaleFactor);
 }
 return unscaled_p;
}

2015年7月22日 星期三

[C#] Rotate bitmap with a special angle

public unsafe void Rotate(Bitmap bmp, float angle)
{
  int width=bmp.Width;
  int height=bmp.Height;
 /*
  * right, down = positive
  * p1------------p2
  * |            | 
  * p4------------p3
  * 
  * p1(0,0), p2(width-1,0), p3(width-1,height-1), p4(0,height-1)
  * 
  * In this coordinate system(left-handed coordinate system), clockwise rotation matrix (theta): (cos -sin 
  *                                                                                               sin  cos)
  * 
  */
 //Calculate new vertex
 Point p1 = RotatePoint(new Point(0, 0), angle);
 Point p2 = RotatePoint(new Point(width - 1, 0), angle);
 Point p3 = RotatePoint(new Point(width - 1, height - 1), angle);
 Point p4 = RotatePoint(new Point(0, height - 1), angle);
 //Calculate new size
 int dstWidth = Math.Max(Math.Abs(p3.X - p1.X) + 1, Math.Abs(p4.X - p2.X) + 1);
 int dstHeight = Math.Max(Math.Abs(p3.Y - p1.X) + 1, Math.Abs(p4.Y - p2.Y) + 1);
 /*
  * Calculate offset between old and new coordinate system
  * left-top point in new coordiante system -> (0,0)
  * 
  */
 int offsetX = -new int[4] { p1.X, p2.X, p3.X, p4.X }.Min();
 int offsetY = -new int[4] { p1.Y, p2.Y, p3.Y, p4.Y }.Min();
 //create bmp
 Bitmap dstBitmap = new Bitmap(dstWidth, dstHeight, PixelFormat.Format32bppArgb);
 Rectangle srcRect = new Rectangle(0, 0, width, height);
 Rectangle dstRect = new Rectangle(0, 0, dstWidth, dstHeight);
 BitmapData srcBmpData = bmp.LockBits(srcRect, ImageLockMode.ReadOnly, PixelFormat.Format32bppArgb);
 BitmapData dstBmpData = dstBitmap.LockBits(dstRect, ImageLockMode.WriteOnly, PixelFormat.Format32bppArgb);
 //define sin and cos
 double sin = Math.Sin(angle * Math.PI / 180);
 double cos = Math.Cos(angle * Math.PI / 180);
 int srcStride = srcBmpData.Stride;
 int dstStride = dstBmpData.Stride;
 //define pointer
 byte* srcP = (byte*)srcBmpData.Scan0.ToPointer();
 byte* dstP = (byte*)dstBmpData.Scan0.ToPointer();
 Parallel.For(0,dstHeight,i=>
 {
  Parallel.For(0, dstWidth, j =>
  {
   int k = 4 * j + i * dstStride;
   //Calculate corresponding point in old coordinate system
   Point oldPoint = RotatePoint(new Point(j - offsetX, i - offsetY), -angle);
   if (oldPoint.X >= 0 && oldPoint.X < width && oldPoint.Y >= 0 && oldPoint.Y < height)
   {
    dstP[k] = srcP[4 * oldPoint.X + srcStride * oldPoint.Y];
    dstP[k + 1] = srcP[4 * oldPoint.X + srcStride * oldPoint.Y + 1];
    dstP[k + 2] = srcP[4 * oldPoint.X + srcStride * oldPoint.Y + 2];
    dstP[k + 3] = srcP[4 * oldPoint.X + srcStride * oldPoint.Y + 3];
   }
   else
   {
    dstP[k] = dstP[k + 1] = dstP[k + 2] = 0xff;
    dstP[k + 3] = 0x0;
   }
  });
 });
 bmp.UnlockBits(srcBmpData);
 dstBitmap.UnlockBits(dstBmpData);
 bmp = (Bitmap)dstBitmap.Clone();
 dstBitmap.Dispose();
 width = bmp.Width;
 height = bmp.Height;
}

2015年6月17日 星期三

[Android] commit()與apply()

SharedPreferences.Editor中的commit()與apply()都是向SharedPreferences寫入數據,差別如下︰

commit()︰立刻同步寫入數據到磁碟中的SharedPreferences,並且具有回傳值(boolean)表示成功或失敗

apply()︰API 9之後加入的method,會將欲寫入的數據暫存於記憶體中,進行一個非同步寫入,因此速度較commit()快,但是不具有傳回值

因此假如不需要傳回值的時候,建議使用apply()取代commit()

Android Developer的解釋如下︰
As SharedPreferences instances are singletons within a process, it's safe to replace any instance of commit() with apply() if you were already ignoring the return value.
You don't need to worry about Android component lifecycles and their interaction with apply() writing to disk. The framework makes sure in-flight disk writes from apply() complete before switching states.
 Reference: http://developer.android.com/reference/android/content/SharedPreferences.Editor.html

2015年6月13日 星期六

[Windows] Windows 7 / Windows 8破解使用者密碼

若是連Administrator都被密碼鎖住無法登入,可依照以下方法破解︰

1. 使用任何可開機裝置進入系統(Windows安裝光碟->修復主控台、Windows PE、MS-DOS with NTFS support、Linux),只要可以操作檔案系統即可

2. 輸入以下命令將放大鏡工具改為命令提示字元(Command Prompt)︰

    c: (或是你安裝windows的硬碟代號,不一定是c)
    cd windows\system32
    ren Magnify.exe Magnify1.exe (將原本的放大鏡程式改名)
    ren cmd.exe Magnify.exe (將命令提示字元改名為放大鏡)

3. 重新開機,進入Windows 7登入畫面

4. 點選左下角的「輕鬆存取」,勾選「讓螢幕上的項目放大一些(放大鏡)」,按下「確定」,即可開啟命令提示字元(Command Prompt)

5. 輸入以下命令,創建一個具有系統管理員身份的帳戶︰

    net user test123 /add
    net localgroup administrators test123 /add

    以上兩行命令代表創建一個test123的帳戶,並賦予系統管理員權限

6. 以剛剛創立的test123帳戶即可成功登入系統

7. 輸入以下命令,還原放大鏡工具︰
 
    ren Magnify.exe cmd.exe
    ren Magnify1.exe Magnify.exe

8. 進入「控制台」->「使用者帳戶」-> [原本的使用者] -> 「移除密碼」

9. 登入後即可以原本的使用者登入(不需密碼)

2015年6月10日 星期三

[Android] Custom ListView with SimpleAdapter

若要以SimpleAdapter建立包含RadioButton或是CheckBox的Custom ListView,可依照以下方法︰ 1. 將ListView中將各元件設定為以下屬性
android:focusable="false"
android:focusableInTouchMode="false"
android:clickable="false"

2. 建立一個變數以管理RadioButton或是CheckBox的狀態 

3. 建立ListView的OnItemClickListener方法以管理RadioButton或是CheckBox的狀態

Example:
//Create data
private ArrayList<<HashMap<String, Object>> itemList;

依照以下格式設定資料來源
Title: 主標題
Content: 副標題(內容)
Checked: 核取狀態(true / false)

final SimpleAdapter simpleAdapter = new SimpleAdapter(this,itemList,R.layout.customListView,new String[]{"Title","Content","Checked"},new int[]{R.id.TextView1,R.id.TextView2,R.id.RadioButton1});
simpleAdapter.setViewBinder(new SimpleAdapter.ViewBinder() {
 @Override
 public boolean setViewValue(View view, Object data, String textRepresentation) {
  //Hide empty textView
  if (data == null) {
   view.setVisibility(View.GONE);
   return true;
  }
  view.setVisibility(View.VISIBLE);
  return false;
 }
});
ListView listview = (ListView) findViewById(R.id.ListView1);
listview .setAdapter(simpleAdapter);
listview .setOnItemClickListener(new AdapterView.OnItemClickListener() {
 @Override
 public void onItemClick(AdapterView parent, View view, int position, long id) {
  RadioButton radiobutton1 = (RadioButton) findViewById(R.id.RadioButton1);
  //Save position
  for (HashMap<String, Object> data : itemList)
   data.put("Checked", false); //Clear state
  itemList.get(position).put("Checked", true); //Set current radio button to checked
  simpleAdapter.notifyDataSetChanged();
 }
});

最後需要取得資料時,直接判斷ArrayList中HashMap的Checked欄位即可得知被選取的資料

[Android] 避免ListView高度過高,遮蓋下方元件

在LinearLayout中設定
android:layout_weight="0.9"
即可避免遮蓋下方元件

[Android] 將App從「最近App列表」中隱藏

1. 在AndroidManifest.xml中加入以下屬性
<activity>
  ...
  android:excludeFromRecents="true"
  ...
</activity>
或是以intent啟動activity時加入以下屬性
intent.setFlags(Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
2. 在AndroidManifest.xml中加入以下屬性
<activity>
  ...
  android:label=""
  ...
</activity>