部分源码:
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:
}
}
}

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