2013年8月9日 星期五

Android 開發筆記 - 依照檔案 Content-type / MIME Type 開啓對應 app

專門的 app 一定會希望無時無刻把使用者留在自己家裡,但是什麼都自己來真的太累了 XD 所以,處理特定 Content-type 檔案時,就乾脆叫出系統內其他 app 來負責吧!鄉民用語:「閃開!讓專業的來!」

File file = new File("/sdcard/path/file");
Intent mIntent = new Intent();
mIntent.setAction(Intent.ACTION_VIEW);
mIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
mIntent.setDataAndType(Uri.fromFile(file), "image/jpeg");
startActivity(mIntent);


上述建立在自己知道該檔案類型,若不知道的話,就先用其他方式找吧!

此外,連續用上述方式開啓 3 次檔案,就必須連按三次 back 鍵才能離開外部 app 回到自家 app,這時可以透過 Intent.FLAG_ACTIVITY_NO_HISTORY 來限制外部 app 一離開前景就不進 history ,方便按一下 back 就回去自家 app 啦

mIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK|Intent.FLAG_ACTIVITY_NO_HISTORY);

Android 開發筆記 - 從 Android Service 發動 startActivity

若是只是單純從一個 Activity 發動另一個 Activity:

startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse("http://www.google.com.tw")));

若是從 Service 發動 StartActivity 則需須指定 Intent.FLAG_ACTIVITY_NEW_TASK 方可正常運行:

Intent mIntent = new Intent(Intent.ACTION_VIEW, Uri.parse("http://www.google.com.tw"));
mIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(mIntent);

[Linux] 解決 apt-get 之 gzip: stdout: No space left on device 問題 @ Ubuntu 12.04 Server

噴訊息:

gzip: stdout: No space left on device
E: mkinitramfs failure cpio 141 gzip 1
update-initramfs: failed for /boot/initrd.img-#.#.#-##-generic with 1.
dpkg: error processing initramfs-tools (--configure):
 subprocess installed post-installation script returned error exit status 1
Errors were encountered while processing:
 initramfs-tools
E: Sub-process /usr/bin/dpkg returned an error code (1)

接著查看 /boot:

$ df  -h
Filesystem              Size  Used Avail Use% Mounted on
...
/dev/sda1               228M  218M     0 100% /boot


解法:

$ uname -a
Linux 3.2.0-51-generic #77-Ubuntu SMP Wed Jul 24 20:18:19 UTC 2013 x86_64 x86_64 x86_64 GNU/Linux

代表現在跑 Linux 3.2.0-51 也算正常吧,那就把其他清掉吧

$ dpkg -l 'linux-image-*' | grep '^ii'
ii  linux-image-3.2.0-29-generic        3.2.0-29.46                         Linux kernel image for version 3.2.0 on 64 bit x86 SMP
ii  linux-image-3.2.0-35-generic        3.2.0-35.55                         Linux kernel image for version 3.2.0 on 64 bit x86 SMP
ii  linux-image-3.2.0-37-generic        3.2.0-37.58                         Linux kernel image for version 3.2.0 on 64 bit x86 SMP
ii  linux-image-3.2.0-38-generic        3.2.0-38.61                         Linux kernel image for version 3.2.0 on 64 bit x86 SMP
ii  linux-image-3.2.0-39-generic        3.2.0-39.62                         Linux kernel image for version 3.2.0 on 64 bit x86 SMP
ii  linux-image-3.2.0-40-generic        3.2.0-40.64                         Linux kernel image for version 3.2.0 on 64 bit x86 SMP
ii  linux-image-3.2.0-41-generic        3.2.0-41.66                         Linux kernel image for version 3.2.0 on 64 bit x86 SMP
ii  linux-image-3.2.0-43-generic        3.2.0-43.68                         Linux kernel image for version 3.2.0 on 64 bit x86 SMP
ii  linux-image-3.2.0-44-generic        3.2.0-44.69                         Linux kernel image for version 3.2.0 on 64 bit x86 SMP
ii  linux-image-3.2.0-45-generic        3.2.0-45.70                         Linux kernel image for version 3.2.0 on 64 bit x86 SMP
ii  linux-image-3.2.0-48-generic        3.2.0-48.74                         Linux kernel image for version 3.2.0 on 64 bit x86 SMP
ii  linux-image-3.2.0-49-generic        3.2.0-49.75                         Linux kernel image for version 3.2.0 on 64 bit x86 SMP
ii  linux-image-3.2.0-51-generic        3.2.0-51.77                         Linux kernel image for version 3.2.0 on 64 bit x86 SMP
ii  linux-image-server                  3.2.0.51.61                         Linux kernel image on Server Equipment

開始慢慢清掉之前裝過的:(此例還保留前一份linux-image-3.2.0-49-generic這份)

