解析vcard文件所需要的pim包

解析Android源代码:EventRecurrence与ContactsAsyncHelper
深入解读Android系统中EventRecurrence类的事件重复解析机制及ContactsAsyncHelper类异步加载联系人图片的方法。

        部分源码:
        EventRecurrence

/*
 * Copyright (C) 2006 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package android.pim;
import android.content.res.Resources;
import android.text.TextUtils;
import android.text.format.Time;
import java.util.Calendar;
public class EventRecurrence
{
    /**
     * Thrown when a recurrence string provided can not be parsed according
     * to RFC2445.
     */
    public static class InvalidFormatException extends RuntimeException
    {
        InvalidFormatException(String s) {
            super(s);
        }
    }
    public EventRecurrence()
    {
        wkst = MO;
    }
   
    /**
     * Parse an iCalendar/RFC2445 recur type according to Section 4.3.10.
     */
    public native void parse(String recur);
    public void setStartDate(Time date) {
        startDate = date;
    }
   
    public static final int SECONDLY = 1;
    public static final int MINUTELY = 2;
    public static final int HOURLY = 3;
    public static final int DAILY = 4;
    public static final int WEEKLY = 5;
    public static final int MONTHLY = 6;
    public static final int YEARLY = 7;
    public static final int SU = 0x00010000;
    public static final int MO = 0x00020000;
    public static final int TU = 0x00040000;
    public static final int WE = 0x00080000;
    public static final int TH = 0x00100000;
    public static final int FR = 0x00200000;
    public static final int SA = 0x00400000;
    public Time      startDate;
    public int       freq;
    public String    until;
    public int       count;
    public int       interval;
    public int       wkst;
    public int[]     bysecond;
    public int       bysecondCount;
    public int[]     byminute;
    public int       byminuteCount;
    public int[]     byhour;
    public int       byhourCount;
    public int[]     byday;
    public int[]     bydayNum;
    public int       bydayCount;  
    public int[]     bymonthday;
    public int       bymonthdayCount;
    public int[]     byyearday;
    public int       byyeardayCount;
    public int[]     byweekno;
    public int       byweeknoCount;
    public int[]     bymonth;
    public int       bymonthCount;
    public int[]     bysetpos;
    public int       bysetposCount;
    /**
     * Converts one of the Calendar.SUNDAY constants to the SU, MO, etc.
     * constants.  btw, I think we should switch to those here too, to
     * get rid of this function, if possible.
     */
    public static int calendarDay2Day(int day)
    {
        switch (day)
        {
            case Calendar.SUNDAY:
                return SU;
            case Calendar.MONDAY:
                return MO;
            case Calendar.TUESDAY:
                return TU;
            case Calendar.WEDNESDAY:
                return WE;
            case Calendar.THURSDAY:
                return TH;
            case Calendar.FRIDAY:
                return FR;
            case Calendar.SATURDAY:
                return SA;
            default:
                throw new RuntimeException("bad day of week: " + day);
        }
    }
   
    public static int timeDay2Day(int day)
    {
        switch (day)
        {
            case Time.SUNDAY:
                return SU;
            case Time.MONDAY:
                return MO;
            case Time.TUESDAY:
                return TU;
            case Time.WEDNESDAY:
                return WE;
            case Time.THURSDAY:
                return TH;
            case Time.FRIDAY:
                return FR;
            case Time.SATURDAY:
                return SA;
            default:
                throw new RuntimeException("bad day of week: " + day);
        }
    }
    public static int day2TimeDay(int day)
    {
        switch (day)
        {
            case SU:
                return Time.SUNDAY;
            case MO:
                return Time.MONDAY;
            case TU:
                return Time.TUESDAY;
            case WE:
                return Time.WEDNESDAY;
            case TH:
                return Time.THURSDAY;
            case FR:
                return Time.FRIDAY;
            case SA:
                return Time.SATURDAY;
            default:
                throw new RuntimeException("bad day of week: " + day);
        }
    }
    /**
     * Converts one of the SU, MO, etc. constants to the Calendar.SUNDAY
     * constants.  btw, I think we should switch to those here too, to
     * get rid of this function, if possible.
     */
    public static int day2CalendarDay(int day)
    {
        switch (day)
        {
            case SU:
                return Calendar.SUNDAY;
            case MO:
                return Calendar.MONDAY;
            case TU:
                return Calendar.TUESDAY;
            case WE:
                return Calendar.WEDNESDAY;
            case TH:
                return Calendar.THURSDAY;
            case FR:
                return Calendar.FRIDAY;
            case SA:
                return Calendar.SATURDAY;
            default:
                throw new RuntimeException("bad day of week: " + day);
        }
    }
   
    /**
     * Converts one of the internal day constants (SU, MO, etc.) to the
     * two-letter string representing that constant.
     *
     * @throws IllegalArgumentException Thrown if the day argument is not one of
     * the defined day constants.
     *
     * @param day one the internal constants SU, MO, etc.
     * @return the two-letter string for the day ("SU", "MO", etc.)
     */
    private static String day2String(int day) {
        switch (day) {
        case SU:
            return "SU";
        case MO:
            return "MO";
        case TU:
            return "TU";
        case WE:
            return "WE";
        case TH:
            return "TH";
        case FR:
            return "FR";
        case SA:
            return "SA";
        default:
            throw new IllegalArgumentException("bad day argument: " + day);
        }
    }
    private static void appendNumbers(StringBuilder s, String label,
                                        int count, int[] values)
    {
        if (count > 0) {
            s.append(label);
            count--;
            for (int i=0; i<count; i++) {
                s.append(values[i]);
                s.append(",");
            }
            s.append(values[count]);
        }
    }
    private void appendByDay(StringBuilder s, int i)
    {
        int n = this.bydayNum[i];
        if (n != 0) {
            s.append(n);
        }
        String str = day2String(this.byday[i]);
        s.append(str);
    }
    @Override
    public String toString()
    {
        StringBuilder s = new StringBuilder();
        s.append("FREQ=");
        switch (this.freq)
        {
            case SECONDLY:
                s.append("SECONDLY");
                break;
            case MINUTELY:
                s.append("MINUTELY");
                break;
            case HOURLY:
                s.append("HOURLY");
                break;
            case DAILY:
                s.append("DAILY");
                break;
            case WEEKLY:
                s.append("WEEKLY");
                break;
            case MONTHLY:
                s.append("MONTHLY");
                break;
            case YEARLY:
                s.append("YEARLY");
                break;
        }
        if (!TextUtils.isEmpty(this.until)) {
            s.append(";UNTIL=");
            s.append(until);
        }
       
        if (this.count != 0) {
            s.append(";COUNT=");
            s.append(this.count);
        }
        if (this.interval != 0) {
            s.append(";INTERVAL=");
            s.append(this.interval);
        }
        if (this.wkst != 0) {
            s.append(";WKST=");
            s.append(day2String(this.wkst));
        }
        appendNumbers(s, ";BYSECOND=", this.bysecondCount, this.bysecond);
        appendNumbers(s, ";BYMINUTE=", this.byminuteCount, this.byminute);
        appendNumbers(s, ";BYSECOND=", this.byhourCount, this.byhour);
        // day
        int count = this.bydayCount;
        if (count > 0) {
            s.append(";BYDAY=");
            count--;
            for (int i=0; i<count; i++) {
                appendByDay(s, i);
                s.append(",");
            }
            appendByDay(s, count);
        }
        appendNumbers(s, ";BYMONTHDAY=", this.bymonthdayCount, this.bymonthday);
        appendNumbers(s, ";BYYEARDAY=", this.byyeardayCount, this.byyearday);
        appendNumbers(s, ";BYWEEKNO=", this.byweeknoCount, this.byweekno);
        appendNumbers(s, ";BYMONTH=", this.bymonthCount, this.bymonth);
        appendNumbers(s, ";BYSETPOS=", this.bysetposCount, this.bysetpos);
        return s.toString();
    }
   
    public String getRepeatString() {
        Resources r = Resources.getSystem();
       
        // TODO Implement "Until" portion of string, as well as custom settings
        switch (this.freq) {
            case DAILY:
                return r.getString(com.android.internal.R.string.daily);
            case WEEKLY: {
                if (repeatsOnEveryWeekDay()) {
                    return r.getString(com.android.internal.R.string.every_weekday);
                } else {
                    String format = r.getString(com.android.internal.R.string.weekly);
                    StringBuilder days = new StringBuilder();
               
                    // Do one less iteration in the loop so the last element is added out of the
                    // loop. This is done so the comma is not placed after the last item.
                    int count = this.bydayCount - 1;
                    if (count >= 0) {
                        for (int i = 0 ; i < count ; i++) {
                            days.append(dayToString(r, this.byday[i]));
                            days.append(",");
                        }
                        days.append(dayToString(r, this.byday[count]));
                   
                        return String.format(format, days.toString());
                    }
                   
                    // There is no "BYDAY" specifier, so use the day of the
                    // first event.  For this to work, the setStartDate()
                    // method must have been used by the caller to set the
                    // date of the first event in the recurrence.
                    if (startDate == null) {
                        return null;
                    }
                   
                    int day = timeDay2Day(startDate.weekDay);
                    return String.format(format, dayToString(r, day));
                }
            }
            case MONTHLY: {
                return r.getString(com.android.internal.R.string.monthly);
            }
            case YEARLY:
                return r.getString(com.android.internal.R.string.yearly);
        }
        return null;
    }
   
    public boolean repeatsOnEveryWeekDay() {
        if (this.freq != WEEKLY) {
            return false;
        }
       
        int count = this.bydayCount;
        if (count != 5) {
            return false;
        }
       
        for (int i = 0 ; i < count ; i++) {
            int day = byday[i];
            if (day == SU || day == SA) {
                return false;
            }
        }
       
        return true;
    }
   
    public boolean repeatsMonthlyOnDayCount() {
        if (this.freq != MONTHLY) {
            return false;
        }
       
        if (bydayCount != 1 || bymonthdayCount != 0) {
            return false;
        }
       
        return true;
    }
   
    private String dayToString(Resources r, int day) {
        switch (day) {
        case SU: return r.getString(com.android.internal.R.string.day_of_week_long_sunday);
        case MO: return r.getString(com.android.internal.R.string.day_of_week_long_monday);
        case TU: return r.getString(com.android.internal.R.string.day_of_week_long_tuesday);
        case WE: return r.getString(com.android.internal.R.string.day_of_week_long_wednesday);
        case TH: return r.getString(com.android.internal.R.string.day_of_week_long_thursday);
        case FR: return r.getString(com.android.internal.R.string.day_of_week_long_friday);
        case SA: return r.getString(com.android.internal.R.string.day_of_week_long_saturday);
        default: throw new IllegalArgumentException("bad day argument: " + day);
        }
    }
}
        ContactsAsyncHelper
