2012年1月12日 星期四

Android 開發筆記 - 取得 Calendar Events 與相關筆記

最近摸到一台變形金剛,順便學習一些簡單的東西,這次目標就是取得 Calendar Events,對整個 Android 系統來說,有不少 App 都提供讓人存取的功能,在此以 Calendar 為例。


另外一提的,很多資料都是用 SQLite 格式儲存在系統裡頭,可以透過 DDMS 或其他 shell 方式瀏覽或取出 db 檔案(/data/data/com.android.providers.calendar/databases/calendar.db),接著可以用 Firefox Plugin - SQLite Manager 去查看也挺方便喔。(此動作需要root權限)


程式碼:


package tw.study.provider.calendar;


import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.Hashtable;


import android.app.Activity;
import android.content.ContentResolver;
import android.database.Cursor;
import android.net.Uri;
import android.os.Bundle;
import android.text.format.DateUtils;
import android.util.Log;


public class CalendarEventProviderActivity extends Activity {
    /** Called when the activity is first created. */
    private String tagReport = "CalendarProviderActivity";
    private ArrayList<String> calendarEvents = new ArrayList<String>();
    private Hashtable<String,String> accountInfo = new Hashtable<String,String>();
    private String URI_CALENDAR_BASE = "content://com.android.calendar/calendars";
    private String URI_CALENDAR_RANGE_QUERY = "content://com.android.calendar/instances/when"; // content://com.android.calendar/instances/when/begin/end
    private SimpleDateFormat TimeFormat = new SimpleDateFormat("HH:mm");

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        if(getInfoFromCalendarDB()>0) {
                // do something via calendarEvents
        }
    }

    private int getInfoFromCalendarDB() {
        long dateRangeBegin = new Date().getTime();
        Date today = new Date(dateRangeBegin + DateUtils.DAY_IN_MILLIS);
        long dateRangeEnd=new Date(today.getYear(),today.getMonth(),today.getDate()).getTime();
        
        Log.e(tagReport,"date range from '"+dateRangeBegin +"' to '"+dateRangeEnd+"'");

        ContentResolver contentResolver = this.getContentResolver();
        Cursor accountInfoCursor = contentResolver.query(Uri.parse(URI_CALENDAR_BASE), (new String[] { "_id", "displayName", "selected", "_sync_account" }), null, null, null);
        while (accountInfoCursor.moveToNext()) {
            String _id = accountInfoCursor.getString(0);
            String displayName = accountInfoCursor.getString(1);
            String syncAccount = accountInfoCursor.getString(3);
            if( !accountInfoCursor.getString(2).equals("0") ) { // selected => enable
                accountInfo.put( syncAccount, displayName ); // use for check or display the account name
                accountInfo.put( _id, displayName );
            }
        }

        // show all columns
        String targetQuery = URI_CALENDAR_RANGE_QUERY+"/"+dateRangeBegin+"/"+dateRangeEnd;
        Log.e(tagReport,"Range Query:"+targetQuery);
        Cursor eventCursor = contentResolver.query(Uri.parse(targetQuery), null, null, null, "startDay ASC, startMinute ASC");
        for( String c : eventCursor.getColumnNames() ) 
            Log.e(tagReport,"column name:"+c);

        // add all events
        calendarEvents.clear();
        eventCursor = contentResolver.query(Uri.parse(URI_CALENDAR_RANGE_QUERY+"/"+dateRangeBegin+"/"+dateRangeEnd), new String[] { "title", "begin", "end", "calendar_id"}, null,null,"startDay ASC, startMinute ASC");
        while (eventCursor.moveToNext()) {
            String title = eventCursor.getString(0);
            Long begin = eventCursor.getLong(1);
            Long end = eventCursor.getLong(2);
            String accountId = eventCursor.getString(3);
                if( accountInfo.containsKey(accountId) ) { // skip the account which is not enabled
                calendarEvents.add("["+TimeFormat.format(new Date(begin))+"-"+TimeFormat.format(new Date(end))+"] "+title+"("+accountInfo.get(accountId)+")" );
            }
        }
        return calendarEvents.size();
    }


AndroidManifest.xml:


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


輸出:


date range from '1326368807153' to '1326384000000'
Range Query:content://com.android.calendar/instances/when/1326368807153/1326384000000
column name:originalEvent
column name:ownerAccount
column name:endDay
column name:visibility
column name:endMinute
column name:rrule
column name:event_id
column name:lastDate
column name:hasAlarm
column name:guestsCanModify
column name:transparency
column name:rdate
column name:exrule
column name:guestsCanSeeGuests
column name:title
column name:dtstart
column name:selected
column name:timezone
column name:_id
column name:hasAttendeeData
column name:commentsUri
column name:startDay
column name:description
column name:htmlUri
column name:end
column name:startMinute
column name:hasExtendedProperties
column name:calendar_id
column name:eventLocation
column name:dtend
column name:access_level
column name:allDay
column name:organizer
column name:originalAllDay
column name:deleted
column name:url
column name:begin
column name:originalInstanceTime
column name:duration
column name:color
column name:selfAttendeeStatus
column name:guestsCanInviteOthers
column name:exdate
column name:eventStatus
column name:eventTimezone