$ sudo apt-get purge linux-image-3.2.0-29-generic linux-image-3.2.0-35-generic linux-image-3.2.0-37-generic linux-image-3.2.0-38-generic linux-image-3.2.0-39-generic linux-image-3.2.0-40-generic linux-image-3.2.0-41-generic linux-image-3.2.0-43-generic linux-image-3.2.0-44-generic linux-image-3.2.0-45-generic  linux-image-3.2.0-48-generic

再次查看: 

$ df -h
Filesystem              Size  Used Avail Use% Mounted on
...
/dev/sda1               228M   51M  165M  24% /boot


收工

2013年8月8日 星期四

Android 開發筆記 - 開機自動啓動 Android Service

開機自動啓動主要是指 Android service 的部分,整體上 Android 有非常豐富的事件管理,想要開機啓動就只需要去接收開機事件,收到後在執行 service 即可,架構:

  • 實作事件接收 public class MyEventsReceiver extends BroadcastReceiver
  • 設定 AndroidManifest.xml 監聽的事件,部分事件是需要額外權限的,安裝軟體時會顯示給使用者

BroadcastReceiver:

package org.changyy.study;
public class MyEventsReceiver extends BroadcastReceiver {

Intent MyService;
@Override
public void onReceive(Context context, Intent intent) {
// TODO Auto-generated method stub
String action = intent.getAction();
if( action.compareTo("android.intent.action.BOOT_COMPLETED") == 0 ) {
MyService = new Intent(context, MyService.class);
   context.startService(MyService);
}
}
}


AndroidManifest.xml:

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

<application
    android:icon="@drawable/ic_launcher"
    android:label="@string/app_name"
    android:theme="@style/AppTheme" >

    <service
        android:name="org.changyy.study.MyService"
        android:process=":MyService">
    </service>
    <receiver android:name="org.changyy.study.MyEventsReceiver">
        <intent-filter>
            <action android:name="android.intent.action.BOOT_COMPLETED"></action>
            <action android:name="android.intent.action.MEDIA_MOUNTED"/>
            <action android:name="android.intent.action.MEDIA_UNMOUNTED"/>
            <action android:name="android.intent.action.MEDIA_BAD_REMOVAL"/>
        </intent-filter>
    </receiver>
</application>


此例除了開機事件(android.intent.action.BOOT_COMPLETED)外,順便監聽了外部儲存媒體的插拔。簡單的說,如果要監聽一排的事件,就只要在 AndroidManifest.xml 填寫完後,在 MyEventsReceiver 裡的 onReceive 判斷,若每個事件都要做一樣的事則不用判斷去執行指定項目即可,若需要判斷可透過 intent.getAction() 處理。

2013年8月7日 星期三

Android 開發筆記 - 執行 script 筆記,特別是會處理 stdin 的 script 案例

最近在處理 LOA 開機啓動的設定,發現在 Android app 可以這樣就呼叫 sh 來執行 script 程式:

try {
Process mProcess = Runtime.getRuntime().exec("sh");
DataOutputStream mDataOutputStream = new DataOutputStream(mProcess.getOutputStream());
mDataOutputStream.writeBytes("sh /sdcard/test.sh > /sdcard/run.log \n");
mDataOutputStream.flush();
mDataOutputStream.close();
mProcess.waitFor();
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}


而此例 /sdcard/test.sh 為:

# cat test.sh
echo "Begin"
date

read something
#while [ 1 ] ;
#do
#        echo -n "."
#done

echo "End"
date


上述在 Android app 執行後,可以看到 /sdcard/run.log 顯示:

Begin
Wed Aug  7 21:54:13 CST 2013
End
Wed Aug  7 21:54:13 CST 2013


發現跑起來後,又緊接著關掉,追了很久才發現是 mDataOutputStream.close() 的影響 Orz 比較安全的解法是把 mProcess 和 mDataOutputStream 拉倒 class variable 等級,可隨著物件(此例為 Activity)存在而不被釋放:

public class TestActivity extends Activity {
Process mProcess;
DataOutputStream mDataOutputStream;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
try {
mProcess = Runtime.getRuntime().exec("sh");
mDataOutputStream = new DataOutputStream(mProcess.getOutputStream());
mDataOutputStream.writeBytes("sh /sdcard/test.sh > /sdcard/run.log \n");
mDataOutputStream.flush();
//mDataOutputStream.close();
//mProcess.waitFor();
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
// …
}


回到對 LOA 的問題,是在於執行 sh 後,再透過 chroot 後就交給一隻 /root/init.sh  處理,而它會等 stdin 的資料,而 mDataOutputStream.close() 的結果就像給予 exit/logout/EOF 的現象,使得 LOA 一直起來後馬上又關掉了。