/*
 * Copyright (C) 2008 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package android.pim;
import com.android.internal.telephony.CallerInfo;
import com.android.internal.telephony.Connection;
import android.content.ContentUris;
import android.content.Context;
import android.graphics.drawable.Drawable;
import android.net.Uri;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.Looper;
import android.os.Message;
import android.provider.ContactsContract.Contacts;
import android.util.Log;
import android.view.View;
import android.widget.ImageView;
import java.io.InputStream;
/**
 * Helper class for async access of images.
 */
public class ContactsAsyncHelper extends Handler {
    private static final boolean DBG = false;
    private static final String LOG_TAG = "ContactsAsyncHelper";
    /**
     * Interface for a WorkerHandler result return.
     */
    public interface OnImageLoadCompleteListener {
        /**
         * Called when the image load is complete.
         *
         * @param imagePresent true if an image was found
         */
        public void onImageLoadComplete(int token, Object cookie, ImageView iView,
                boolean imagePresent);
    }
    // constants
    private static final int EVENT_LOAD_IMAGE = 1;
    private static final int DEFAULT_TOKEN = -1;
    // static objects
    private static Handler sThreadHandler;
    private static ContactsAsyncHelper sInstance;
    static {
        sInstance = new ContactsAsyncHelper();
    }
    private static final class WorkerArgs {
        public Context context;
        public ImageView view;
        public Uri uri;
        public int defaultResource;
        public Object result;
        public Object cookie;
        public OnImageLoadCompleteListener listener;
        public CallerInfo info;
    }
    /**
     * public inner class to help out the ContactsAsyncHelper callers
     * with tracking the state of the CallerInfo Queries and image
     * loading.
     *
     * Logic contained herein is used to remove the race conditions
     * that exist as the CallerInfo queries run and mix with the image
     * loads, which then mix with the Phone state changes.
     */
    public static class ImageTracker {
        // Image display states
        public static final int DISPLAY_UNDEFINED = 0;
        public static final int DISPLAY_IMAGE = -1;
        public static final int DISPLAY_DEFAULT = -2;
        // State of the image on the imageview.
        private CallerInfo mCurrentCallerInfo;
        private int displayMode;
        public ImageTracker() {
            mCurrentCallerInfo = null;
            displayMode = DISPLAY_UNDEFINED;
        }
        /**
         * Used to see if the requested call / connection has a
         * different caller attached to it than the one we currently
         * have in the CallCard.
         */
        public boolean isDifferentImageRequest(CallerInfo ci) {
            // note, since the connections are around for the lifetime of the
            // call, and the CallerInfo-related items as well, we can
            // definitely use a simple != comparison.
            return (mCurrentCallerInfo != ci);
        }
        public boolean isDifferentImageRequest(Connection connection) {
            // if the connection does not exist, see if the
            // mCurrentCallerInfo is also null to match.
            if (connection == null) {
                if (DBG) Log.d(LOG_TAG, "isDifferentImageRequest: connection is null");
                return (mCurrentCallerInfo != null);
            }
            Object o = connection.getUserData();
            // if the call does NOT have a callerInfo attached
            // then it is ok to query.
            boolean runQuery = true;
            if (o instanceof CallerInfo) {
                runQuery = isDifferentImageRequest((CallerInfo) o);
            }
            return runQuery;
        }
        /**
         * Simple setter for the CallerInfo object.
         */
        public void setPhotoRequest(CallerInfo ci) {
            mCurrentCallerInfo = ci;
        }
        /**
         * Convenience method used to retrieve the URI
         * representing the Photo file recorded in the attached
         * CallerInfo Object.
         */
        public Uri getPhotoUri() {
            if (mCurrentCallerInfo != null) {
                return ContentUris.withAppendedId(Contacts.CONTENT_URI,
                        mCurrentCallerInfo.person_id);
            }
            return null;
        }
        /**
         * Simple setter for the Photo state.
         */
        public void setPhotoState(int state) {
            displayMode = state;
        }
        /**
         * Simple getter for the Photo state.
         */
        public int getPhotoState() {
            return displayMode;
        }
    }
    /**
     * Thread worker class that handles the task of opening the stream and loading
     * the images.
     */
    private class WorkerHandler extends Handler {
        public WorkerHandler(Looper looper) {
            super(looper);
        }
        @Override
        public void handleMessage(Message msg) {
            WorkerArgs args = (WorkerArgs) msg.obj;
            switch (msg.arg1) {
                case EVENT_LOAD_IMAGE:
                    InputStream inputStream = null;
                    try {
                        inputStream = Contacts.openContactPhotoInputStream(
                                args.context.getContentResolver(), args.uri);
                    } catch (Exception e) {
                        Log.e(LOG_TAG, "Error opening photo input stream", e);
                    }
                    if (inputStream != null) {
                        args.result = Drawable.createFromStream(inputStream, args.uri.toString());
                        if (DBG) Log.d(LOG_TAG, "Loading image: " + msg.arg1 +
                                " token: " + msg.what + " image URI: " + args.uri);
                    } else {
                        args.result = null;
                        if (DBG) Log.d(LOG_TAG, "Problem with image: " + msg.arg1 +
                                " token: " + msg.what + " image URI: " + args.uri +
                                ", using default image.");
                    }
                    break;
                default:
            }
            // send the reply to the enclosing class.
            Message reply = ContactsAsyncHelper.this.obtainMessage(msg.what);
            reply.arg1 = msg.arg1;
            reply.obj = msg.obj;
            reply.sendToTarget();
        }
    }
    /**
     * Private constructor for static class
     */
    private ContactsAsyncHelper() {
        HandlerThread thread = new HandlerThread("ContactsAsyncWorker");
        thread.start();
        sThreadHandler = new WorkerHandler(thread.getLooper());
    }
    /**
     * Convenience method for calls that do not want to deal with listeners and tokens.
     */
    public static final void updateImageViewWithContactPhotoAsync(Context context,
            ImageView imageView, Uri person, int placeholderImageResource) {
        // Added additional Cookie field in the callee.
        updateImageViewWithContactPhotoAsync (null, DEFAULT_TOKEN, null, null, context,
                imageView, person, placeholderImageResource);
    }
    /**
     * Convenience method for calls that do not want to deal with listeners and tokens, but have
     * a CallerInfo object to cache the image to.
     */
    public static final void updateImageViewWithContactPhotoAsync(CallerInfo info, Context context,
            ImageView imageView, Uri person, int placeholderImageResource) {
        // Added additional Cookie field in the callee.
        updateImageViewWithContactPhotoAsync (info, DEFAULT_TOKEN, null, null, context,
                imageView, person, placeholderImageResource);
    }