2012年1月6日 星期五

[Linux] 忘記密碼、更新 root 密碼的處理 @ Ubuntu 11.10

前陣子接手案子,但同事離職前程式碼沒有交接清楚,所以我就稍微黑一下前同事的桌機了 XD 提到破解密碼,過去總有一些電影場景,厲害的黑客在黑幫老大的壓力下(或正妹的服務下),只靠一台筆電跟網路就突破哪台銀行主機等等的,說真的還滿誇張的,最近則看了不可能的任務四,發現內容越來越寫實,真正的破解方式就是要到主機身邊給他本體入侵一下啦!


所以,其實忘記密碼或是想要強行入侵,只要在本機端就可以了 :P 過去我只有用過 FreeBSD 的 Single User Mode 啦,這次算是體驗一下 Ubuntu/Linux 環境:



  1. 重開機進入 Single User Mode (Recovery Mode)

  2. 用 root 將 FS 狀態改寫成可讀寫


    • $ mount -o remount-rw /


  3. 強制更換密碼


    • $ passwd your_id



這次就完成了 XD 比較可能卡住的是第一步跟第二步,像我就搞不懂怎樣進入 Single User Mode。另外,將檔案系統改成可讀寫後,修改的帳密才會儲存起來。最後,我就只是單純地把前輩的密碼換掉,再用他的帳號登入而已 :P 事實也證明,不要以為有帳密管理就安全,一旦主機暴露出來還是一樣很危險。目前我還沒學會的是處理 Windows 系統的部分。

2011年12月28日 星期三

明天的事?就交給明天來煩惱!


工作告一個段落了。今年意外接了兩位前輩的離職工作,原先我已經被分配全時做某個工作了,接了別人離職的工作,等同被加了 2 個人以上的工作量,這真的不是件好事。慶幸的,最後我也走出陰霾。


經過這次體驗,讓我學到不少事,例如經歷一些行政精華啊,也從中強化一些意志,算起來也稱得上大收穫囉 :D


2011年12月18日 星期日

2011-12-31前,記得辦一下中華電信「我的熱線」

前陣子收到不少網路文章,說中華電信在衝市占率?2011年底前申辦「我的熱線」可以享用一組免設定費,且24個月免月租費的服務。


然而,一堆文章都只提到"免設定費"、"免月租費"的訊息,那我在 emome 網站上卻看到"我的熱線"其實是以秒計費的,所以很擔心這只是文字噱頭,最後打電話問客服小姐,才確定這是 2011-11-01 至 2011-12-31 期間,申辦的優惠方案可以"免通話費"喔。目前優惠方案是設定第一組免設定費、免月租費的,假設你的門號設定兩組,那第一組免設定免月租,第二組則是要設定費 50 元且每個月都要 50 元月租費,同理第三、第四和第五組,都是要收月租費跟設定費的。所以,至少去設定一組吧!兩年內打電話都不用錢,何樂而不為?


簡言之,在 2011-12-31 前,到中華電信服務中心攜帶"雙證件"辦理「我的熱線」,可享有一組熱線號碼 24 個月完全免費(免設定費、免月租費、免通話費)。假設是在 2011-12-17 時,A門號設定打給 B 後,過不久就會收到系統寄來的簡訊,接著在 2013-12-17(18?) 前,從 A 打給 B 都是免費的。


這麼好的大放送,記得去辦一下啊。有任何問題可以用手機播打 0800-080-123 詢問客服人員,另外,CHT 週六、日營業服務據點可以參考一下,我是挑服務中心而不是特約的,有問題可以先問看看特約店是不是可以辦理。


相關連結:



2011年12月8日 星期四

繞路,沒啥不好的

竹東火車站


今晚挑了一間不曾去過的火車站,到那邊兌換車票,碰到了不熟票務系統的站長,大概是正逢早班晚班交接吧,看著他小慌地處理事情,我倒覺得有股溫馨的氣氛,不用著急外頭的車是否擋到別人,因為此刻的車站人煙稀少。我喜歡這樣的步調,甚至舉一下手跳一下就可以摸到車站外的天花板呢。



前陣子幾天假到處晃晃,終點站拜訪了恩師,恩師看到我的體型就跟我說有空繞路走走也不錯,然而,這「繞路」卻也點醒我的心境。周末順便看了「翻滾吧!阿信」,看完電影後上個廁所,連牆上都寫著:


證嚴法師:「信心、毅力、勇氣,三者具備,天下沒有做不成的事。


是啊,其實有時不會太在意別人的眼光,就像 Steve Jobs 自傳裡寫的那句話,每天當作最後一天,在那個情境下,別人的眼光、壓力都將顯得膚淺的。好好把認為有價值得事,從頭到尾地實踐吧,真正對你自己負責的,還是你自己啊。