    /**
     * Start an image load, attach the result to the specified CallerInfo object.
     * Note, when the query is started, we make the ImageView INVISIBLE if the
     * placeholderImageResource value is -1.  When we're given a valid (!= -1)
     * placeholderImageResource value, we make sure the image is visible.
     */
    public static final void updateImageViewWithContactPhotoAsync(CallerInfo info, int token,
            OnImageLoadCompleteListener listener, Object cookie, Context context,
            ImageView imageView, Uri person, int placeholderImageResource) {
        // in case the source caller info is null, the URI will be null as well.
        // just update using the placeholder image in this case.
        if (person == null) {
            if (DBG) Log.d(LOG_TAG, "target image is null, just display placeholder.");
            imageView.setVisibility(View.VISIBLE);
            imageView.setImageResource(placeholderImageResource);
            return;
        }
        // Added additional Cookie field in the callee to handle arguments
        // sent to the callback function.
        // setup arguments
        WorkerArgs args = new WorkerArgs();
        args.cookie = cookie;
        args.context = context;
        args.view = imageView;
        args.uri = person;
        args.defaultResource = placeholderImageResource;
        args.listener = listener;
        args.info = info;
        // setup message arguments
        Message msg = sThreadHandler.obtainMessage(token);
        msg.arg1 = EVENT_LOAD_IMAGE;
        msg.obj = args;
        if (DBG) Log.d(LOG_TAG, "Begin loading image: " + args.uri +
                ", displaying default image for now.");
        // set the default image first, when the query is complete, we will
        // replace the image with the correct one.
        if (placeholderImageResource != -1) {
            imageView.setVisibility(View.VISIBLE);
            imageView.setImageResource(placeholderImageResource);
        } else {
            imageView.setVisibility(View.INVISIBLE);
        }
        // notify the thread to begin working
        sThreadHandler.sendMessage(msg);
    }
    /**
     * Called when loading is done.
     */
    @Override
    public void handleMessage(Message msg) {
        WorkerArgs args = (WorkerArgs) msg.obj;
        switch (msg.arg1) {
            case EVENT_LOAD_IMAGE:
                boolean imagePresent = false;
                // if the image has been loaded then display it, otherwise set default.
                // in either case, make sure the image is visible.
                if (args.result != null) {
                    args.view.setVisibility(View.VISIBLE);
                    args.view.setImageDrawable((Drawable) args.result);
                    // make sure the cached photo data is updated.
                    if (args.info != null) {
                        args.info.cachedPhoto = (Drawable) args.result;
                    }
                    imagePresent = true;
                } else if (args.defaultResource != -1) {
                    args.view.setVisibility(View.VISIBLE);
                    args.view.setImageResource(args.defaultResource);
                }
                // Note that the data is cached.
                if (args.info != null) {
                    args.info.isCachedPhotoCurrent = true;
                }
                // notify the listener if it is there.
                if (args.listener != null) {
                    if (DBG) Log.d(LOG_TAG, "Notifying listener: " + args.listener.toString() +
                            " image: " + args.uri + " completed");
                    args.listener.onImageLoadComplete(msg.what, args.cookie, args.view,
                            imagePresent);
                }
                break;
            default:
        }
    }
}




26112647_5fiF.jpg
转载:http://www.adobex.com/android/source/details/00000410.htm

转载于:https://my.oschina.net/androidcode/blog/105074

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值