Change package name.

This commit is contained in:
Suhail Alkowaileet 2015-09-04 16:34:27 +03:00
parent e3ee56b0d5
commit 82c14f0be5
82 changed files with 4141 additions and 4457 deletions

View File

@ -18,10 +18,10 @@
*/
-->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="org.sufficientlysecure.standalonecalendar"
package="ws.xsoh.etar"
android:installLocation="auto"
android:versionCode="3"
android:versionName="standalone-1.2" >
android:versionCode="4"
android:versionName="1.0">
<!-- android:sharedUserLabel="@string/app_label"> -->
<!--

View File

@ -15,7 +15,7 @@ def findReplace(directory, find, replace, filePattern):
findReplace("res", r"(<string name=\"custom\" product=\"tablet\".*>).*(</string>)", r"", "strings.xml")
# change import of generated R file to fix packagename
findReplace("src", r"import com.android.calendar.R;", r"import org.sufficientlysecure.standalonecalendar.R;", "*.java")
findReplace("src", r"import com.android.calendar.R;", r"import ws.xsoh.etar.R;", "*.java")
# add R import to com.android.calendar
findReplace("src", r"package com.android.calendar;", r"package com.android.calendar;\n\nimport org.sufficientlysecure.standalonecalendar.R;", "*.java")
findReplace("src", r"package com.android.calendar;", r"package com.android.calendar;\n\nimport ws.xsoh.etar.R;", "*.java")

View File

@ -16,15 +16,13 @@
package com.android.calendar;
import org.sufficientlysecure.standalonecalendar.R;
import android.app.Activity;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager.NameNotFoundException;
import android.os.Bundle;
import android.preference.PreferenceFragment;
import org.sufficientlysecure.standalonecalendar.R;
import ws.xsoh.etar.R;
public class AboutPreferences extends PreferenceFragment {
private static final String BUILD_VERSION = "build_version";

View File

@ -76,13 +76,13 @@ import com.android.calendar.agenda.AgendaFragment;
import com.android.calendar.month.MonthByWeekFragment;
import com.android.calendar.selectcalendars.SelectVisibleCalendarsFragment;
import org.sufficientlysecure.standalonecalendar.R;
import java.io.IOException;
import java.util.List;
import java.util.Locale;
import java.util.TimeZone;
import ws.xsoh.etar.R;
import static android.provider.CalendarContract.Attendees.ATTENDEE_STATUS;
import static android.provider.CalendarContract.EXTRA_EVENT_ALL_DAY;
import static android.provider.CalendarContract.EXTRA_EVENT_BEGIN_TIME;
@ -192,15 +192,6 @@ public class AllInOneActivity extends AbstractCalendarActivity implements EventH
private MenuItem mControlsMenu;
private Menu mOptionsMenu;
private QueryHandler mHandler;
// runs every midnight/time changes and refreshes the today icon
private final Runnable mTimeChangesUpdater = new Runnable() {
@Override
public void run() {
mTimeZone = Utils.getTimeZone(AllInOneActivity.this, mHomeTimeUpdater);
AllInOneActivity.this.invalidateOptionsMenu();
Utils.setMidnightUpdater(mHandler, mTimeChangesUpdater, mTimeZone);
}
};
private final Runnable mHomeTimeUpdater = new Runnable() {
@Override
public void run() {
@ -210,6 +201,15 @@ public class AllInOneActivity extends AbstractCalendarActivity implements EventH
Utils.setMidnightUpdater(mHandler, mTimeChangesUpdater, mTimeZone);
}
};
// runs every midnight/time changes and refreshes the today icon
private final Runnable mTimeChangesUpdater = new Runnable() {
@Override
public void run() {
mTimeZone = Utils.getTimeZone(AllInOneActivity.this, mHomeTimeUpdater);
AllInOneActivity.this.invalidateOptionsMenu();
Utils.setMidnightUpdater(mHandler, mTimeChangesUpdater, mTimeZone);
}
};
private boolean mCheckForAccounts = true;
private String mHideString;
private String mShowString;

View File

@ -16,8 +16,6 @@
package com.android.calendar;
import org.sufficientlysecure.standalonecalendar.R;
import android.content.Context;
import android.view.Menu;
import android.view.MenuItem;

View File

@ -16,10 +16,6 @@
package com.android.calendar;
import org.sufficientlysecure.standalonecalendar.R;
import com.android.calendar.AsyncQueryServiceHelper.OperationInfo;
import android.content.ContentProviderOperation;
import android.content.ContentProviderResult;
import android.content.ContentResolver;
@ -31,6 +27,8 @@ import android.os.Handler;
import android.os.Message;
import android.util.Log;
import com.android.calendar.AsyncQueryServiceHelper.OperationInfo;
import java.util.ArrayList;
import java.util.concurrent.atomic.AtomicInteger;
@ -46,71 +44,14 @@ import java.util.concurrent.atomic.AtomicInteger;
* application which serializes all the calls.
*/
public class AsyncQueryService extends Handler {
private static final String TAG = "AsyncQuery";
static final boolean localLOGV = false;
private static final String TAG = "AsyncQuery";
// Used for generating unique tokens for calls to this service
private static AtomicInteger mUniqueToken = new AtomicInteger(0);
private Context mContext;
private Handler mHandler = this; // can be overridden for testing
/**
* Data class which holds into info of the queued operation
*/
public static class Operation {
static final int EVENT_ARG_QUERY = 1;
static final int EVENT_ARG_INSERT = 2;
static final int EVENT_ARG_UPDATE = 3;
static final int EVENT_ARG_DELETE = 4;
static final int EVENT_ARG_BATCH = 5;
/**
* unique identify for cancellation purpose
*/
public int token;
/**
* One of the EVENT_ARG_ constants in the class describing the operation
*/
public int op;
/**
* {@link SystemClock.elapsedRealtime()} based
*/
public long scheduledExecutionTime;
protected static char opToChar(int op) {
switch (op) {
case Operation.EVENT_ARG_QUERY:
return 'Q';
case Operation.EVENT_ARG_INSERT:
return 'I';
case Operation.EVENT_ARG_UPDATE:
return 'U';
case Operation.EVENT_ARG_DELETE:
return 'D';
case Operation.EVENT_ARG_BATCH:
return 'B';
default:
return '?';
}
}
@Override
public String toString() {
StringBuilder builder = new StringBuilder();
builder.append("Operation [op=");
builder.append(op);
builder.append(", token=");
builder.append(token);
builder.append(", scheduledExecutionTime=");
builder.append(scheduledExecutionTime);
builder.append("]");
return builder.toString();
}
}
public AsyncQueryService(Context context) {
mContext = context;
}
@ -436,4 +377,60 @@ public class AsyncQueryService extends Handler {
protected void setTestHandler(Handler handler) {
mHandler = handler;
}
/**
* Data class which holds into info of the queued operation
*/
public static class Operation {
static final int EVENT_ARG_QUERY = 1;
static final int EVENT_ARG_INSERT = 2;
static final int EVENT_ARG_UPDATE = 3;
static final int EVENT_ARG_DELETE = 4;
static final int EVENT_ARG_BATCH = 5;
/**
* unique identify for cancellation purpose
*/
public int token;
/**
* One of the EVENT_ARG_ constants in the class describing the operation
*/
public int op;
/**
* {@link SystemClock.elapsedRealtime()} based
*/
public long scheduledExecutionTime;
protected static char opToChar(int op) {
switch (op) {
case Operation.EVENT_ARG_QUERY:
return 'Q';
case Operation.EVENT_ARG_INSERT:
return 'I';
case Operation.EVENT_ARG_UPDATE:
return 'U';
case Operation.EVENT_ARG_DELETE:
return 'D';
case Operation.EVENT_ARG_BATCH:
return 'B';
default:
return '?';
}
}
@Override
public String toString() {
StringBuilder builder = new StringBuilder();
builder.append("Operation [op=");
builder.append(op);
builder.append(", token=");
builder.append(token);
builder.append(", scheduledExecutionTime=");
builder.append(scheduledExecutionTime);
builder.append("]");
return builder.toString();
}
}
}

View File

@ -16,10 +16,6 @@
package com.android.calendar;
import org.sufficientlysecure.standalonecalendar.R;
import com.android.calendar.AsyncQueryService.Operation;
import android.app.IntentService;
import android.content.ContentProviderOperation;
import android.content.ContentResolver;
@ -35,6 +31,8 @@ import android.os.RemoteException;
import android.os.SystemClock;
import android.util.Log;
import com.android.calendar.AsyncQueryService.Operation;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
@ -50,106 +48,12 @@ public class AsyncQueryServiceHelper extends IntentService {
protected Class<AsyncQueryService> mService = AsyncQueryService.class;
protected static class OperationInfo implements Delayed{
public int token; // Used for cancel
public int op;
public ContentResolver resolver;
public Uri uri;
public String authority;
public Handler handler;
public String[] projection;
public String selection;
public String[] selectionArgs;
public String orderBy;
public Object result;
public Object cookie;
public ContentValues values;
public ArrayList<ContentProviderOperation> cpo;
public AsyncQueryServiceHelper(String name) {
super(name);
}
/**
* delayMillis is relative time e.g. 10,000 milliseconds
*/
public long delayMillis;
/**
* scheduleTimeMillis is the time scheduled for this to be processed.
* e.g. SystemClock.elapsedRealtime() + 10,000 milliseconds Based on
* {@link android.os.SystemClock#elapsedRealtime }
*/
private long mScheduledTimeMillis = 0;
// @VisibleForTesting
void calculateScheduledTime() {
mScheduledTimeMillis = SystemClock.elapsedRealtime() + delayMillis;
}
// @Override // Uncomment with Java6
public long getDelay(TimeUnit unit) {
return unit.convert(mScheduledTimeMillis - SystemClock.elapsedRealtime(),
TimeUnit.MILLISECONDS);
}
// @Override // Uncomment with Java6
public int compareTo(Delayed another) {
OperationInfo anotherArgs = (OperationInfo) another;
if (this.mScheduledTimeMillis == anotherArgs.mScheduledTimeMillis) {
return 0;
} else if (this.mScheduledTimeMillis < anotherArgs.mScheduledTimeMillis) {
return -1;
} else {
return 1;
}
}
@Override
public String toString() {
StringBuilder builder = new StringBuilder();
builder.append("OperationInfo [\n\t token= ");
builder.append(token);
builder.append(",\n\t op= ");
builder.append(Operation.opToChar(op));
builder.append(",\n\t uri= ");
builder.append(uri);
builder.append(",\n\t authority= ");
builder.append(authority);
builder.append(",\n\t delayMillis= ");
builder.append(delayMillis);
builder.append(",\n\t mScheduledTimeMillis= ");
builder.append(mScheduledTimeMillis);
builder.append(",\n\t resolver= ");
builder.append(resolver);
builder.append(",\n\t handler= ");
builder.append(handler);
builder.append(",\n\t projection= ");
builder.append(Arrays.toString(projection));
builder.append(",\n\t selection= ");
builder.append(selection);
builder.append(",\n\t selectionArgs= ");
builder.append(Arrays.toString(selectionArgs));
builder.append(",\n\t orderBy= ");
builder.append(orderBy);
builder.append(",\n\t result= ");
builder.append(result);
builder.append(",\n\t cookie= ");
builder.append(cookie);
builder.append(",\n\t values= ");
builder.append(values);
builder.append(",\n\t cpo= ");
builder.append(cpo);
builder.append("\n]");
return builder.toString();
}
/**
* Compares an user-visible operation to this private OperationInfo
* object
*
* @param o operation to be compared
* @return true if logically equivalent
*/
public boolean equivalent(Operation o) {
return o.token == this.token && o.op == this.op;
}
public AsyncQueryServiceHelper() {
super("AsyncQueryServiceHelper");
}
/**
@ -233,14 +137,6 @@ public class AsyncQueryServiceHelper extends IntentService {
return canceled;
}
public AsyncQueryServiceHelper(String name) {
super(name);
}
public AsyncQueryServiceHelper() {
super("AsyncQueryServiceHelper");
}
@Override
protected void onHandleIntent(Intent intent) {
OperationInfo args;
@ -377,4 +273,106 @@ public class AsyncQueryServiceHelper extends IntentService {
}
super.onDestroy();
}
protected static class OperationInfo implements Delayed {
public int token; // Used for cancel
public int op;
public ContentResolver resolver;
public Uri uri;
public String authority;
public Handler handler;
public String[] projection;
public String selection;
public String[] selectionArgs;
public String orderBy;
public Object result;
public Object cookie;
public ContentValues values;
public ArrayList<ContentProviderOperation> cpo;
/**
* delayMillis is relative time e.g. 10,000 milliseconds
*/
public long delayMillis;
/**
* scheduleTimeMillis is the time scheduled for this to be processed.
* e.g. SystemClock.elapsedRealtime() + 10,000 milliseconds Based on
* {@link android.os.SystemClock#elapsedRealtime }
*/
private long mScheduledTimeMillis = 0;
// @VisibleForTesting
void calculateScheduledTime() {
mScheduledTimeMillis = SystemClock.elapsedRealtime() + delayMillis;
}
// @Override // Uncomment with Java6
public long getDelay(TimeUnit unit) {
return unit.convert(mScheduledTimeMillis - SystemClock.elapsedRealtime(),
TimeUnit.MILLISECONDS);
}
// @Override // Uncomment with Java6
public int compareTo(Delayed another) {
OperationInfo anotherArgs = (OperationInfo) another;
if (this.mScheduledTimeMillis == anotherArgs.mScheduledTimeMillis) {
return 0;
} else if (this.mScheduledTimeMillis < anotherArgs.mScheduledTimeMillis) {
return -1;
} else {
return 1;
}
}
@Override
public String toString() {
StringBuilder builder = new StringBuilder();
builder.append("OperationInfo [\n\t token= ");
builder.append(token);
builder.append(",\n\t op= ");
builder.append(Operation.opToChar(op));
builder.append(",\n\t uri= ");
builder.append(uri);
builder.append(",\n\t authority= ");
builder.append(authority);
builder.append(",\n\t delayMillis= ");
builder.append(delayMillis);
builder.append(",\n\t mScheduledTimeMillis= ");
builder.append(mScheduledTimeMillis);
builder.append(",\n\t resolver= ");
builder.append(resolver);
builder.append(",\n\t handler= ");
builder.append(handler);
builder.append(",\n\t projection= ");
builder.append(Arrays.toString(projection));
builder.append(",\n\t selection= ");
builder.append(selection);
builder.append(",\n\t selectionArgs= ");
builder.append(Arrays.toString(selectionArgs));
builder.append(",\n\t orderBy= ");
builder.append(orderBy);
builder.append(",\n\t result= ");
builder.append(result);
builder.append(",\n\t cookie= ");
builder.append(cookie);
builder.append(",\n\t values= ");
builder.append(values);
builder.append(",\n\t cpo= ");
builder.append(cpo);
builder.append("\n]");
return builder.toString();
}
/**
* Compares an user-visible operation to this private OperationInfo
* object
*
* @param o operation to be compared
* @return true if logically equivalent
*/
public boolean equivalent(Operation o) {
return o.token == this.token && o.op == this.op;
}
}
}

View File

@ -16,8 +16,6 @@
package com.android.calendar;
import org.sufficientlysecure.standalonecalendar.R;
import android.app.Application;
public class CalendarApplication extends Application {

View File

@ -16,8 +16,6 @@
package com.android.calendar;
import org.sufficientlysecure.standalonecalendar.R;
import android.app.backup.BackupAgentHelper;
import android.app.backup.BackupDataInput;
import android.app.backup.SharedPreferencesBackupHelper;

View File

@ -16,8 +16,6 @@
package com.android.calendar;
import org.sufficientlysecure.standalonecalendar.R;
import android.app.Activity;
import android.app.Dialog;
import android.content.ContentUris;
@ -37,124 +35,35 @@ import com.android.colorpicker.HsvColorComparator;
import java.util.ArrayList;
import java.util.Arrays;
import ws.xsoh.etar.R;
public class CalendarColorPickerDialog extends ColorPickerDialog {
private static final int NUM_COLUMNS = 4;
private static final String KEY_CALENDAR_ID = "calendar_id";
private static final String KEY_COLOR_KEYS = "color_keys";
private static final int TOKEN_QUERY_CALENDARS = 1 << 1;
private static final int TOKEN_QUERY_COLORS = 1 << 2;
public static final int COLORS_INDEX_COLOR = 0;
public static final int COLORS_INDEX_COLOR_KEY = 1;
static final String[] CALENDARS_PROJECTION = new String[] {
Calendars.ACCOUNT_NAME,
Calendars.ACCOUNT_TYPE,
Calendars.CALENDAR_COLOR
};
static final int CALENDARS_INDEX_ACCOUNT_NAME = 0;
static final int CALENDARS_INDEX_ACCOUNT_TYPE = 1;
static final int CALENDARS_INDEX_CALENDAR_COLOR = 2;
static final String[] COLORS_PROJECTION = new String[] {
Colors.COLOR,
Colors.COLOR_KEY
};
static final String COLORS_WHERE = Colors.ACCOUNT_NAME + "=? AND " + Colors.ACCOUNT_TYPE +
"=? AND " + Colors.COLOR_TYPE + "=" + Colors.TYPE_CALENDAR;
public static final int COLORS_INDEX_COLOR = 0;
public static final int COLORS_INDEX_COLOR_KEY = 1;
private static final int NUM_COLUMNS = 4;
private static final String KEY_CALENDAR_ID = "calendar_id";
private static final String KEY_COLOR_KEYS = "color_keys";
private static final int TOKEN_QUERY_CALENDARS = 1 << 1;
private static final int TOKEN_QUERY_COLORS = 1 << 2;
private QueryService mService;
private SparseIntArray mColorKeyMap = new SparseIntArray();
private long mCalendarId;
private class QueryService extends AsyncQueryService {
private QueryService(Context context) {
super(context);
}
@Override
protected void onQueryComplete(int token, Object cookie, Cursor cursor) {
// If the query didn't return a cursor for some reason return
if (cursor == null) {
return;
}
// If the Activity is finishing, then close the cursor.
// Otherwise, use the new cursor in the adapter.
final Activity activity = getActivity();
if (activity == null || activity.isFinishing()) {
cursor.close();
return;
}
switch (token) {
case TOKEN_QUERY_CALENDARS:
if (!cursor.moveToFirst()) {
cursor.close();
dismiss();
break;
}
mSelectedColor = Utils.getDisplayColorFromColor(
cursor.getInt(CALENDARS_INDEX_CALENDAR_COLOR));
Uri uri = Colors.CONTENT_URI;
String[] args = new String[] {
cursor.getString(CALENDARS_INDEX_ACCOUNT_NAME),
cursor.getString(CALENDARS_INDEX_ACCOUNT_TYPE) };
cursor.close();
startQuery(TOKEN_QUERY_COLORS, null, uri, COLORS_PROJECTION, COLORS_WHERE,
args, null);
break;
case TOKEN_QUERY_COLORS:
if (!cursor.moveToFirst()) {
cursor.close();
dismiss();
break;
}
mColorKeyMap.clear();
ArrayList<Integer> colors = new ArrayList<Integer>();
do
{
int colorKey = cursor.getInt(COLORS_INDEX_COLOR_KEY);
int rawColor = cursor.getInt(COLORS_INDEX_COLOR);
int displayColor = Utils.getDisplayColorFromColor(rawColor);
mColorKeyMap.put(displayColor, colorKey);
colors.add(displayColor);
} while (cursor.moveToNext());
Integer[] colorsToSort = colors.toArray(new Integer[colors.size()]);
Arrays.sort(colorsToSort, new HsvColorComparator());
mColors = new int[colorsToSort.length];
for (int i = 0; i < mColors.length; i++) {
mColors[i] = colorsToSort[i];
}
showPaletteView();
cursor.close();
break;
}
}
}
private class OnCalendarColorSelectedListener implements OnColorSelectedListener {
@Override
public void onColorSelected(int color) {
if (color == mSelectedColor || mService == null) {
return;
}
ContentValues values = new ContentValues();
values.put(Calendars.CALENDAR_COLOR_KEY, mColorKeyMap.get(color));
mService.startUpdate(mService.getNextToken(), null, ContentUris.withAppendedId(
Calendars.CONTENT_URI, mCalendarId), values, null, null, Utils.UNDO_DELAY);
}
}
public CalendarColorPickerDialog() {
// Empty constructor required for dialog fragments.
}
@ -236,4 +145,85 @@ public class CalendarColorPickerDialog extends ColorPickerDialog {
CALENDARS_PROJECTION, null, null, null);
}
}
private class QueryService extends AsyncQueryService {
private QueryService(Context context) {
super(context);
}
@Override
protected void onQueryComplete(int token, Object cookie, Cursor cursor) {
// If the query didn't return a cursor for some reason return
if (cursor == null) {
return;
}
// If the Activity is finishing, then close the cursor.
// Otherwise, use the new cursor in the adapter.
final Activity activity = getActivity();
if (activity == null || activity.isFinishing()) {
cursor.close();
return;
}
switch (token) {
case TOKEN_QUERY_CALENDARS:
if (!cursor.moveToFirst()) {
cursor.close();
dismiss();
break;
}
mSelectedColor = Utils.getDisplayColorFromColor(
cursor.getInt(CALENDARS_INDEX_CALENDAR_COLOR));
Uri uri = Colors.CONTENT_URI;
String[] args = new String[]{
cursor.getString(CALENDARS_INDEX_ACCOUNT_NAME),
cursor.getString(CALENDARS_INDEX_ACCOUNT_TYPE)};
cursor.close();
startQuery(TOKEN_QUERY_COLORS, null, uri, COLORS_PROJECTION, COLORS_WHERE,
args, null);
break;
case TOKEN_QUERY_COLORS:
if (!cursor.moveToFirst()) {
cursor.close();
dismiss();
break;
}
mColorKeyMap.clear();
ArrayList<Integer> colors = new ArrayList<Integer>();
do {
int colorKey = cursor.getInt(COLORS_INDEX_COLOR_KEY);
int rawColor = cursor.getInt(COLORS_INDEX_COLOR);
int displayColor = Utils.getDisplayColorFromColor(rawColor);
mColorKeyMap.put(displayColor, colorKey);
colors.add(displayColor);
} while (cursor.moveToNext());
Integer[] colorsToSort = colors.toArray(new Integer[colors.size()]);
Arrays.sort(colorsToSort, new HsvColorComparator());
mColors = new int[colorsToSort.length];
for (int i = 0; i < mColors.length; i++) {
mColors[i] = colorsToSort[i];
}
showPaletteView();
cursor.close();
break;
}
}
}
private class OnCalendarColorSelectedListener implements OnColorSelectedListener {
@Override
public void onColorSelected(int color) {
if (color == mSelectedColor || mService == null) {
return;
}
ContentValues values = new ContentValues();
values.put(Calendars.CALENDAR_COLOR_KEY, mColorKeyMap.get(color));
mService.startUpdate(mService.getNextToken(), null, ContentUris.withAppendedId(
Calendars.CONTENT_URI, mCalendarId), values, null, null, Utils.UNDO_DELAY);
}
}
}

View File

@ -16,13 +16,6 @@
package com.android.calendar;
import org.sufficientlysecure.standalonecalendar.R;
import static android.provider.CalendarContract.EXTRA_EVENT_ALL_DAY;
import static android.provider.CalendarContract.EXTRA_EVENT_BEGIN_TIME;
import static android.provider.CalendarContract.EXTRA_EVENT_END_TIME;
import static android.provider.CalendarContract.Attendees.ATTENDEE_STATUS;
import android.accounts.Account;
import android.accounts.AccountManager;
import android.app.Activity;
@ -51,210 +44,22 @@ import java.util.LinkedList;
import java.util.Map.Entry;
import java.util.WeakHashMap;
import static android.provider.CalendarContract.Attendees.ATTENDEE_STATUS;
import static android.provider.CalendarContract.EXTRA_EVENT_ALL_DAY;
import static android.provider.CalendarContract.EXTRA_EVENT_BEGIN_TIME;
import static android.provider.CalendarContract.EXTRA_EVENT_END_TIME;
public class CalendarController {
private static final boolean DEBUG = false;
private static final String TAG = "CalendarController";
public static final String EVENT_EDIT_ON_LAUNCH = "editMode";
public static final int MIN_CALENDAR_YEAR = 1970;
public static final int MAX_CALENDAR_YEAR = 2036;
public static final int MIN_CALENDAR_WEEK = 0;
public static final int MAX_CALENDAR_WEEK = 3497; // weeks between 1/1/1970 and 1/1/2037
private final Context mContext;
// This uses a LinkedHashMap so that we can replace fragments based on the
// view id they are being expanded into since we can't guarantee a reference
// to the handler will be findable
private final LinkedHashMap<Integer,EventHandler> eventHandlers =
new LinkedHashMap<Integer,EventHandler>(5);
private final LinkedList<Integer> mToBeRemovedEventHandlers = new LinkedList<Integer>();
private final LinkedHashMap<Integer, EventHandler> mToBeAddedEventHandlers = new LinkedHashMap<
Integer, EventHandler>();
private Pair<Integer, EventHandler> mFirstEventHandler;
private Pair<Integer, EventHandler> mToBeAddedFirstEventHandler;
private volatile int mDispatchInProgressCounter = 0;
private static WeakHashMap<Context, CalendarController> instances =
new WeakHashMap<Context, CalendarController>();
private final WeakHashMap<Object, Long> filters = new WeakHashMap<Object, Long>(1);
private int mViewType = -1;
private int mDetailViewType = -1;
private int mPreviousViewType = -1;
private long mEventId = -1;
private final Time mTime = new Time();
private long mDateFlags = 0;
private final Runnable mUpdateTimezone = new Runnable() {
@Override
public void run() {
mTime.switchTimezone(Utils.getTimeZone(mContext, this));
}
};
/**
* One of the event types that are sent to or from the controller
*/
public interface EventType {
final long CREATE_EVENT = 1L;
// Simple view of an event
final long VIEW_EVENT = 1L << 1;
// Full detail view in read only mode
final long VIEW_EVENT_DETAILS = 1L << 2;
// full detail view in edit mode
final long EDIT_EVENT = 1L << 3;
final long DELETE_EVENT = 1L << 4;
final long GO_TO = 1L << 5;
final long LAUNCH_SETTINGS = 1L << 6;
final long EVENTS_CHANGED = 1L << 7;
final long SEARCH = 1L << 8;
// User has pressed the home key
final long USER_HOME = 1L << 9;
// date range has changed, update the title
final long UPDATE_TITLE = 1L << 10;
// select which calendars to display
final long LAUNCH_SELECT_VISIBLE_CALENDARS = 1L << 11;
}
/**
* One of the Agenda/Day/Week/Month view types
*/
public interface ViewType {
final int DETAIL = -1;
final int CURRENT = 0;
final int AGENDA = 1;
final int DAY = 2;
final int WEEK = 3;
final int MONTH = 4;
final int EDIT = 5;
final int MAX_VALUE = 5;
}
public static class EventInfo {
private static final long ATTENTEE_STATUS_MASK = 0xFF;
private static final long ALL_DAY_MASK = 0x100;
private static final int ATTENDEE_STATUS_NONE_MASK = 0x01;
private static final int ATTENDEE_STATUS_ACCEPTED_MASK = 0x02;
private static final int ATTENDEE_STATUS_DECLINED_MASK = 0x04;
private static final int ATTENDEE_STATUS_TENTATIVE_MASK = 0x08;
public long eventType; // one of the EventType
public int viewType; // one of the ViewType
public long id; // event id
public Time selectedTime; // the selected time in focus
// Event start and end times. All-day events are represented in:
// - local time for GO_TO commands
// - UTC time for VIEW_EVENT and other event-related commands
public Time startTime;
public Time endTime;
public int x; // x coordinate in the activity space
public int y; // y coordinate in the activity space
public String query; // query for a user search
public ComponentName componentName; // used in combination with query
public String eventTitle;
public long calendarId;
/**
* For EventType.VIEW_EVENT:
* It is the default attendee response and an all day event indicator.
* Set to Attendees.ATTENDEE_STATUS_NONE, Attendees.ATTENDEE_STATUS_ACCEPTED,
* Attendees.ATTENDEE_STATUS_DECLINED, or Attendees.ATTENDEE_STATUS_TENTATIVE.
* To signal the event is an all-day event, "or" ALL_DAY_MASK with the response.
* Alternatively, use buildViewExtraLong(), getResponse(), and isAllDay().
* <p>
* For EventType.CREATE_EVENT:
* Set to {@link #EXTRA_CREATE_ALL_DAY} for creating an all-day event.
* <p>
* For EventType.GO_TO:
* Set to {@link #EXTRA_GOTO_TIME} to go to the specified date/time.
* Set to {@link #EXTRA_GOTO_DATE} to consider the date but ignore the time.
* Set to {@link #EXTRA_GOTO_BACK_TO_PREVIOUS} if back should bring back previous view.
* Set to {@link #EXTRA_GOTO_TODAY} if this is a user request to go to the current time.
* <p>
* For EventType.UPDATE_TITLE:
* Set formatting flags for Utils.formatDateRange
*/
public long extraLong;
public boolean isAllDay() {
if (eventType != EventType.VIEW_EVENT) {
Log.wtf(TAG, "illegal call to isAllDay , wrong event type " + eventType);
return false;
}
return ((extraLong & ALL_DAY_MASK) != 0) ? true : false;
}
public int getResponse() {
if (eventType != EventType.VIEW_EVENT) {
Log.wtf(TAG, "illegal call to getResponse , wrong event type " + eventType);
return Attendees.ATTENDEE_STATUS_NONE;
}
int response = (int)(extraLong & ATTENTEE_STATUS_MASK);
switch (response) {
case ATTENDEE_STATUS_NONE_MASK:
return Attendees.ATTENDEE_STATUS_NONE;
case ATTENDEE_STATUS_ACCEPTED_MASK:
return Attendees.ATTENDEE_STATUS_ACCEPTED;
case ATTENDEE_STATUS_DECLINED_MASK:
return Attendees.ATTENDEE_STATUS_DECLINED;
case ATTENDEE_STATUS_TENTATIVE_MASK:
return Attendees.ATTENDEE_STATUS_TENTATIVE;
default:
Log.wtf(TAG,"Unknown attendee response " + response);
}
return ATTENDEE_STATUS_NONE_MASK;
}
// Used to build the extra long for a VIEW event.
public static long buildViewExtraLong(int response, boolean allDay) {
long extra = allDay ? ALL_DAY_MASK : 0;
switch (response) {
case Attendees.ATTENDEE_STATUS_NONE:
extra |= ATTENDEE_STATUS_NONE_MASK;
break;
case Attendees.ATTENDEE_STATUS_ACCEPTED:
extra |= ATTENDEE_STATUS_ACCEPTED_MASK;
break;
case Attendees.ATTENDEE_STATUS_DECLINED:
extra |= ATTENDEE_STATUS_DECLINED_MASK;
break;
case Attendees.ATTENDEE_STATUS_TENTATIVE:
extra |= ATTENDEE_STATUS_TENTATIVE_MASK;
break;
default:
Log.wtf(TAG,"Unknown attendee response " + response);
extra |= ATTENDEE_STATUS_NONE_MASK;
break;
}
return extra;
}
}
/**
* Pass to the ExtraLong parameter for EventType.CREATE_EVENT to create
* an all-day event
*/
public static final long EXTRA_CREATE_ALL_DAY = 0x10;
/**
* Pass to the ExtraLong parameter for EventType.GO_TO to signal the time
* can be ignored
@ -263,16 +68,43 @@ public class CalendarController {
public static final long EXTRA_GOTO_TIME = 2;
public static final long EXTRA_GOTO_BACK_TO_PREVIOUS = 4;
public static final long EXTRA_GOTO_TODAY = 8;
private static final boolean DEBUG = false;
private static final String TAG = "CalendarController";
private static WeakHashMap<Context, CalendarController> instances =
new WeakHashMap<Context, CalendarController>();
private final Context mContext;
// This uses a LinkedHashMap so that we can replace fragments based on the
// view id they are being expanded into since we can't guarantee a reference
// to the handler will be findable
private final LinkedHashMap<Integer,EventHandler> eventHandlers =
new LinkedHashMap<Integer,EventHandler>(5);
private final LinkedList<Integer> mToBeRemovedEventHandlers = new LinkedList<Integer>();
private final LinkedHashMap<Integer, EventHandler> mToBeAddedEventHandlers = new LinkedHashMap<
Integer, EventHandler>();
private final WeakHashMap<Object, Long> filters = new WeakHashMap<Object, Long>(1);
private final Time mTime = new Time();
private final Runnable mUpdateTimezone = new Runnable() {
@Override
public void run() {
mTime.switchTimezone(Utils.getTimeZone(mContext, this));
}
};
private Pair<Integer, EventHandler> mFirstEventHandler;
private Pair<Integer, EventHandler> mToBeAddedFirstEventHandler;
private volatile int mDispatchInProgressCounter = 0;
private int mViewType = -1;
private int mDetailViewType = -1;
private int mPreviousViewType = -1;
private long mEventId = -1;
private long mDateFlags = 0;
public interface EventHandler {
long getSupportedEventTypes();
void handleEvent(EventInfo event);
/**
* This notifies the handler that the database has changed and it should
* update its view.
*/
void eventsChanged();
private CalendarController(Context context) {
mContext = context;
mUpdateTimezone.run();
mTime.setToNow();
mDetailViewType = Utils.getSharedPreference(mContext,
GeneralPreferences.KEY_DETAILED_VIEW,
GeneralPreferences.DEFAULT_DETAILED_VIEW);
}
/**
@ -302,17 +134,8 @@ public class CalendarController {
instances.remove(context);
}
private CalendarController(Context context) {
mContext = context;
mUpdateTimezone.run();
mTime.setToNow();
mDetailViewType = Utils.getSharedPreference(mContext,
GeneralPreferences.KEY_DETAILED_VIEW,
GeneralPreferences.DEFAULT_DETAILED_VIEW);
}
public void sendEventRelatedEvent(Object sender, long eventType, long eventId, long startMillis,
long endMillis, int x, int y, long selectedMillis) {
long endMillis, int x, int y, long selectedMillis) {
// TODO: pass the real allDay status or at least a status that says we don't know the
// status and have the receiver query the data.
// The current use of this method for VIEW_EVENT is by the day view to show an EventInfo
@ -337,9 +160,9 @@ public class CalendarController {
* @param selectedMillis The time to specify as selected
*/
public void sendEventRelatedEventWithExtra(Object sender, long eventType, long eventId,
long startMillis, long endMillis, int x, int y, long extraLong, long selectedMillis) {
long startMillis, long endMillis, int x, int y, long extraLong, long selectedMillis) {
sendEventRelatedEventWithExtraWithTitleWithCalendarId(sender, eventType, eventId,
startMillis, endMillis, x, y, extraLong, selectedMillis, null, -1);
startMillis, endMillis, x, y, extraLong, selectedMillis, null, -1);
}
/**
@ -359,8 +182,8 @@ public class CalendarController {
* @param calendarId The id of the calendar which the event belongs to
*/
public void sendEventRelatedEventWithExtraWithTitleWithCalendarId(Object sender, long eventType,
long eventId, long startMillis, long endMillis, int x, int y, long extraLong,
long selectedMillis, String title, long calendarId) {
long eventId, long startMillis, long endMillis, int x, int y, long extraLong,
long selectedMillis, String title, long calendarId) {
EventInfo info = new EventInfo();
info.eventType = eventType;
if (eventType == EventType.EDIT_EVENT || eventType == EventType.VIEW_EVENT_DETAILS) {
@ -385,18 +208,19 @@ public class CalendarController {
info.calendarId = calendarId;
this.sendEvent(sender, info);
}
/**
* Helper for sending non-calendar-event events
*
* @param sender object of the caller
* @param sender object of the caller
* @param eventType one of {@link EventType}
* @param start start time
* @param end end time
* @param eventId event id
* @param viewType {@link ViewType}
* @param start start time
* @param end end time
* @param eventId event id
* @param viewType {@link ViewType}
*/
public void sendEvent(Object sender, long eventType, Time start, Time end, long eventId,
int viewType) {
int viewType) {
sendEvent(sender, eventType, start, end, start, eventId, viewType, EXTRA_GOTO_TIME, null,
null);
}
@ -405,13 +229,13 @@ public class CalendarController {
* sendEvent() variant with extraLong, search query, and search component name.
*/
public void sendEvent(Object sender, long eventType, Time start, Time end, long eventId,
int viewType, long extraLong, String query, ComponentName componentName) {
int viewType, long extraLong, String query, ComponentName componentName) {
sendEvent(sender, eventType, start, end, start, eventId, viewType, extraLong, query,
componentName);
}
public void sendEvent(Object sender, long eventType, Time start, Time end, Time selected,
long eventId, int viewType, long extraLong, String query, ComponentName componentName) {
long eventId, int viewType, long extraLong, String query, ComponentName componentName) {
EventInfo info = new EventInfo();
info.eventType = eventType;
info.startTime = start;
@ -516,7 +340,7 @@ public class CalendarController {
boolean handled = false;
synchronized (this) {
mDispatchInProgressCounter ++;
mDispatchInProgressCounter++;
if (DEBUG) {
Log.d(TAG, "sendEvent: Dispatching to " + eventHandlers.size() + " handlers");
@ -532,7 +356,7 @@ public class CalendarController {
}
}
for (Iterator<Entry<Integer, EventHandler>> handlers =
eventHandlers.entrySet().iterator(); handlers.hasNext();) {
eventHandlers.entrySet().iterator(); handlers.hasNext(); ) {
Entry<Integer, EventHandler> entry = handlers.next();
int key = entry.getKey();
if (mFirstEventHandler != null && key == mFirstEventHandler.first) {
@ -550,7 +374,7 @@ public class CalendarController {
}
}
mDispatchInProgressCounter --;
mDispatchInProgressCounter--;
if (mDispatchInProgressCounter == 0) {
@ -683,14 +507,6 @@ public class CalendarController {
return mTime.toMillis(false);
}
/**
* @return the last set of date flags sent with
* {@link EventType#UPDATE_TITLE}
*/
public long getDateFlags() {
return mDateFlags;
}
/**
* Set the time this controller is currently pointed at
*
@ -700,6 +516,14 @@ public class CalendarController {
mTime.set(millisTime);
}
/**
* @return the last set of date flags sent with
* {@link EventType#UPDATE_TITLE}
*/
public long getDateFlags() {
return mDateFlags;
}
/**
* @return the last event ID the edit view was launched with
*/
@ -707,10 +531,20 @@ public class CalendarController {
return mEventId;
}
// Sets the eventId. Should only be used for initialization.
public void setEventId(long eventId) {
mEventId = eventId;
}
public int getViewType() {
return mViewType;
}
// Forces the viewType. Should only be used for initialization.
public void setViewType(int viewType) {
mViewType = viewType;
}
public int getPreviousViewType() {
return mPreviousViewType;
}
@ -730,15 +564,15 @@ public class CalendarController {
}
private void launchCreateEvent(long startMillis, long endMillis, boolean allDayEvent,
String title, long calendarId) {
String title, long calendarId) {
Intent intent = generateCreateEventIntent(startMillis, endMillis, allDayEvent, title,
calendarId);
calendarId);
mEventId = -1;
mContext.startActivity(intent);
}
public Intent generateCreateEventIntent(long startMillis, long endMillis,
boolean allDayEvent, String title, long calendarId) {
boolean allDayEvent, String title, long calendarId) {
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setClass(mContext, EditEventActivity.class);
intent.putExtra(EXTRA_EVENT_BEGIN_TIME, startMillis);
@ -772,6 +606,17 @@ public class CalendarController {
mContext.startActivity(intent);
}
private void launchDeleteEvent(long eventId, long startMillis, long endMillis) {
launchDeleteEventAndFinish(null, eventId, startMillis, endMillis, -1);
}
private void launchDeleteEventAndFinish(Activity parentActivity, long eventId, long startMillis,
long endMillis, int deleteWhich) {
DeleteEventHelper deleteEventHelper = new DeleteEventHelper(mContext, parentActivity,
parentActivity != null /* exit when done */);
deleteEventHelper.delete(startMillis, endMillis, eventId, deleteWhich);
}
// private void launchAlerts() {
// Intent intent = new Intent();
// intent.setClass(mContext, AlertActivity.class);
@ -779,20 +624,9 @@ public class CalendarController {
// mContext.startActivity(intent);
// }
private void launchDeleteEvent(long eventId, long startMillis, long endMillis) {
launchDeleteEventAndFinish(null, eventId, startMillis, endMillis, -1);
}
private void launchDeleteEventAndFinish(Activity parentActivity, long eventId, long startMillis,
long endMillis, int deleteWhich) {
DeleteEventHelper deleteEventHelper = new DeleteEventHelper(mContext, parentActivity,
parentActivity != null /* exit when done */);
deleteEventHelper.delete(startMillis, endMillis, eventId, deleteWhich);
}
private void launchSearch(long eventId, String query, ComponentName componentName) {
final SearchManager searchManager =
(SearchManager)mContext.getSystemService(Context.SEARCH_SERVICE);
(SearchManager) mContext.getSystemService(Context.SEARCH_SERVICE);
final SearchableInfo searchableInfo = searchManager.getSearchableInfo(componentName);
final Intent intent = new Intent(Intent.ACTION_SEARCH);
intent.putExtra(SearchManager.QUERY, query);
@ -819,16 +653,6 @@ public class CalendarController {
}
}
// Forces the viewType. Should only be used for initialization.
public void setViewType(int viewType) {
mViewType = viewType;
}
// Sets the eventId. Should only be used for initialization.
public void setEventId(long eventId) {
mEventId = eventId;
}
private String eventInfoToString(EventInfo eventInfo) {
String tmp = "Unknown";
@ -875,4 +699,170 @@ public class CalendarController {
builder.append(eventInfo.y);
return builder.toString();
}
/**
* One of the event types that are sent to or from the controller
*/
public interface EventType {
final long CREATE_EVENT = 1L;
// Simple view of an event
final long VIEW_EVENT = 1L << 1;
// Full detail view in read only mode
final long VIEW_EVENT_DETAILS = 1L << 2;
// full detail view in edit mode
final long EDIT_EVENT = 1L << 3;
final long DELETE_EVENT = 1L << 4;
final long GO_TO = 1L << 5;
final long LAUNCH_SETTINGS = 1L << 6;
final long EVENTS_CHANGED = 1L << 7;
final long SEARCH = 1L << 8;
// User has pressed the home key
final long USER_HOME = 1L << 9;
// date range has changed, update the title
final long UPDATE_TITLE = 1L << 10;
// select which calendars to display
final long LAUNCH_SELECT_VISIBLE_CALENDARS = 1L << 11;
}
/**
* One of the Agenda/Day/Week/Month view types
*/
public interface ViewType {
final int DETAIL = -1;
final int CURRENT = 0;
final int AGENDA = 1;
final int DAY = 2;
final int WEEK = 3;
final int MONTH = 4;
final int EDIT = 5;
final int MAX_VALUE = 5;
}
public interface EventHandler {
long getSupportedEventTypes();
void handleEvent(EventInfo event);
/**
* This notifies the handler that the database has changed and it should
* update its view.
*/
void eventsChanged();
}
public static class EventInfo {
private static final long ATTENTEE_STATUS_MASK = 0xFF;
private static final long ALL_DAY_MASK = 0x100;
private static final int ATTENDEE_STATUS_NONE_MASK = 0x01;
private static final int ATTENDEE_STATUS_ACCEPTED_MASK = 0x02;
private static final int ATTENDEE_STATUS_DECLINED_MASK = 0x04;
private static final int ATTENDEE_STATUS_TENTATIVE_MASK = 0x08;
public long eventType; // one of the EventType
public int viewType; // one of the ViewType
public long id; // event id
public Time selectedTime; // the selected time in focus
// Event start and end times. All-day events are represented in:
// - local time for GO_TO commands
// - UTC time for VIEW_EVENT and other event-related commands
public Time startTime;
public Time endTime;
public int x; // x coordinate in the activity space
public int y; // y coordinate in the activity space
public String query; // query for a user search
public ComponentName componentName; // used in combination with query
public String eventTitle;
public long calendarId;
/**
* For EventType.VIEW_EVENT:
* It is the default attendee response and an all day event indicator.
* Set to Attendees.ATTENDEE_STATUS_NONE, Attendees.ATTENDEE_STATUS_ACCEPTED,
* Attendees.ATTENDEE_STATUS_DECLINED, or Attendees.ATTENDEE_STATUS_TENTATIVE.
* To signal the event is an all-day event, "or" ALL_DAY_MASK with the response.
* Alternatively, use buildViewExtraLong(), getResponse(), and isAllDay().
* <p/>
* For EventType.CREATE_EVENT:
* Set to {@link #EXTRA_CREATE_ALL_DAY} for creating an all-day event.
* <p/>
* For EventType.GO_TO:
* Set to {@link #EXTRA_GOTO_TIME} to go to the specified date/time.
* Set to {@link #EXTRA_GOTO_DATE} to consider the date but ignore the time.
* Set to {@link #EXTRA_GOTO_BACK_TO_PREVIOUS} if back should bring back previous view.
* Set to {@link #EXTRA_GOTO_TODAY} if this is a user request to go to the current time.
* <p/>
* For EventType.UPDATE_TITLE:
* Set formatting flags for Utils.formatDateRange
*/
public long extraLong;
// Used to build the extra long for a VIEW event.
public static long buildViewExtraLong(int response, boolean allDay) {
long extra = allDay ? ALL_DAY_MASK : 0;
switch (response) {
case Attendees.ATTENDEE_STATUS_NONE:
extra |= ATTENDEE_STATUS_NONE_MASK;
break;
case Attendees.ATTENDEE_STATUS_ACCEPTED:
extra |= ATTENDEE_STATUS_ACCEPTED_MASK;
break;
case Attendees.ATTENDEE_STATUS_DECLINED:
extra |= ATTENDEE_STATUS_DECLINED_MASK;
break;
case Attendees.ATTENDEE_STATUS_TENTATIVE:
extra |= ATTENDEE_STATUS_TENTATIVE_MASK;
break;
default:
Log.wtf(TAG, "Unknown attendee response " + response);
extra |= ATTENDEE_STATUS_NONE_MASK;
break;
}
return extra;
}
public boolean isAllDay() {
if (eventType != EventType.VIEW_EVENT) {
Log.wtf(TAG, "illegal call to isAllDay , wrong event type " + eventType);
return false;
}
return ((extraLong & ALL_DAY_MASK) != 0) ? true : false;
}
public int getResponse() {
if (eventType != EventType.VIEW_EVENT) {
Log.wtf(TAG, "illegal call to getResponse , wrong event type " + eventType);
return Attendees.ATTENDEE_STATUS_NONE;
}
int response = (int) (extraLong & ATTENTEE_STATUS_MASK);
switch (response) {
case ATTENDEE_STATUS_NONE_MASK:
return Attendees.ATTENDEE_STATUS_NONE;
case ATTENDEE_STATUS_ACCEPTED_MASK:
return Attendees.ATTENDEE_STATUS_ACCEPTED;
case ATTENDEE_STATUS_DECLINED_MASK:
return Attendees.ATTENDEE_STATUS_DECLINED;
case ATTENDEE_STATUS_TENTATIVE_MASK:
return Attendees.ATTENDEE_STATUS_TENTATIVE;
default:
Log.wtf(TAG, "Unknown attendee response " + response);
}
return ATTENDEE_STATUS_NONE_MASK;
}
}
}

View File

@ -16,8 +16,6 @@
package com.android.calendar;
import org.sufficientlysecure.standalonecalendar.R;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
@ -46,180 +44,25 @@ import java.util.TimeZone;
*/
public class CalendarEventModel implements Serializable {
private static final String TAG = "CalendarEventModel";
public static class Attendee implements Serializable {
@Override
public int hashCode() {
return (mEmail == null) ? 0 : mEmail.hashCode();
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (!(obj instanceof Attendee)) {
return false;
}
Attendee other = (Attendee) obj;
if (!TextUtils.equals(mEmail, other.mEmail)) {
return false;
}
return true;
}
String getDisplayName() {
if (TextUtils.isEmpty(mName)) {
return mEmail;
} else {
return mName;
}
}
public String mName;
public String mEmail;
public int mStatus;
public String mIdentity;
public String mIdNamespace;
public Attendee(String name, String email) {
this(name, email, Attendees.ATTENDEE_STATUS_NONE, null, null);
}
public Attendee(String name, String email, int status, String identity,
String idNamespace) {
mName = name;
mEmail = email;
mStatus = status;
mIdentity = identity;
mIdNamespace = idNamespace;
}
}
/**
* A single reminder entry.
*
* Instances of the class are immutable.
*/
public static class ReminderEntry implements Comparable<ReminderEntry>, Serializable {
private final int mMinutes;
private final int mMethod;
/**
* Returns a new ReminderEntry, with the specified minutes and method.
*
* @param minutes Number of minutes before the start of the event that the alert will fire.
* @param method Type of alert ({@link Reminders#METHOD_ALERT}, etc).
*/
public static ReminderEntry valueOf(int minutes, int method) {
// TODO: cache common instances
return new ReminderEntry(minutes, method);
}
/**
* Returns a ReminderEntry, with the specified number of minutes and a default alert method.
*
* @param minutes Number of minutes before the start of the event that the alert will fire.
*/
public static ReminderEntry valueOf(int minutes) {
return valueOf(minutes, Reminders.METHOD_DEFAULT);
}
/**
* Constructs a new ReminderEntry.
*
* @param minutes Number of minutes before the start of the event that the alert will fire.
* @param method Type of alert ({@link Reminders#METHOD_ALERT}, etc).
*/
private ReminderEntry(int minutes, int method) {
// TODO: error-check args
mMinutes = minutes;
mMethod = method;
}
@Override
public int hashCode() {
return mMinutes * 10 + mMethod;
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (!(obj instanceof ReminderEntry)) {
return false;
}
ReminderEntry re = (ReminderEntry) obj;
if (re.mMinutes != mMinutes) {
return false;
}
// Treat ALERT and DEFAULT as equivalent. This is useful during the "has anything
// "changed" test, so that if DEFAULT is present, but we don't change anything,
// the internal conversion of DEFAULT to ALERT doesn't force a database update.
return re.mMethod == mMethod ||
(re.mMethod == Reminders.METHOD_DEFAULT && mMethod == Reminders.METHOD_ALERT) ||
(re.mMethod == Reminders.METHOD_ALERT && mMethod == Reminders.METHOD_DEFAULT);
}
@Override
public String toString() {
return "ReminderEntry min=" + mMinutes + " meth=" + mMethod;
}
/**
* Comparison function for a sort ordered primarily descending by minutes,
* secondarily ascending by method type.
*/
@Override
public int compareTo(ReminderEntry re) {
if (re.mMinutes != mMinutes) {
return re.mMinutes - mMinutes;
}
if (re.mMethod != mMethod) {
return mMethod - re.mMethod;
}
return 0;
}
/** Returns the minutes. */
public int getMinutes() {
return mMinutes;
}
/** Returns the alert method. */
public int getMethod() {
return mMethod;
}
}
// TODO strip out fields that don't ever get used
/**
* The uri of the event in the db. This should only be null for new events.
*/
public String mUri = null;
public long mId = -1;
// TODO strip out fields that don't ever get used
public long mCalendarId = -1;
public String mCalendarDisplayName = ""; // Make sure this is in sync with the mCalendarId
private int mCalendarColor = -1;
private boolean mCalendarColorInitialized = false;
public String mCalendarAccountName;
public String mCalendarAccountType;
public int mCalendarMaxReminders;
public String mCalendarAllowedReminders;
public String mCalendarAllowedAttendeeTypes;
public String mCalendarAllowedAvailability;
public String mSyncId = null;
public String mSyncAccount = null;
public String mSyncAccountType = null;
public EventColorCache mEventColorCache;
private int mEventColor = -1;
private boolean mEventColorInitialized = false;
// PROVIDER_NOTES owner account comes from the calendars table
public String mOwnerAccount = null;
public String mTitle = null;
@ -233,12 +76,10 @@ public class CalendarEventModel implements Serializable {
*/
public boolean mIsOrganizer = true;
public boolean mIsFirstEventInSeries = true;
// This should be set the same as mStart when created and is used for making changes to
// recurring events. It should not be updated after it is initially set.
public long mOriginalStart = -1;
public long mStart = -1;
// This should be set the same as mEnd when created and is used for making changes to
// recurring events. It should not be updated after it is initially set.
public long mOriginalEnd = -1;
@ -249,7 +90,6 @@ public class CalendarEventModel implements Serializable {
public boolean mAllDay = false;
public boolean mHasAlarm = false;
public int mAvailability = Events.AVAILABILITY_BUSY;
// PROVIDER_NOTES How does an event not have attendee data? The owner is added
// as an attendee by default.
public boolean mHasAttendeeData = true;
@ -262,24 +102,22 @@ public class CalendarEventModel implements Serializable {
public boolean mGuestsCanModify = false;
public boolean mGuestsCanInviteOthers = false;
public boolean mGuestsCanSeeGuests = false;
public boolean mOrganizerCanRespond = false;
public int mCalendarAccessLevel = Calendars.CAL_ACCESS_CONTRIBUTOR;
public int mEventStatus = Events.STATUS_CONFIRMED;
// The model can't be updated with a calendar cursor until it has been
// updated with an event cursor.
public boolean mModelUpdatedWithEventCursor;
public int mAccessLevel = 0;
public ArrayList<ReminderEntry> mReminders;
public ArrayList<ReminderEntry> mDefaultReminders;
// PROVIDER_NOTES Using EditEventHelper the owner should not be included in this
// list and will instead be added by saveEvent. Is this what we want?
public LinkedHashMap<String, Attendee> mAttendeesList;
private int mCalendarColor = -1;
private boolean mCalendarColorInitialized = false;
private int mEventColor = -1;
private boolean mEventColorInitialized = false;
public CalendarEventModel() {
mReminders = new ArrayList<ReminderEntry>();
mDefaultReminders = new ArrayList<ReminderEntry>();
@ -915,15 +753,15 @@ public class CalendarEventModel implements Serializable {
return mCalendarColor;
}
public int getEventColor() {
return mEventColor;
}
public void setCalendarColor(int color) {
mCalendarColor = color;
mCalendarColorInitialized = true;
}
public int getEventColor() {
return mEventColor;
}
public void setEventColor(int color) {
mEventColor = color;
mEventColorInitialized = true;
@ -943,4 +781,153 @@ public class CalendarEventModel implements Serializable {
}
return -1;
}
public static class Attendee implements Serializable {
public String mName;
public String mEmail;
public int mStatus;
public String mIdentity;
public String mIdNamespace;
public Attendee(String name, String email) {
this(name, email, Attendees.ATTENDEE_STATUS_NONE, null, null);
}
public Attendee(String name, String email, int status, String identity,
String idNamespace) {
mName = name;
mEmail = email;
mStatus = status;
mIdentity = identity;
mIdNamespace = idNamespace;
}
@Override
public int hashCode() {
return (mEmail == null) ? 0 : mEmail.hashCode();
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (!(obj instanceof Attendee)) {
return false;
}
Attendee other = (Attendee) obj;
if (!TextUtils.equals(mEmail, other.mEmail)) {
return false;
}
return true;
}
String getDisplayName() {
if (TextUtils.isEmpty(mName)) {
return mEmail;
} else {
return mName;
}
}
}
/**
* A single reminder entry.
* <p/>
* Instances of the class are immutable.
*/
public static class ReminderEntry implements Comparable<ReminderEntry>, Serializable {
private final int mMinutes;
private final int mMethod;
/**
* Constructs a new ReminderEntry.
*
* @param minutes Number of minutes before the start of the event that the alert will fire.
* @param method Type of alert ({@link Reminders#METHOD_ALERT}, etc).
*/
private ReminderEntry(int minutes, int method) {
// TODO: error-check args
mMinutes = minutes;
mMethod = method;
}
/**
* Returns a new ReminderEntry, with the specified minutes and method.
*
* @param minutes Number of minutes before the start of the event that the alert will fire.
* @param method Type of alert ({@link Reminders#METHOD_ALERT}, etc).
*/
public static ReminderEntry valueOf(int minutes, int method) {
// TODO: cache common instances
return new ReminderEntry(minutes, method);
}
/**
* Returns a ReminderEntry, with the specified number of minutes and a default alert method.
*
* @param minutes Number of minutes before the start of the event that the alert will fire.
*/
public static ReminderEntry valueOf(int minutes) {
return valueOf(minutes, Reminders.METHOD_DEFAULT);
}
@Override
public int hashCode() {
return mMinutes * 10 + mMethod;
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (!(obj instanceof ReminderEntry)) {
return false;
}
ReminderEntry re = (ReminderEntry) obj;
if (re.mMinutes != mMinutes) {
return false;
}
// Treat ALERT and DEFAULT as equivalent. This is useful during the "has anything
// "changed" test, so that if DEFAULT is present, but we don't change anything,
// the internal conversion of DEFAULT to ALERT doesn't force a database update.
return re.mMethod == mMethod ||
(re.mMethod == Reminders.METHOD_DEFAULT && mMethod == Reminders.METHOD_ALERT) ||
(re.mMethod == Reminders.METHOD_ALERT && mMethod == Reminders.METHOD_DEFAULT);
}
@Override
public String toString() {
return "ReminderEntry min=" + mMinutes + " meth=" + mMethod;
}
/**
* Comparison function for a sort ordered primarily descending by minutes,
* secondarily ascending by method type.
*/
@Override
public int compareTo(ReminderEntry re) {
if (re.mMinutes != mMinutes) {
return re.mMinutes - mMinutes;
}
if (re.mMethod != mMethod) {
return mMethod - re.mMethod;
}
return 0;
}
/** Returns the minutes. */
public int getMinutes() {
return mMinutes;
}
/** Returns the alert method. */
public int getMethod() {
return mMethod;
}
}
}

View File

@ -15,8 +15,6 @@
*/
package com.android.calendar;
import org.sufficientlysecure.standalonecalendar.R;
import android.content.SearchRecentSuggestionsProvider;
public class CalendarRecentSuggestionsProvider extends SearchRecentSuggestionsProvider {

View File

@ -37,10 +37,10 @@ import android.widget.LinearLayout;
import com.android.calendar.selectcalendars.SelectCalendarsSyncFragment;
import org.sufficientlysecure.standalonecalendar.R;
import java.util.List;
import ws.xsoh.etar.R;
public class CalendarSettingsActivity extends PreferenceActivity {
private static final int CHECK_ACCOUNTS_DELAY = 3000;
private Account[] mAccounts;

View File

@ -8,11 +8,11 @@ import android.text.format.DateUtils;
import android.text.format.Time;
import android.view.LayoutInflater;
import org.sufficientlysecure.standalonecalendar.R;
import java.util.Formatter;
import java.util.Locale;
import ws.xsoh.etar.R;
/**
* Created by xsoh64 on 7/21/15.
*/

View File

@ -16,8 +16,6 @@
package com.android.calendar;
import org.sufficientlysecure.standalonecalendar.R;
import android.content.AsyncQueryHandler;
import android.content.ContentResolver;
import android.content.ContentValues;
@ -43,34 +41,51 @@ public class CalendarUtils {
private static final boolean DEBUG = false;
private static final String TAG = "CalendarUtils";
/**
* A helper method for writing a String value to the preferences
* asynchronously.
*
* @param context A context with access to the correct preferences
* @param key The preference to write to
* @param value The value to write
*/
public static void setSharedPreference(SharedPreferences prefs, String key, String value) {
// SharedPreferences prefs = getSharedPreferences(context);
SharedPreferences.Editor editor = prefs.edit();
editor.putString(key, value);
editor.apply();
}
/**
* A helper method for writing a boolean value to the preferences
* asynchronously.
*
* @param context A context with access to the correct preferences
* @param key The preference to write to
* @param value The value to write
*/
public static void setSharedPreference(SharedPreferences prefs, String key, boolean value) {
// SharedPreferences prefs = getSharedPreferences(context, prefsName);
SharedPreferences.Editor editor = prefs.edit();
editor.putBoolean(key, value);
editor.apply();
}
/**
* Return a properly configured SharedPreferences instance
*/
public static SharedPreferences getSharedPreferences(Context context, String prefsName) {
return context.getSharedPreferences(prefsName, Context.MODE_PRIVATE);
}
/**
* This class contains methods specific to reading and writing time zone
* values.
*/
public static class TimeZoneUtils {
private static final String[] TIMEZONE_TYPE_ARGS = { CalendarCache.KEY_TIMEZONE_TYPE };
private static final String[] TIMEZONE_INSTANCES_ARGS =
{ CalendarCache.KEY_TIMEZONE_INSTANCES };
public static final String[] CALENDAR_CACHE_POJECTION = {
CalendarCache.KEY, CalendarCache.VALUE
};
private static StringBuilder mSB = new StringBuilder(50);
private static Formatter mF = new Formatter(mSB, Locale.getDefault());
private volatile static boolean mFirstTZRequest = true;
private volatile static boolean mTZQueryInProgress = false;
private volatile static boolean mUseHomeTZ = false;
private volatile static String mHomeTZ = Time.getCurrentTimezone();
private static HashSet<Runnable> mTZCallbacks = new HashSet<Runnable>();
private static int mToken = 1;
private static AsyncTZHandler mHandler;
// The name of the shared preferences file. This name must be maintained for historical
// reasons, as it's what PreferenceManager assigned the first time the file was created.
private final String mPrefsName;
/**
* This is the key used for writing whether or not a home time zone should
* be used in the Calendar app to the Calendar Preferences.
@ -81,65 +96,21 @@ public class CalendarUtils {
* home time zones are enabled for the Calendar app.
*/
public static final String KEY_HOME_TZ = "preferences_home_tz";
/**
* This is a helper class for handling the async queries and updates for the
* time zone settings in Calendar.
*/
private class AsyncTZHandler extends AsyncQueryHandler {
public AsyncTZHandler(ContentResolver cr) {
super(cr);
}
@Override
protected void onQueryComplete(int token, Object cookie, Cursor cursor) {
synchronized (mTZCallbacks) {
if (cursor == null) {
mTZQueryInProgress = false;
mFirstTZRequest = true;
return;
}
boolean writePrefs = false;
// Check the values in the db
int keyColumn = cursor.getColumnIndexOrThrow(CalendarCache.KEY);
int valueColumn = cursor.getColumnIndexOrThrow(CalendarCache.VALUE);
while(cursor.moveToNext()) {
String key = cursor.getString(keyColumn);
String value = cursor.getString(valueColumn);
if (TextUtils.equals(key, CalendarCache.KEY_TIMEZONE_TYPE)) {
boolean useHomeTZ = !TextUtils.equals(
value, CalendarCache.TIMEZONE_TYPE_AUTO);
if (useHomeTZ != mUseHomeTZ) {
writePrefs = true;
mUseHomeTZ = useHomeTZ;
}
} else if (TextUtils.equals(
key, CalendarCache.KEY_TIMEZONE_INSTANCES_PREVIOUS)) {
if (!TextUtils.isEmpty(value) && !TextUtils.equals(mHomeTZ, value)) {
writePrefs = true;
mHomeTZ = value;
}
}
}
cursor.close();
if (writePrefs) {
SharedPreferences prefs = getSharedPreferences((Context)cookie, mPrefsName);
// Write the prefs
setSharedPreference(prefs, KEY_HOME_TZ_ENABLED, mUseHomeTZ);
setSharedPreference(prefs, KEY_HOME_TZ, mHomeTZ);
}
mTZQueryInProgress = false;
for (Runnable callback : mTZCallbacks) {
if (callback != null) {
callback.run();
}
}
mTZCallbacks.clear();
}
}
}
private static final String[] TIMEZONE_TYPE_ARGS = {CalendarCache.KEY_TIMEZONE_TYPE};
private static final String[] TIMEZONE_INSTANCES_ARGS =
{CalendarCache.KEY_TIMEZONE_INSTANCES};
private static StringBuilder mSB = new StringBuilder(50);
private static Formatter mF = new Formatter(mSB, Locale.getDefault());
private volatile static boolean mFirstTZRequest = true;
private volatile static boolean mTZQueryInProgress = false;
private volatile static boolean mUseHomeTZ = false;
private volatile static String mHomeTZ = Time.getCurrentTimezone();
private static HashSet<Runnable> mTZCallbacks = new HashSet<Runnable>();
private static int mToken = 1;
private static AsyncTZHandler mHandler;
// The name of the shared preferences file. This name must be maintained for historical
// reasons, as it's what PreferenceManager assigned the first time the file was created.
private final String mPrefsName;
/**
* The name of the file where the shared prefs for Calendar are stored
@ -315,40 +286,64 @@ public class CalendarUtils {
getTimeZone(context, callback);
}
}
/**
* This is a helper class for handling the async queries and updates for the
* time zone settings in Calendar.
*/
private class AsyncTZHandler extends AsyncQueryHandler {
public AsyncTZHandler(ContentResolver cr) {
super(cr);
}
@Override
protected void onQueryComplete(int token, Object cookie, Cursor cursor) {
synchronized (mTZCallbacks) {
if (cursor == null) {
mTZQueryInProgress = false;
mFirstTZRequest = true;
return;
}
boolean writePrefs = false;
// Check the values in the db
int keyColumn = cursor.getColumnIndexOrThrow(CalendarCache.KEY);
int valueColumn = cursor.getColumnIndexOrThrow(CalendarCache.VALUE);
while (cursor.moveToNext()) {
String key = cursor.getString(keyColumn);
String value = cursor.getString(valueColumn);
if (TextUtils.equals(key, CalendarCache.KEY_TIMEZONE_TYPE)) {
boolean useHomeTZ = !TextUtils.equals(
value, CalendarCache.TIMEZONE_TYPE_AUTO);
if (useHomeTZ != mUseHomeTZ) {
writePrefs = true;
mUseHomeTZ = useHomeTZ;
}
} else if (TextUtils.equals(
key, CalendarCache.KEY_TIMEZONE_INSTANCES_PREVIOUS)) {
if (!TextUtils.isEmpty(value) && !TextUtils.equals(mHomeTZ, value)) {
writePrefs = true;
mHomeTZ = value;
}
}
}
cursor.close();
if (writePrefs) {
SharedPreferences prefs = getSharedPreferences((Context) cookie, mPrefsName);
// Write the prefs
setSharedPreference(prefs, KEY_HOME_TZ_ENABLED, mUseHomeTZ);
setSharedPreference(prefs, KEY_HOME_TZ, mHomeTZ);
}
mTZQueryInProgress = false;
for (Runnable callback : mTZCallbacks) {
if (callback != null) {
callback.run();
}
}
mTZCallbacks.clear();
}
}
}
}
/**
* A helper method for writing a String value to the preferences
* asynchronously.
*
* @param context A context with access to the correct preferences
* @param key The preference to write to
* @param value The value to write
*/
public static void setSharedPreference(SharedPreferences prefs, String key, String value) {
// SharedPreferences prefs = getSharedPreferences(context);
SharedPreferences.Editor editor = prefs.edit();
editor.putString(key, value);
editor.apply();
}
/**
* A helper method for writing a boolean value to the preferences
* asynchronously.
*
* @param context A context with access to the correct preferences
* @param key The preference to write to
* @param value The value to write
*/
public static void setSharedPreference(SharedPreferences prefs, String key, boolean value) {
// SharedPreferences prefs = getSharedPreferences(context, prefsName);
SharedPreferences.Editor editor = prefs.edit();
editor.putBoolean(key, value);
editor.apply();
}
/** Return a properly configured SharedPreferences instance */
public static SharedPreferences getSharedPreferences(Context context, String prefsName) {
return context.getSharedPreferences(prefsName, Context.MODE_PRIVATE);
}
}

View File

@ -16,10 +16,6 @@
package com.android.calendar;
import org.sufficientlysecure.standalonecalendar.R;
import com.android.calendar.CalendarController.ViewType;
import android.content.Context;
import android.os.Handler;
import android.text.format.DateUtils;
@ -30,9 +26,13 @@ import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.TextView;
import com.android.calendar.CalendarController.ViewType;
import java.util.Formatter;
import java.util.Locale;
import ws.xsoh.etar.R;
/*
* The MenuSpinnerAdapter defines the look of the ActionBar's pull down menu
@ -43,40 +43,32 @@ import java.util.Locale;
public class CalendarViewAdapter extends BaseAdapter {
public static final int DAY_BUTTON_INDEX = 0;
public static final int WEEK_BUTTON_INDEX = 1;
public static final int MONTH_BUTTON_INDEX = 2;
public static final int AGENDA_BUTTON_INDEX = 3;
static final int VIEW_TYPE_NUM = 1; // Increase this if you add more view types
private static final String TAG = "MenuSpinnerAdapter";
// Defines the types of view returned by this spinner
private static final int BUTTON_VIEW_TYPE = 0;
private final String mButtonNames []; // Text on buttons
private final LayoutInflater mInflater;
private final Context mContext;
private final Formatter mFormatter;
private final StringBuilder mStringBuilder;
private final boolean mShowDate; // Spinner mode indicator (view name or view name with date)
// Used to define the look of the menu button according to the current view:
// Day view: show day of the week + full date underneath
// Week view: show the month + year
// Month view: show the month + year
// Agenda view: show day of the week + full date underneath
private int mCurrentMainView;
private final LayoutInflater mInflater;
// Defines the types of view returned by this spinner
private static final int BUTTON_VIEW_TYPE = 0;
static final int VIEW_TYPE_NUM = 1; // Increase this if you add more view types
public static final int DAY_BUTTON_INDEX = 0;
public static final int WEEK_BUTTON_INDEX = 1;
public static final int MONTH_BUTTON_INDEX = 2;
public static final int AGENDA_BUTTON_INDEX = 3;
// The current selected event's time, used to calculate the date and day of the week
// for the buttons.
private long mMilliTime;
private String mTimeZone;
private long mTodayJulianDay;
private final Context mContext;
private final Formatter mFormatter;
private final StringBuilder mStringBuilder;
private Handler mMidnightHandler = null; // Used to run a time update every midnight
private final boolean mShowDate; // Spinner mode indicator (view name or view name with date)
// Updates time specific variables (time-zone, today's Julian day).
private final Runnable mTimeUpdater = new Runnable() {
@Override

View File

@ -16,13 +16,11 @@
package com.android.calendar;
import org.sufficientlysecure.standalonecalendar.R;
import java.io.IOException;
import android.content.Context;
import android.os.Bundle;
import java.io.IOException;
public interface CloudNotificationBackplane {
public boolean open(Context context);
public boolean subscribeToGroup(String senderId, String account, String groupId)

View File

@ -16,8 +16,6 @@
package com.android.calendar;
import org.sufficientlysecure.standalonecalendar.R;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Paint;
@ -35,26 +33,21 @@ import android.view.View;
*/
public class ColorChipView extends View {
private static final String TAG = "ColorChipView";
public static final int DRAW_FULL = 0;
// Style of drawing
// Full rectangle for accepted events
// Border for tentative events
// Cross-hatched with 50% transparency for declined events
public static final int DRAW_FULL = 0;
public static final int DRAW_BORDER = 1;
public static final int DRAW_FADED = 2;
private static final String TAG = "ColorChipView";
private static final int DEF_BORDER_WIDTH = 4;
int mBorderWidth = DEF_BORDER_WIDTH;
int mColor;
private int mDrawStyle = DRAW_FULL;
private float mDefStrokeWidth;
private Paint mPaint;
private static final int DEF_BORDER_WIDTH = 4;
int mBorderWidth = DEF_BORDER_WIDTH;
int mColor;
public ColorChipView(Context context) {
super(context);
init();

View File

@ -16,10 +16,6 @@
package com.android.calendar;
import org.sufficientlysecure.standalonecalendar.R;
import com.android.calendar.event.EditEventHelper.AttendeeItem;
import android.content.Context;
import android.graphics.drawable.Drawable;
import android.net.Uri;
@ -32,6 +28,8 @@ import android.util.Log;
import android.view.View;
import android.widget.ImageView;
import com.android.calendar.event.EditEventHelper.AttendeeItem;
import java.io.InputStream;
/**
@ -41,87 +39,14 @@ public class ContactsAsyncHelper extends Handler {
private static final boolean DBG = false;
private static final String LOG_TAG = "ContactsAsyncHelper";
private static ContactsAsyncHelper mInstance = null;
/**
* 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 EVENT_LOAD_DRAWABLE = 2;
private static final int DEFAULT_TOKEN = -1;
private static ContactsAsyncHelper mInstance = null;
// static objects
private static Handler sThreadHandler;
private static final class WorkerArgs {
public Context context;
public ImageView view;
public Uri uri;
public int defaultResource;
public Object result;
public AttendeeItem item;
public Runnable callback;
}
/**
* 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_DRAWABLE:
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
*/
@ -252,4 +177,74 @@ public class ContactsAsyncHelper extends Handler {
default:
}
}
/**
* 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);
}
private static final class WorkerArgs {
public Context context;
public ImageView view;
public Uri uri;
public int defaultResource;
public Object result;
public AttendeeItem item;
public Runnable callback;
}
/**
* 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_DRAWABLE:
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();
}
}
}

View File

@ -16,11 +16,6 @@
package com.android.calendar;
import org.sufficientlysecure.standalonecalendar.R;
import com.android.calendar.CalendarController.EventInfo;
import com.android.calendar.CalendarController.EventType;
import android.app.Fragment;
import android.content.Context;
import android.os.Bundle;
@ -35,19 +30,22 @@ import android.widget.ProgressBar;
import android.widget.ViewSwitcher;
import android.widget.ViewSwitcher.ViewFactory;
import com.android.calendar.CalendarController.EventInfo;
import com.android.calendar.CalendarController.EventType;
import ws.xsoh.etar.R;
/**
* This is the base class for Day and Week Activities.
*/
public class DayFragment extends Fragment implements CalendarController.EventHandler, ViewFactory {
protected static final String BUNDLE_KEY_RESTORE_TIME = "key_restore_time";
/**
* The view id used for all the views we create. It's OK to have all child
* views have the same ID. This ID is used to pick which view receives
* focus when a view hierarchy is saved / restore
*/
private static final int VIEW_ID = 1;
protected static final String BUNDLE_KEY_RESTORE_TIME = "key_restore_time";
protected ProgressBar mProgressBar;
protected ViewSwitcher mViewSwitcher;
protected Animation mInAnimationForward;

View File

@ -16,8 +16,6 @@
package com.android.calendar;
import org.sufficientlysecure.standalonecalendar.R;
import android.util.MonthDisplayHelper;
/**
@ -73,6 +71,11 @@ public class DayOfMonthCursor extends MonthDisplayHelper {
return getDayAt(mRow, mColumn);
}
public void setSelectedDayOfMonth(int dayOfMonth) {
mRow = getRowOf(dayOfMonth);
mColumn = getColumnOf(dayOfMonth);
}
/**
* @return 0 if the selection is in the current month, otherwise -1 or +1
* depending on whether the selection is in the first or last row.
@ -87,11 +90,6 @@ public class DayOfMonthCursor extends MonthDisplayHelper {
return 1;
}
public void setSelectedDayOfMonth(int dayOfMonth) {
mRow = getRowOf(dayOfMonth);
mColumn = getColumnOf(dayOfMonth);
}
public boolean isSelected(int row, int column) {
return (mRow == row) && (mColumn == column);
}

View File

@ -25,10 +25,10 @@ import android.graphics.Rect;
import android.graphics.Typeface;
import android.graphics.drawable.Drawable;
import org.sufficientlysecure.standalonecalendar.R;
import java.text.NumberFormat;
import ws.xsoh.etar.R;
/**
* A custom view to draw the day of the month in the today button in the options menu
*/

View File

@ -80,8 +80,6 @@ import android.widget.ViewSwitcher;
import com.android.calendar.CalendarController.EventType;
import com.android.calendar.CalendarController.ViewType;
import org.sufficientlysecure.standalonecalendar.R;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Calendar;
@ -90,6 +88,8 @@ import java.util.Locale;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import ws.xsoh.etar.R;
/**
* View for multi-day view. So far only 1 and 7 day have been tested.
*/

View File

@ -16,11 +16,6 @@
package com.android.calendar;
import org.sufficientlysecure.standalonecalendar.R;
import com.android.calendar.event.EditEventHelper;
import com.android.calendarcommon2.EventRecurrence;
import android.app.Activity;
import android.app.AlertDialog;
import android.app.Dialog;
@ -38,9 +33,14 @@ import android.text.format.Time;
import android.widget.ArrayAdapter;
import android.widget.Button;
import com.android.calendar.event.EditEventHelper;
import com.android.calendarcommon2.EventRecurrence;
import java.util.ArrayList;
import java.util.Arrays;
import ws.xsoh.etar.R;
/**
* A helper class for deleting events. If a normal event is selected for
* deletion, then this pops up a confirmation dialog. If the user confirms,
@ -63,20 +63,6 @@ import java.util.Arrays;
* {@link #delete()} multiple times).
*/
public class DeleteEventHelper {
private final Activity mParent;
private Context mContext;
private long mStartMillis;
private long mEndMillis;
private CalendarEventModel mModel;
/**
* If true, then call finish() on the parent activity when done.
*/
private boolean mExitWhenDone;
// the runnable to execute when the delete is confirmed
private Runnable mCallback;
/**
* These are the corresponding indices into the array of strings
* "R.array.delete_repeating_labels" in the resource file.
@ -84,7 +70,17 @@ public class DeleteEventHelper {
public static final int DELETE_SELECTED = 0;
public static final int DELETE_ALL_FOLLOWING = 1;
public static final int DELETE_ALL = 2;
private final Activity mParent;
private Context mContext;
private long mStartMillis;
private long mEndMillis;
private CalendarEventModel mModel;
/**
* If true, then call finish() on the parent activity when done.
*/
private boolean mExitWhenDone;
// the runnable to execute when the delete is confirmed
private Runnable mCallback;
private int mWhichDelete;
private ArrayList<Integer> mWhichIndex;
private AlertDialog mAlertDialog;
@ -95,11 +91,67 @@ public class DeleteEventHelper {
private AsyncQueryService mService;
private DeleteNotifyListener mDeleteStartedListener = null;
/**
* This callback is used when a normal event is deleted.
*/
private DialogInterface.OnClickListener mDeleteNormalDialogListener =
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int button) {
deleteStarted();
long id = mModel.mId; // mCursor.getInt(mEventIndexId);
Uri uri = ContentUris.withAppendedId(CalendarContract.Events.CONTENT_URI, id);
mService.startDelete(mService.getNextToken(), null, uri, null, null, Utils.UNDO_DELAY);
if (mCallback != null) {
mCallback.run();
}
if (mExitWhenDone) {
mParent.finish();
}
}
};
/**
* This callback is used when an exception to an event is deleted
*/
private DialogInterface.OnClickListener mDeleteExceptionDialogListener =
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int button) {
deleteStarted();
deleteExceptionEvent();
if (mCallback != null) {
mCallback.run();
}
if (mExitWhenDone) {
mParent.finish();
}
}
};
/**
* This callback is used when a list item for a repeating event is selected
*/
private DialogInterface.OnClickListener mDeleteListListener =
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int button) {
// set mWhichDelete to the delete type at that index
mWhichDelete = mWhichIndex.get(button);
public interface DeleteNotifyListener {
public void onDeleteStarted();
}
// Enable the "ok" button now that the user has selected which
// events in the series to delete.
Button ok = mAlertDialog.getButton(DialogInterface.BUTTON_POSITIVE);
ok.setEnabled(true);
}
};
/**
* This callback is used when a repeating event is deleted.
*/
private DialogInterface.OnClickListener mDeleteRepeatingDialogListener =
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int button) {
deleteStarted();
if (mWhichDelete != -1) {
deleteRepeatingEvent(mWhichDelete);
}
}
};
public DeleteEventHelper(Context context, Activity parentActivity, boolean exitWhenDone) {
if (exitWhenDone && parentActivity == null) {
@ -129,71 +181,6 @@ public class DeleteEventHelper {
mExitWhenDone = exitWhenDone;
}
/**
* This callback is used when a normal event is deleted.
*/
private DialogInterface.OnClickListener mDeleteNormalDialogListener =
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int button) {
deleteStarted();
long id = mModel.mId; // mCursor.getInt(mEventIndexId);
Uri uri = ContentUris.withAppendedId(CalendarContract.Events.CONTENT_URI, id);
mService.startDelete(mService.getNextToken(), null, uri, null, null, Utils.UNDO_DELAY);
if (mCallback != null) {
mCallback.run();
}
if (mExitWhenDone) {
mParent.finish();
}
}
};
/**
* This callback is used when an exception to an event is deleted
*/
private DialogInterface.OnClickListener mDeleteExceptionDialogListener =
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int button) {
deleteStarted();
deleteExceptionEvent();
if (mCallback != null) {
mCallback.run();
}
if (mExitWhenDone) {
mParent.finish();
}
}
};
/**
* This callback is used when a list item for a repeating event is selected
*/
private DialogInterface.OnClickListener mDeleteListListener =
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int button) {
// set mWhichDelete to the delete type at that index
mWhichDelete = mWhichIndex.get(button);
// Enable the "ok" button now that the user has selected which
// events in the series to delete.
Button ok = mAlertDialog.getButton(DialogInterface.BUTTON_POSITIVE);
ok.setEnabled(true);
}
};
/**
* This callback is used when a repeating event is deleted.
*/
private DialogInterface.OnClickListener mDeleteRepeatingDialogListener =
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int button) {
deleteStarted();
if (mWhichDelete != -1) {
deleteRepeatingEvent(mWhichDelete);
}
}
};
/**
* Does the required processing for deleting an event, which includes
* first popping up a dialog asking for confirmation (if the event is
@ -465,4 +452,8 @@ public class DeleteEventHelper {
mAlertDialog.dismiss();
}
}
public interface DeleteNotifyListener {
public void onDeleteStarted();
}
}

View File

@ -16,14 +16,14 @@
package com.android.calendar;
import org.sufficientlysecure.standalonecalendar.R;
import android.app.Activity;
import android.app.AlertDialog;
import android.content.DialogInterface;
import android.content.DialogInterface.OnDismissListener;
import android.widget.Button;
import ws.xsoh.etar.R;
/**
* A helper class for editing the response to an invitation when the invitation
* is a repeating event.
@ -39,6 +39,21 @@ public class EditResponseHelper implements DialogInterface.OnClickListener, OnDi
* and is invoked when the "Ok" button is selected.
*/
private DialogInterface.OnClickListener mDialogListener;
/**
* This callback is used when a list item is selected
*/
private DialogInterface.OnClickListener mListListener =
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
mWhichEvents = which;
// Enable the "ok" button now that the user has selected which
// events in the series to delete.
Button ok = mAlertDialog.getButton(DialogInterface.BUTTON_POSITIVE);
ok.setEnabled(true);
}
};
private DialogInterface.OnDismissListener mDismissListener;
public EditResponseHelper(Activity parent) {
mParent = parent;
@ -93,24 +108,6 @@ public class EditResponseHelper implements DialogInterface.OnClickListener, OnDi
mClickedOk = clickedOk;
}
/**
* This callback is used when a list item is selected
*/
private DialogInterface.OnClickListener mListListener =
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
mWhichEvents = which;
// Enable the "ok" button now that the user has selected which
// events in the series to delete.
Button ok = mAlertDialog.getButton(DialogInterface.BUTTON_POSITIVE);
ok.setEnabled(true);
}
};
private DialogInterface.OnDismissListener mDismissListener;
/**
* Set the dismiss listener to be called when the dialog is ended. There,
* use getWhichEvents() to see how the dialog was dismissed; if it returns

View File

@ -16,11 +16,6 @@
package com.android.calendar;
import org.sufficientlysecure.standalonecalendar.R;
import com.android.common.contacts.BaseEmailAddressAdapter;
import com.android.ex.chips.AccountSpecifier;
import android.content.Context;
import android.text.TextUtils;
import android.view.LayoutInflater;
@ -28,6 +23,11 @@ import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import com.android.common.contacts.BaseEmailAddressAdapter;
import com.android.ex.chips.AccountSpecifier;
import ws.xsoh.etar.R;
/**
* An adaptation of {@link BaseEmailAddressAdapter} for the Email app. The main
* purpose of the class is to bind the generic implementation to the resources

View File

@ -16,8 +16,6 @@
package com.android.calendar;
import org.sufficientlysecure.standalonecalendar.R;
import android.content.ContentResolver;
import android.content.ContentUris;
import android.content.Context;
@ -39,6 +37,8 @@ import java.util.Arrays;
import java.util.Iterator;
import java.util.concurrent.atomic.AtomicInteger;
import ws.xsoh.etar.R;
// TODO: should Event be Parcelable so it can be passed via Intents?
public class Event implements Cloneable {
@ -60,10 +60,6 @@ public class Event implements Cloneable {
private static final String SORT_ALLDAY_BY =
"startDay ASC, endDay DESC, title ASC";
private static final String DISPLAY_AS_ALLDAY = "dispAllday";
private static final String EVENTS_WHERE = DISPLAY_AS_ALLDAY + "=0";
private static final String ALLDAY_WHERE = DISPLAY_AS_ALLDAY + "=1";
// The projection to use when querying instances to build a list of events
public static final String[] EVENT_PROJECTION = new String[] {
Instances.TITLE, // 0
@ -88,7 +84,8 @@ public class Event implements Cloneable {
Instances.ALL_DAY + "=1 OR (" + Instances.END + "-" + Instances.BEGIN + ")>="
+ DateUtils.DAY_IN_MILLIS + " AS " + DISPLAY_AS_ALLDAY, // 19
};
private static final String EVENTS_WHERE = DISPLAY_AS_ALLDAY + "=0";
private static final String ALLDAY_WHERE = DISPLAY_AS_ALLDAY + "=1";
// The indices for the projection array above.
private static final int PROJECTION_TITLE_INDEX = 0;
private static final int PROJECTION_LOCATION_INDEX = 1;
@ -109,6 +106,8 @@ public class Event implements Cloneable {
private static final int PROJECTION_ORGANIZER_INDEX = 17;
private static final int PROJECTION_GUESTS_CAN_INVITE_OTHERS_INDEX = 18;
private static final int PROJECTION_DISPLAY_AS_ALLDAY = 19;
private static String mNoTitleString;
private static int mNoColorColor;
static {
if (!Utils.isJellybeanOrLater()) {
@ -116,9 +115,6 @@ public class Event implements Cloneable {
}
}
private static String mNoTitleString;
private static int mNoColorColor;
public long id;
public int color;
public CharSequence title;
@ -134,69 +130,22 @@ public class Event implements Cloneable {
public long startMillis; // UTC milliseconds since the epoch
public long endMillis; // UTC milliseconds since the epoch
private int mColumn;
private int mMaxColumns;
public boolean hasAlarm;
public boolean isRepeating;
public int selfAttendeeStatus;
// The coordinates of the event rectangle drawn on the screen.
public float left;
public float right;
public float top;
public float bottom;
// These 4 fields are used for navigating among events within the selected
// hour in the Day and Week view.
public Event nextRight;
public Event nextLeft;
public Event nextUp;
public Event nextDown;
@Override
public final Object clone() throws CloneNotSupportedException {
super.clone();
Event e = new Event();
e.title = title;
e.color = color;
e.location = location;
e.allDay = allDay;
e.startDay = startDay;
e.endDay = endDay;
e.startTime = startTime;
e.endTime = endTime;
e.startMillis = startMillis;
e.endMillis = endMillis;
e.hasAlarm = hasAlarm;
e.isRepeating = isRepeating;
e.selfAttendeeStatus = selfAttendeeStatus;
e.organizer = organizer;
e.guestsCanModify = guestsCanModify;
return e;
}
public final void copyTo(Event dest) {
dest.id = id;
dest.title = title;
dest.color = color;
dest.location = location;
dest.allDay = allDay;
dest.startDay = startDay;
dest.endDay = endDay;
dest.startTime = startTime;
dest.endTime = endTime;
dest.startMillis = startMillis;
dest.endMillis = endMillis;
dest.hasAlarm = hasAlarm;
dest.isRepeating = isRepeating;
dest.selfAttendeeStatus = selfAttendeeStatus;
dest.organizer = organizer;
dest.guestsCanModify = guestsCanModify;
}
private int mColumn;
private int mMaxColumns;
public static final Event newInstance() {
Event e = new Event();
@ -538,6 +487,49 @@ public class Event implements Cloneable {
return 64;
}
@Override
public final Object clone() throws CloneNotSupportedException {
super.clone();
Event e = new Event();
e.title = title;
e.color = color;
e.location = location;
e.allDay = allDay;
e.startDay = startDay;
e.endDay = endDay;
e.startTime = startTime;
e.endTime = endTime;
e.startMillis = startMillis;
e.endMillis = endMillis;
e.hasAlarm = hasAlarm;
e.isRepeating = isRepeating;
e.selfAttendeeStatus = selfAttendeeStatus;
e.organizer = organizer;
e.guestsCanModify = guestsCanModify;
return e;
}
public final void copyTo(Event dest) {
dest.id = id;
dest.title = title;
dest.color = color;
dest.location = location;
dest.allDay = allDay;
dest.startDay = startDay;
dest.endDay = endDay;
dest.startTime = startTime;
dest.endTime = endTime;
dest.startMillis = startMillis;
dest.endMillis = endMillis;
dest.hasAlarm = hasAlarm;
dest.isRepeating = isRepeating;
dest.selfAttendeeStatus = selfAttendeeStatus;
dest.organizer = organizer;
dest.guestsCanModify = guestsCanModify;
}
public final void dump() {
Log.e("Cal", "+-----------------------------------------+");
Log.e("Cal", "+ id = " + id);
@ -605,38 +597,38 @@ public class Event implements Cloneable {
return text;
}
public void setColumn(int column) {
mColumn = column;
}
public int getColumn() {
return mColumn;
}
public void setMaxColumns(int maxColumns) {
mMaxColumns = maxColumns;
public void setColumn(int column) {
mColumn = column;
}
public int getMaxColumns() {
return mMaxColumns;
}
public void setStartMillis(long startMillis) {
this.startMillis = startMillis;
public void setMaxColumns(int maxColumns) {
mMaxColumns = maxColumns;
}
public long getStartMillis() {
return startMillis;
}
public void setEndMillis(long endMillis) {
this.endMillis = endMillis;
public void setStartMillis(long startMillis) {
this.startMillis = startMillis;
}
public long getEndMillis() {
return endMillis;
}
public void setEndMillis(long endMillis) {
this.endMillis = endMillis;
}
public boolean drawAsAllday() {
// Use >= so we'll pick up Exchange allday events
return allDay || endMillis - startMillis >= DateUtils.DAY_IN_MILLIS;

View File

@ -16,8 +16,6 @@
package com.android.calendar;
import org.sufficientlysecure.standalonecalendar.R;
import android.graphics.Rect;
public class EventGeometry {

View File

@ -32,11 +32,11 @@ import android.widget.Toast;
import com.android.calendar.CalendarEventModel.ReminderEntry;
import org.sufficientlysecure.standalonecalendar.R;
import java.util.ArrayList;
import java.util.List;
import ws.xsoh.etar.R;
import static android.provider.CalendarContract.Attendees.ATTENDEE_STATUS;
import static android.provider.CalendarContract.EXTRA_EVENT_BEGIN_TIME;
import static android.provider.CalendarContract.EXTRA_EVENT_END_TIME;

View File

@ -16,13 +16,6 @@
package com.android.calendar;
import org.sufficientlysecure.standalonecalendar.R;
import static android.provider.CalendarContract.EXTRA_EVENT_ALL_DAY;
import static android.provider.CalendarContract.EXTRA_EVENT_BEGIN_TIME;
import static android.provider.CalendarContract.EXTRA_EVENT_END_TIME;
import static com.android.calendar.CalendarController.EVENT_EDIT_ON_LAUNCH;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.ObjectAnimator;
@ -116,6 +109,13 @@ import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import ws.xsoh.etar.R;
import static android.provider.CalendarContract.EXTRA_EVENT_ALL_DAY;
import static android.provider.CalendarContract.EXTRA_EVENT_BEGIN_TIME;
import static android.provider.CalendarContract.EXTRA_EVENT_END_TIME;
import static com.android.calendar.CalendarController.EVENT_EDIT_ON_LAUNCH;
public class EventInfoFragment extends DialogFragment implements OnCheckedChangeListener,
CalendarController.EventHandler, OnClickListener, DeleteEventHelper.DeleteNotifyListener,
OnColorSelectedListener {
@ -124,9 +124,11 @@ public class EventInfoFragment extends DialogFragment implements OnCheckedChange
public static final String TAG = "EventInfoFragment";
public static final String COLOR_PICKER_DIALOG_TAG = "EventColorPickerDialog";
private static final int REQUEST_CODE_COLOR_PICKER = 0;
// Style of view
public static final int FULL_WINDOW_STYLE = 0;
public static final int DIALOG_WINDOW_STYLE = 1;
public static final int COLORS_INDEX_COLOR = 1;
public static final int COLORS_INDEX_COLOR_KEY = 2;
protected static final String BUNDLE_KEY_EVENT_ID = "key_event_id";
protected static final String BUNDLE_KEY_START_MILLIS = "key_start_millis";
protected static final String BUNDLE_KEY_END_MILLIS = "key_end_millis";
@ -148,25 +150,38 @@ public class EventInfoFragment extends DialogFragment implements OnCheckedChange
protected static final String BUNDLE_KEY_RESPONSE_WHICH_EVENTS = "key_response_which_events";
protected static final String BUNDLE_KEY_REMINDER_MINUTES = "key_reminder_minutes";
protected static final String BUNDLE_KEY_REMINDER_METHODS = "key_reminder_methods";
private static final String PERIOD_SPACE = ". ";
private static final String NO_EVENT_COLOR = "";
/**
* These are the corresponding indices into the array of strings
* "R.array.change_response_labels" in the resource file.
*/
static final int UPDATE_SINGLE = 0;
static final int UPDATE_ALL = 1;
// Style of view
public static final int FULL_WINDOW_STYLE = 0;
public static final int DIALOG_WINDOW_STYLE = 1;
private int mWindowStyle = DIALOG_WINDOW_STYLE;
static final String[] CALENDARS_PROJECTION = new String[]{
Calendars._ID, // 0
Calendars.CALENDAR_DISPLAY_NAME, // 1
Calendars.OWNER_ACCOUNT, // 2
Calendars.CAN_ORGANIZER_RESPOND, // 3
Calendars.ACCOUNT_NAME, // 4
Calendars.ACCOUNT_TYPE // 5
};
static final int CALENDARS_INDEX_DISPLAY_NAME = 1;
static final int CALENDARS_INDEX_OWNER_ACCOUNT = 2;
static final int CALENDARS_INDEX_OWNER_CAN_RESPOND = 3;
static final int CALENDARS_INDEX_ACCOUNT_NAME = 4;
static final int CALENDARS_INDEX_ACCOUNT_TYPE = 5;
static final String CALENDARS_WHERE = Calendars._ID + "=?";
static final String CALENDARS_DUPLICATE_NAME_WHERE = Calendars.CALENDAR_DISPLAY_NAME + "=?";
static final String CALENDARS_VISIBLE_WHERE = Calendars.VISIBLE + "=?";
static final String[] COLORS_PROJECTION = new String[]{
Colors._ID, // 0
Colors.COLOR, // 1
Colors.COLOR_KEY // 2
};
static final String COLORS_WHERE = Colors.ACCOUNT_NAME + "=? AND " + Colors.ACCOUNT_TYPE +
"=? AND " + Colors.COLOR_TYPE + "=" + Colors.TYPE_EVENT;
private static final int REQUEST_CODE_COLOR_PICKER = 0;
private static final String PERIOD_SPACE = ". ";
private static final String NO_EVENT_COLOR = "";
// Query tokens for QueryHandler
private static final int TOKEN_QUERY_EVENT = 1 << 0;
private static final int TOKEN_QUERY_CALENDARS = 1 << 1;
@ -175,13 +190,9 @@ public class EventInfoFragment extends DialogFragment implements OnCheckedChange
private static final int TOKEN_QUERY_REMINDERS = 1 << 4;
private static final int TOKEN_QUERY_VISIBLE_CALENDARS = 1 << 5;
private static final int TOKEN_QUERY_COLORS = 1 << 6;
private static final int TOKEN_QUERY_ALL = TOKEN_QUERY_DUPLICATE_CALENDARS
| TOKEN_QUERY_ATTENDEES | TOKEN_QUERY_CALENDARS | TOKEN_QUERY_EVENT
| TOKEN_QUERY_REMINDERS | TOKEN_QUERY_VISIBLE_CALENDARS | TOKEN_QUERY_COLORS;
private int mCurrentQuery = 0;
private static final String[] EVENT_PROJECTION = new String[] {
Events._ID, // 0 do not remove; used in DeleteEventHelper
Events.TITLE, // 1 do not remove; used in DeleteEventHelper
@ -229,7 +240,6 @@ public class EventInfoFragment extends DialogFragment implements OnCheckedChange
private static final int EVENT_INDEX_CUSTOM_APP_URI = 19;
private static final int EVENT_INDEX_DTEND = 20;
private static final int EVENT_INDEX_DURATION = 21;
private static final String[] ATTENDEES_PROJECTION = new String[] {
Attendees._ID, // 0
Attendees.ATTENDEE_NAME, // 1
@ -246,6 +256,26 @@ public class EventInfoFragment extends DialogFragment implements OnCheckedChange
private static final int ATTENDEES_INDEX_STATUS = 4;
private static final int ATTENDEES_INDEX_IDENTITY = 5;
private static final int ATTENDEES_INDEX_ID_NAMESPACE = 6;
private static final String ATTENDEES_WHERE = Attendees.EVENT_ID + "=?";
private static final String ATTENDEES_SORT_ORDER = Attendees.ATTENDEE_NAME + " ASC, "
+ Attendees.ATTENDEE_EMAIL + " ASC";
private static final String[] REMINDERS_PROJECTION = new String[] {
Reminders._ID, // 0
Reminders.MINUTES, // 1
Reminders.METHOD // 2
};
private static final int REMINDERS_INDEX_ID = 0;
private static final int REMINDERS_MINUTES_ID = 1;
private static final int REMINDERS_METHOD_ID = 2;
private static final String REMINDERS_WHERE = Reminders.EVENT_ID + "=?";
private static final int FADE_IN_TIME = 300; // in milliseconds
private static final int LOADING_MSG_DELAY = 600; // in milliseconds
private static final int LOADING_MSG_MIN_DISPLAY_TIME = 600;
private static float mScale = 0; // Used for supporting different screen densities
private static int mCustomAppIconSize = 32;
private static int mDialogWidth = 500;
private static int mDialogHeight = 600;
private static int DIALOG_TOP_MARGIN = 8;
static {
if (!Utils.isJellybeanOrLater()) {
@ -257,69 +287,28 @@ public class EventInfoFragment extends DialogFragment implements OnCheckedChange
}
}
private static final String ATTENDEES_WHERE = Attendees.EVENT_ID + "=?";
private static final String ATTENDEES_SORT_ORDER = Attendees.ATTENDEE_NAME + " ASC, "
+ Attendees.ATTENDEE_EMAIL + " ASC";
private static final String[] REMINDERS_PROJECTION = new String[] {
Reminders._ID, // 0
Reminders.MINUTES, // 1
Reminders.METHOD // 2
};
private static final int REMINDERS_INDEX_ID = 0;
private static final int REMINDERS_MINUTES_ID = 1;
private static final int REMINDERS_METHOD_ID = 2;
private static final String REMINDERS_WHERE = Reminders.EVENT_ID + "=?";
static final String[] CALENDARS_PROJECTION = new String[] {
Calendars._ID, // 0
Calendars.CALENDAR_DISPLAY_NAME, // 1
Calendars.OWNER_ACCOUNT, // 2
Calendars.CAN_ORGANIZER_RESPOND, // 3
Calendars.ACCOUNT_NAME, // 4
Calendars.ACCOUNT_TYPE // 5
};
static final int CALENDARS_INDEX_DISPLAY_NAME = 1;
static final int CALENDARS_INDEX_OWNER_ACCOUNT = 2;
static final int CALENDARS_INDEX_OWNER_CAN_RESPOND = 3;
static final int CALENDARS_INDEX_ACCOUNT_NAME = 4;
static final int CALENDARS_INDEX_ACCOUNT_TYPE = 5;
static final String CALENDARS_WHERE = Calendars._ID + "=?";
static final String CALENDARS_DUPLICATE_NAME_WHERE = Calendars.CALENDAR_DISPLAY_NAME + "=?";
static final String CALENDARS_VISIBLE_WHERE = Calendars.VISIBLE + "=?";
static final String[] COLORS_PROJECTION = new String[] {
Colors._ID, // 0
Colors.COLOR, // 1
Colors.COLOR_KEY // 2
};
static final String COLORS_WHERE = Colors.ACCOUNT_NAME + "=? AND " + Colors.ACCOUNT_TYPE +
"=? AND " + Colors.COLOR_TYPE + "=" + Colors.TYPE_EVENT;
public static final int COLORS_INDEX_COLOR = 1;
public static final int COLORS_INDEX_COLOR_KEY = 2;
private final ArrayList<LinearLayout> mReminderViews = new ArrayList<LinearLayout>(0);
public ArrayList<ReminderEntry> mReminders;
public ArrayList<ReminderEntry> mOriginalReminders = new ArrayList<ReminderEntry>();
public ArrayList<ReminderEntry> mUnsupportedReminders = new ArrayList<ReminderEntry>();
ArrayList<Attendee> mAcceptedAttendees = new ArrayList<Attendee>();
ArrayList<Attendee> mDeclinedAttendees = new ArrayList<Attendee>();
ArrayList<Attendee> mTentativeAttendees = new ArrayList<Attendee>();
ArrayList<Attendee> mNoResponseAttendees = new ArrayList<Attendee>();
ArrayList<String> mToEmails = new ArrayList<String>();
ArrayList<String> mCcEmails = new ArrayList<String>();
private int mWindowStyle = DIALOG_WINDOW_STYLE;
private int mCurrentQuery = 0;
private View mView;
private Uri mUri;
private long mEventId;
private Cursor mEventCursor;
private Cursor mAttendeesCursor;
private Cursor mCalendarsCursor;
private Cursor mRemindersCursor;
private static float mScale = 0; // Used for supporting different screen densities
private static int mCustomAppIconSize = 32;
private long mStartMillis;
private long mEndMillis;
private boolean mAllDay;
private boolean mHasAttendeeData;
private String mEventOrganizerEmail;
private String mEventOrganizerDisplayName = "";
@ -335,7 +324,6 @@ public class EventInfoFragment extends DialogFragment implements OnCheckedChange
private EditResponseHelper mEditResponseHelper;
private boolean mDeleteDialogVisible = false;
private DeleteEventHelper mDeleteHelper;
private int mOriginalAttendeeResponse;
private int mAttendeeResponseFromIntent = Attendees.ATTENDEE_STATUS_NONE;
private int mUserSetResponse = Attendees.ATTENDEE_STATUS_NONE;
@ -349,7 +337,6 @@ public class EventInfoFragment extends DialogFragment implements OnCheckedChange
private String mCalendarAllowedReminders;
// Used to prevent saving changes in event if it is being deleted.
private boolean mEventDeletionStarted = false;
private TextView mTitle;
private TextView mWhenDateTime;
private TextView mWhere;
@ -362,63 +349,6 @@ public class EventInfoFragment extends DialogFragment implements OnCheckedChange
private View mLoadingMsgView;
private ObjectAnimator mAnimateAlpha;
private long mLoadingMsgStartTime;
private EventColorPickerDialog mColorPickerDialog;
private SparseIntArray mDisplayColorKeyMap = new SparseIntArray();
private int[] mColors;
private int mOriginalColor = -1;
private boolean mOriginalColorInitialized = false;
private int mCalendarColor = -1;
private boolean mCalendarColorInitialized = false;
private int mCurrentColor = -1;
private boolean mCurrentColorInitialized = false;
private int mCurrentColorKey = -1;
private static final int FADE_IN_TIME = 300; // in milliseconds
private static final int LOADING_MSG_DELAY = 600; // in milliseconds
private static final int LOADING_MSG_MIN_DISPLAY_TIME = 600;
private boolean mNoCrossFade = false; // Used to prevent repeated cross-fade
private RadioGroup mResponseRadioGroup;
ArrayList<Attendee> mAcceptedAttendees = new ArrayList<Attendee>();
ArrayList<Attendee> mDeclinedAttendees = new ArrayList<Attendee>();
ArrayList<Attendee> mTentativeAttendees = new ArrayList<Attendee>();
ArrayList<Attendee> mNoResponseAttendees = new ArrayList<Attendee>();
ArrayList<String> mToEmails = new ArrayList<String>();
ArrayList<String> mCcEmails = new ArrayList<String>();
private int mDefaultReminderMinutes;
private final ArrayList<LinearLayout> mReminderViews = new ArrayList<LinearLayout>(0);
public ArrayList<ReminderEntry> mReminders;
public ArrayList<ReminderEntry> mOriginalReminders = new ArrayList<ReminderEntry>();
public ArrayList<ReminderEntry> mUnsupportedReminders = new ArrayList<ReminderEntry>();
private boolean mUserModifiedReminders = false;
/**
* Contents of the "minutes" spinner. This has default values from the XML file, augmented
* with any additional values that were already associated with the event.
*/
private ArrayList<Integer> mReminderMinuteValues;
private ArrayList<String> mReminderMinuteLabels;
/**
* Contents of the "methods" spinner. The "values" list specifies the method constant
* (e.g. {@link Reminders#METHOD_ALERT}) associated with the labels. Any methods that
* aren't allowed by the Calendar will be removed.
*/
private ArrayList<Integer> mReminderMethodValues;
private ArrayList<String> mReminderMethodLabels;
private QueryHandler mHandler;
private final Runnable mTZUpdater = new Runnable() {
@Override
public void run() {
updateEvent(mView);
}
};
private final Runnable mLoadingMsgAlphaUpdater = new Runnable() {
@Override
public void run() {
@ -430,218 +360,63 @@ public class EventInfoFragment extends DialogFragment implements OnCheckedChange
}
}
};
private EventColorPickerDialog mColorPickerDialog;
private SparseIntArray mDisplayColorKeyMap = new SparseIntArray();
private int[] mColors;
private int mOriginalColor = -1;
private boolean mOriginalColorInitialized = false;
private int mCalendarColor = -1;
private boolean mCalendarColorInitialized = false;
private int mCurrentColor = -1;
private boolean mCurrentColorInitialized = false;
private int mCurrentColorKey = -1;
private boolean mNoCrossFade = false; // Used to prevent repeated cross-fade
private RadioGroup mResponseRadioGroup;
private int mDefaultReminderMinutes;
private boolean mUserModifiedReminders = false;
/**
* Contents of the "minutes" spinner. This has default values from the XML file, augmented
* with any additional values that were already associated with the event.
*/
private ArrayList<Integer> mReminderMinuteValues;
private ArrayList<String> mReminderMinuteLabels;
/**
* Contents of the "methods" spinner. The "values" list specifies the method constant
* (e.g. {@link Reminders#METHOD_ALERT}) associated with the labels. Any methods that
* aren't allowed by the Calendar will be removed.
*/
private ArrayList<Integer> mReminderMethodValues;
private ArrayList<String> mReminderMethodLabels;
private QueryHandler mHandler;
private OnItemSelectedListener mReminderChangeListener;
private static int mDialogWidth = 500;
private static int mDialogHeight = 600;
private static int DIALOG_TOP_MARGIN = 8;
private boolean mIsDialog = false;
private boolean mIsPaused = true;
private boolean mDismissOnResume = false;
private final Runnable onDeleteRunnable = new Runnable() {
@Override
public void run() {
if (EventInfoFragment.this.mIsPaused) {
mDismissOnResume = true;
return;
}
if (EventInfoFragment.this.isVisible()) {
EventInfoFragment.this.dismiss();
}
}
};
private int mX = -1;
private int mY = -1;
private int mMinTop; // Dialog cannot be above this location
private boolean mIsTabletConfig;
private Activity mActivity;
private Context mContext;
private CalendarController mController;
private class QueryHandler extends AsyncQueryService {
public QueryHandler(Context context) {
super(context);
}
private final Runnable mTZUpdater = new Runnable() {
@Override
protected void onQueryComplete(int token, Object cookie, Cursor cursor) {
// if the activity is finishing, then close the cursor and return
final Activity activity = getActivity();
if (activity == null || activity.isFinishing()) {
if (cursor != null) {
cursor.close();
}
return;
}
switch (token) {
case TOKEN_QUERY_EVENT:
mEventCursor = Utils.matrixCursorFromCursor(cursor);
if (initEventCursor()) {
// The cursor is empty. This can happen if the event was
// deleted.
// FRAG_TODO we should no longer rely on Activity.finish()
activity.finish();
return;
}
if (!mCalendarColorInitialized) {
mCalendarColor = Utils.getDisplayColorFromColor(
mEventCursor.getInt(EVENT_INDEX_CALENDAR_COLOR));
mCalendarColorInitialized = true;
}
if (!mOriginalColorInitialized) {
mOriginalColor = mEventCursor.isNull(EVENT_INDEX_EVENT_COLOR)
? mCalendarColor : Utils.getDisplayColorFromColor(
mEventCursor.getInt(EVENT_INDEX_EVENT_COLOR));
mOriginalColorInitialized = true;
}
if (!mCurrentColorInitialized) {
mCurrentColor = mOriginalColor;
mCurrentColorInitialized = true;
}
updateEvent(mView);
prepareReminders();
// start calendar query
Uri uri = Calendars.CONTENT_URI;
String[] args = new String[] {
Long.toString(mEventCursor.getLong(EVENT_INDEX_CALENDAR_ID))};
startQuery(TOKEN_QUERY_CALENDARS, null, uri, CALENDARS_PROJECTION,
CALENDARS_WHERE, args, null);
break;
case TOKEN_QUERY_CALENDARS:
mCalendarsCursor = Utils.matrixCursorFromCursor(cursor);
updateCalendar(mView);
// FRAG_TODO fragments shouldn't set the title anymore
updateTitle();
args = new String[] {
mCalendarsCursor.getString(CALENDARS_INDEX_ACCOUNT_NAME),
mCalendarsCursor.getString(CALENDARS_INDEX_ACCOUNT_TYPE) };
uri = Colors.CONTENT_URI;
startQuery(TOKEN_QUERY_COLORS, null, uri, COLORS_PROJECTION, COLORS_WHERE, args,
null);
if (!mIsBusyFreeCalendar) {
args = new String[] { Long.toString(mEventId) };
// start attendees query
uri = Attendees.CONTENT_URI;
startQuery(TOKEN_QUERY_ATTENDEES, null, uri, ATTENDEES_PROJECTION,
ATTENDEES_WHERE, args, ATTENDEES_SORT_ORDER);
} else {
sendAccessibilityEventIfQueryDone(TOKEN_QUERY_ATTENDEES);
}
if (mHasAlarm) {
// start reminders query
args = new String[] { Long.toString(mEventId) };
uri = Reminders.CONTENT_URI;
startQuery(TOKEN_QUERY_REMINDERS, null, uri,
REMINDERS_PROJECTION, REMINDERS_WHERE, args, null);
} else {
sendAccessibilityEventIfQueryDone(TOKEN_QUERY_REMINDERS);
}
break;
case TOKEN_QUERY_COLORS:
ArrayList<Integer> colors = new ArrayList<Integer>();
if (cursor.moveToFirst()) {
do
{
int colorKey = cursor.getInt(COLORS_INDEX_COLOR_KEY);
int rawColor = cursor.getInt(COLORS_INDEX_COLOR);
int displayColor = Utils.getDisplayColorFromColor(rawColor);
mDisplayColorKeyMap.put(displayColor, colorKey);
colors.add(displayColor);
} while (cursor.moveToNext());
}
cursor.close();
Integer[] sortedColors = new Integer[colors.size()];
Arrays.sort(colors.toArray(sortedColors), new HsvColorComparator());
mColors = new int[sortedColors.length];
for (int i = 0; i < sortedColors.length; i++) {
mColors[i] = sortedColors[i].intValue();
float[] hsv = new float[3];
Color.colorToHSV(mColors[i], hsv);
if (DEBUG) {
Log.d("Color", "H:" + hsv[0] + ",S:" + hsv[1] + ",V:" + hsv[2]);
}
}
if (mCanModifyCalendar) {
View button = mView.findViewById(R.id.change_color);
if (button != null && mColors.length > 0) {
button.setEnabled(true);
button.setVisibility(View.VISIBLE);
}
}
updateMenu();
break;
case TOKEN_QUERY_ATTENDEES:
mAttendeesCursor = Utils.matrixCursorFromCursor(cursor);
initAttendeesCursor(mView);
updateResponse(mView);
break;
case TOKEN_QUERY_REMINDERS:
mRemindersCursor = Utils.matrixCursorFromCursor(cursor);
initReminders(mView, mRemindersCursor);
break;
case TOKEN_QUERY_VISIBLE_CALENDARS:
if (cursor.getCount() > 1) {
// Start duplicate calendars query to detect whether to add the calendar
// email to the calendar owner display.
String displayName = mCalendarsCursor.getString(CALENDARS_INDEX_DISPLAY_NAME);
mHandler.startQuery(TOKEN_QUERY_DUPLICATE_CALENDARS, null,
Calendars.CONTENT_URI, CALENDARS_PROJECTION,
CALENDARS_DUPLICATE_NAME_WHERE, new String[] {displayName}, null);
} else {
// Don't need to display the calendar owner when there is only a single
// calendar. Skip the duplicate calendars query.
setVisibilityCommon(mView, R.id.calendar_container, View.GONE);
mCurrentQuery |= TOKEN_QUERY_DUPLICATE_CALENDARS;
}
break;
case TOKEN_QUERY_DUPLICATE_CALENDARS:
SpannableStringBuilder sb = new SpannableStringBuilder();
// Calendar display name
String calendarName = mCalendarsCursor.getString(CALENDARS_INDEX_DISPLAY_NAME);
sb.append(calendarName);
// Show email account if display name is not unique and
// display name != email
String email = mCalendarsCursor.getString(CALENDARS_INDEX_OWNER_ACCOUNT);
if (cursor.getCount() > 1 && !calendarName.equalsIgnoreCase(email) &&
Utils.isValidEmail(email)) {
sb.append(" (").append(email).append(")");
}
setVisibilityCommon(mView, R.id.calendar_container, View.VISIBLE);
setTextCommon(mView, R.id.calendar_name, sb);
break;
}
cursor.close();
sendAccessibilityEventIfQueryDone(token);
// All queries are done, show the view.
if (mCurrentQuery == TOKEN_QUERY_ALL) {
if (mLoadingMsgView.getAlpha() == 1) {
// Loading message is showing, let it stay a bit more (to prevent
// flashing) by adding a start delay to the event animation
long timeDiff = LOADING_MSG_MIN_DISPLAY_TIME - (System.currentTimeMillis() -
mLoadingMsgStartTime);
if (timeDiff > 0) {
mAnimateAlpha.setStartDelay(timeDiff);
}
}
if (!mAnimateAlpha.isRunning() &&!mAnimateAlpha.isStarted() && !mNoCrossFade) {
mAnimateAlpha.start();
} else {
mScrollView.setAlpha(1);
mLoadingMsgView.setVisibility(View.GONE);
}
}
public void run() {
updateEvent(mView);
}
}
private void sendAccessibilityEventIfQueryDone(int token) {
mCurrentQuery |= token;
if (mCurrentQuery == TOKEN_QUERY_ALL) {
sendAccessibilityEvent();
}
}
};
private CalendarController mController;
public EventInfoFragment(Context context, Uri uri, long startMillis, long endMillis,
int attendeeResponse, boolean isDialog, int windowStyle,
@ -687,6 +462,69 @@ public class EventInfoFragment extends DialogFragment implements OnCheckedChange
mEventId = eventId;
}
public static int getResponseFromButtonId(int buttonId) {
int response;
if (buttonId == R.id.response_yes) {
response = Attendees.ATTENDEE_STATUS_ACCEPTED;
} else if (buttonId == R.id.response_maybe) {
response = Attendees.ATTENDEE_STATUS_TENTATIVE;
} else if (buttonId == R.id.response_no) {
response = Attendees.ATTENDEE_STATUS_DECLINED;
} else {
response = Attendees.ATTENDEE_STATUS_NONE;
}
return response;
}
public static int findButtonIdForResponse(int response) {
int buttonId;
switch (response) {
case Attendees.ATTENDEE_STATUS_ACCEPTED:
buttonId = R.id.response_yes;
break;
case Attendees.ATTENDEE_STATUS_TENTATIVE:
buttonId = R.id.response_maybe;
break;
case Attendees.ATTENDEE_STATUS_DECLINED:
buttonId = R.id.response_no;
break;
default:
buttonId = -1;
}
return buttonId;
}
/**
* Loads an integer array asset into a list.
*/
private static ArrayList<Integer> loadIntegerArray(Resources r, int resNum) {
int[] vals = r.getIntArray(resNum);
int size = vals.length;
ArrayList<Integer> list = new ArrayList<Integer>(size);
for (int i = 0; i < size; i++) {
list.add(vals[i]);
}
return list;
}
/**
* Loads a String array asset into a list.
*/
private static ArrayList<String> loadStringArray(Resources r, int resNum) {
String[] labels = r.getStringArray(resNum);
ArrayList<String> list = new ArrayList<String>(Arrays.asList(labels));
return list;
}
private void sendAccessibilityEventIfQueryDone(int token) {
mCurrentQuery |= token;
if (mCurrentQuery == TOKEN_QUERY_ALL) {
sendAccessibilityEvent();
}
}
@Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
@ -1029,19 +867,6 @@ public class EventInfoFragment extends DialogFragment implements OnCheckedChange
return mView;
}
private final Runnable onDeleteRunnable = new Runnable() {
@Override
public void run() {
if (EventInfoFragment.this.mIsPaused) {
mDismissOnResume = true;
return;
}
if (EventInfoFragment.this.isVisible()) {
EventInfoFragment.this.dismiss();
}
}
};
private void updateTitle() {
Resources res = getActivity().getResources();
if (mCanModifyCalendar && !mIsOrganizer) {
@ -1408,38 +1233,6 @@ public class EventInfoFragment extends DialogFragment implements OnCheckedChange
Utils.UNDO_DELAY);
}
public static int getResponseFromButtonId(int buttonId) {
int response;
if (buttonId == R.id.response_yes) {
response = Attendees.ATTENDEE_STATUS_ACCEPTED;
} else if (buttonId == R.id.response_maybe) {
response = Attendees.ATTENDEE_STATUS_TENTATIVE;
} else if (buttonId == R.id.response_no) {
response = Attendees.ATTENDEE_STATUS_DECLINED;
} else {
response = Attendees.ATTENDEE_STATUS_NONE;
}
return response;
}
public static int findButtonIdForResponse(int response) {
int buttonId;
switch (response) {
case Attendees.ATTENDEE_STATUS_ACCEPTED:
buttonId = R.id.response_yes;
break;
case Attendees.ATTENDEE_STATUS_TENTATIVE:
buttonId = R.id.response_maybe;
break;
case Attendees.ATTENDEE_STATUS_DECLINED:
buttonId = R.id.response_no;
break;
default:
buttonId = -1;
}
return buttonId;
}
private void doEdit() {
Context c = getActivity();
// This ensures that we aren't in the process of closing and have been
@ -2128,7 +1921,6 @@ public class EventInfoFragment extends DialogFragment implements OnCheckedChange
EventViewUtils.updateAddReminderButton(mView, mReminderViews, mMaxReminders);
}
/**
* Add a new reminder when the user hits the "add reminder" button. We use the default
* reminder time and method.
@ -2182,7 +1974,6 @@ public class EventInfoFragment extends DialogFragment implements OnCheckedChange
}
}
private boolean saveReminders() {
ArrayList<ContentProviderOperation> ops = new ArrayList<ContentProviderOperation>(3);
@ -2229,29 +2020,6 @@ public class EventInfoFragment extends DialogFragment implements OnCheckedChange
startActivity(i);
}
/**
* Loads an integer array asset into a list.
*/
private static ArrayList<Integer> loadIntegerArray(Resources r, int resNum) {
int[] vals = r.getIntArray(resNum);
int size = vals.length;
ArrayList<Integer> list = new ArrayList<Integer>(size);
for (int i = 0; i < size; i++) {
list.add(vals[i]);
}
return list;
}
/**
* Loads a String array asset into a list.
*/
private static ArrayList<String> loadStringArray(Resources r, int resNum) {
String[] labels = r.getStringArray(resNum);
ArrayList<String> list = new ArrayList<String>(Arrays.asList(labels));
return list;
}
@Override
public void onDeleteStarted() {
mEventDeletionStarted = true;
@ -2277,9 +2045,11 @@ public class EventInfoFragment extends DialogFragment implements OnCheckedChange
public long getStartMillis() {
return mStartMillis;
}
public long getEndMillis() {
return mEndMillis;
}
private void setDialogSize(Resources r) {
mDialogWidth = (int)r.getDimension(R.dimen.event_info_dialog_width);
mDialogHeight = (int)r.getDimension(R.dimen.event_info_dialog_height);
@ -2291,4 +2061,191 @@ public class EventInfoFragment extends DialogFragment implements OnCheckedChange
mCurrentColorKey = mDisplayColorKeyMap.get(color);
mHeadlines.setBackgroundColor(color);
}
private class QueryHandler extends AsyncQueryService {
public QueryHandler(Context context) {
super(context);
}
@Override
protected void onQueryComplete(int token, Object cookie, Cursor cursor) {
// if the activity is finishing, then close the cursor and return
final Activity activity = getActivity();
if (activity == null || activity.isFinishing()) {
if (cursor != null) {
cursor.close();
}
return;
}
switch (token) {
case TOKEN_QUERY_EVENT:
mEventCursor = Utils.matrixCursorFromCursor(cursor);
if (initEventCursor()) {
// The cursor is empty. This can happen if the event was
// deleted.
// FRAG_TODO we should no longer rely on Activity.finish()
activity.finish();
return;
}
if (!mCalendarColorInitialized) {
mCalendarColor = Utils.getDisplayColorFromColor(
mEventCursor.getInt(EVENT_INDEX_CALENDAR_COLOR));
mCalendarColorInitialized = true;
}
if (!mOriginalColorInitialized) {
mOriginalColor = mEventCursor.isNull(EVENT_INDEX_EVENT_COLOR)
? mCalendarColor : Utils.getDisplayColorFromColor(
mEventCursor.getInt(EVENT_INDEX_EVENT_COLOR));
mOriginalColorInitialized = true;
}
if (!mCurrentColorInitialized) {
mCurrentColor = mOriginalColor;
mCurrentColorInitialized = true;
}
updateEvent(mView);
prepareReminders();
// start calendar query
Uri uri = Calendars.CONTENT_URI;
String[] args = new String[]{
Long.toString(mEventCursor.getLong(EVENT_INDEX_CALENDAR_ID))};
startQuery(TOKEN_QUERY_CALENDARS, null, uri, CALENDARS_PROJECTION,
CALENDARS_WHERE, args, null);
break;
case TOKEN_QUERY_CALENDARS:
mCalendarsCursor = Utils.matrixCursorFromCursor(cursor);
updateCalendar(mView);
// FRAG_TODO fragments shouldn't set the title anymore
updateTitle();
args = new String[]{
mCalendarsCursor.getString(CALENDARS_INDEX_ACCOUNT_NAME),
mCalendarsCursor.getString(CALENDARS_INDEX_ACCOUNT_TYPE)};
uri = Colors.CONTENT_URI;
startQuery(TOKEN_QUERY_COLORS, null, uri, COLORS_PROJECTION, COLORS_WHERE, args,
null);
if (!mIsBusyFreeCalendar) {
args = new String[]{Long.toString(mEventId)};
// start attendees query
uri = Attendees.CONTENT_URI;
startQuery(TOKEN_QUERY_ATTENDEES, null, uri, ATTENDEES_PROJECTION,
ATTENDEES_WHERE, args, ATTENDEES_SORT_ORDER);
} else {
sendAccessibilityEventIfQueryDone(TOKEN_QUERY_ATTENDEES);
}
if (mHasAlarm) {
// start reminders query
args = new String[]{Long.toString(mEventId)};
uri = Reminders.CONTENT_URI;
startQuery(TOKEN_QUERY_REMINDERS, null, uri,
REMINDERS_PROJECTION, REMINDERS_WHERE, args, null);
} else {
sendAccessibilityEventIfQueryDone(TOKEN_QUERY_REMINDERS);
}
break;
case TOKEN_QUERY_COLORS:
ArrayList<Integer> colors = new ArrayList<Integer>();
if (cursor.moveToFirst()) {
do {
int colorKey = cursor.getInt(COLORS_INDEX_COLOR_KEY);
int rawColor = cursor.getInt(COLORS_INDEX_COLOR);
int displayColor = Utils.getDisplayColorFromColor(rawColor);
mDisplayColorKeyMap.put(displayColor, colorKey);
colors.add(displayColor);
} while (cursor.moveToNext());
}
cursor.close();
Integer[] sortedColors = new Integer[colors.size()];
Arrays.sort(colors.toArray(sortedColors), new HsvColorComparator());
mColors = new int[sortedColors.length];
for (int i = 0; i < sortedColors.length; i++) {
mColors[i] = sortedColors[i].intValue();
float[] hsv = new float[3];
Color.colorToHSV(mColors[i], hsv);
if (DEBUG) {
Log.d("Color", "H:" + hsv[0] + ",S:" + hsv[1] + ",V:" + hsv[2]);
}
}
if (mCanModifyCalendar) {
View button = mView.findViewById(R.id.change_color);
if (button != null && mColors.length > 0) {
button.setEnabled(true);
button.setVisibility(View.VISIBLE);
}
}
updateMenu();
break;
case TOKEN_QUERY_ATTENDEES:
mAttendeesCursor = Utils.matrixCursorFromCursor(cursor);
initAttendeesCursor(mView);
updateResponse(mView);
break;
case TOKEN_QUERY_REMINDERS:
mRemindersCursor = Utils.matrixCursorFromCursor(cursor);
initReminders(mView, mRemindersCursor);
break;
case TOKEN_QUERY_VISIBLE_CALENDARS:
if (cursor.getCount() > 1) {
// Start duplicate calendars query to detect whether to add the calendar
// email to the calendar owner display.
String displayName = mCalendarsCursor.getString(CALENDARS_INDEX_DISPLAY_NAME);
mHandler.startQuery(TOKEN_QUERY_DUPLICATE_CALENDARS, null,
Calendars.CONTENT_URI, CALENDARS_PROJECTION,
CALENDARS_DUPLICATE_NAME_WHERE, new String[]{displayName}, null);
} else {
// Don't need to display the calendar owner when there is only a single
// calendar. Skip the duplicate calendars query.
setVisibilityCommon(mView, R.id.calendar_container, View.GONE);
mCurrentQuery |= TOKEN_QUERY_DUPLICATE_CALENDARS;
}
break;
case TOKEN_QUERY_DUPLICATE_CALENDARS:
SpannableStringBuilder sb = new SpannableStringBuilder();
// Calendar display name
String calendarName = mCalendarsCursor.getString(CALENDARS_INDEX_DISPLAY_NAME);
sb.append(calendarName);
// Show email account if display name is not unique and
// display name != email
String email = mCalendarsCursor.getString(CALENDARS_INDEX_OWNER_ACCOUNT);
if (cursor.getCount() > 1 && !calendarName.equalsIgnoreCase(email) &&
Utils.isValidEmail(email)) {
sb.append(" (").append(email).append(")");
}
setVisibilityCommon(mView, R.id.calendar_container, View.VISIBLE);
setTextCommon(mView, R.id.calendar_name, sb);
break;
}
cursor.close();
sendAccessibilityEventIfQueryDone(token);
// All queries are done, show the view.
if (mCurrentQuery == TOKEN_QUERY_ALL) {
if (mLoadingMsgView.getAlpha() == 1) {
// Loading message is showing, let it stay a bit more (to prevent
// flashing) by adding a start delay to the event animation
long timeDiff = LOADING_MSG_MIN_DISPLAY_TIME - (System.currentTimeMillis() -
mLoadingMsgStartTime);
if (timeDiff > 0) {
mAnimateAlpha.setStartDelay(timeDiff);
}
}
if (!mAnimateAlpha.isRunning() && !mAnimateAlpha.isStarted() && !mNoCrossFade) {
mAnimateAlpha.start();
} else {
mScrollView.setAlpha(1);
mLoadingMsgView.setVisibility(View.GONE);
}
}
}
}
}

View File

@ -16,8 +16,6 @@
package com.android.calendar;
import org.sufficientlysecure.standalonecalendar.R;
import android.content.ContentResolver;
import android.content.Context;
import android.database.Cursor;
@ -42,6 +40,83 @@ public class EventLoader {
private LoaderThread mLoaderThread;
private ContentResolver mResolver;
public EventLoader(Context context) {
mContext = context;
mLoaderQueue = new LinkedBlockingQueue<LoadRequest>();
mResolver = context.getContentResolver();
}
/**
* Call this from the activity's onResume()
*/
public void startBackgroundThread() {
mLoaderThread = new LoaderThread(mLoaderQueue, this);
mLoaderThread.start();
}
/**
* Call this from the activity's onPause()
*/
public void stopBackgroundThread() {
mLoaderThread.shutdown();
}
/**
* Loads "numDays" days worth of events, starting at start, into events.
* Posts uiCallback to the {@link Handler} for this view, which will run in the UI thread.
* Reuses an existing background thread, if events were already being loaded in the background.
* NOTE: events and uiCallback are not used if an existing background thread gets reused --
* the ones that were passed in on the call that results in the background thread getting
* created are used, and the most recent call's worth of data is loaded into events and posted
* via the uiCallback.
*/
public void loadEventsInBackground(final int numDays, final ArrayList<Event> events,
int startDay, final Runnable successCallback, final Runnable cancelCallback) {
// Increment the sequence number for requests. We don't care if the
// sequence numbers wrap around because we test for equality with the
// latest one.
int id = mSequenceNumber.incrementAndGet();
// Send the load request to the background thread
LoadEventsRequest request = new LoadEventsRequest(id, startDay, numDays,
events, successCallback, cancelCallback);
try {
mLoaderQueue.put(request);
} catch (InterruptedException ex) {
// The put() method fails with InterruptedException if the
// queue is full. This should never happen because the queue
// has no limit.
Log.e("Cal", "loadEventsInBackground() interrupted!");
}
}
/**
* Sends a request for the days with events to be marked. Loads "numDays"
* worth of days, starting at start, and fills in eventDays to express which
* days have events.
*
* @param startDay First day to check for events
* @param numDays Days following the start day to check
* @param eventDay Whether or not an event exists on that day
* @param uiCallback What to do when done (log data, redraw screen)
*/
void loadEventDaysInBackground(int startDay, int numDays, boolean[] eventDays,
final Runnable uiCallback) {
// Send load request to the background thread
LoadEventDaysRequest request = new LoadEventDaysRequest(startDay, numDays,
eventDays, uiCallback);
try {
mLoaderQueue.put(request);
} catch (InterruptedException ex) {
// The put() method fails with InterruptedException if the
// queue is full. This should never happen because the queue
// has no limit.
Log.e("Cal", "loadEventDaysInBackground() interrupted!");
}
}
private static interface LoadRequest {
public void processRequest(EventLoader eventLoader);
public void skipRequest(EventLoader eventLoader);
@ -62,17 +137,16 @@ public class EventLoader {
*
*/
private static class LoadEventDaysRequest implements LoadRequest {
public int startDay;
public int numDays;
public boolean[] eventDays;
public Runnable uiCallback;
/**
* The projection used by the EventDays query.
*/
private static final String[] PROJECTION = {
CalendarContract.EventDays.STARTDAY, CalendarContract.EventDays.ENDDAY
};
public int startDay;
public int numDays;
public boolean[] eventDays;
public Runnable uiCallback;
public LoadEventDaysRequest(int startDay, int numDays, boolean[] eventDays,
final Runnable uiCallback)
@ -207,82 +281,4 @@ public class EventLoader {
}
}
}
public EventLoader(Context context) {
mContext = context;
mLoaderQueue = new LinkedBlockingQueue<LoadRequest>();
mResolver = context.getContentResolver();
}
/**
* Call this from the activity's onResume()
*/
public void startBackgroundThread() {
mLoaderThread = new LoaderThread(mLoaderQueue, this);
mLoaderThread.start();
}
/**
* Call this from the activity's onPause()
*/
public void stopBackgroundThread() {
mLoaderThread.shutdown();
}
/**
* Loads "numDays" days worth of events, starting at start, into events.
* Posts uiCallback to the {@link Handler} for this view, which will run in the UI thread.
* Reuses an existing background thread, if events were already being loaded in the background.
* NOTE: events and uiCallback are not used if an existing background thread gets reused --
* the ones that were passed in on the call that results in the background thread getting
* created are used, and the most recent call's worth of data is loaded into events and posted
* via the uiCallback.
*/
public void loadEventsInBackground(final int numDays, final ArrayList<Event> events,
int startDay, final Runnable successCallback, final Runnable cancelCallback) {
// Increment the sequence number for requests. We don't care if the
// sequence numbers wrap around because we test for equality with the
// latest one.
int id = mSequenceNumber.incrementAndGet();
// Send the load request to the background thread
LoadEventsRequest request = new LoadEventsRequest(id, startDay, numDays,
events, successCallback, cancelCallback);
try {
mLoaderQueue.put(request);
} catch (InterruptedException ex) {
// The put() method fails with InterruptedException if the
// queue is full. This should never happen because the queue
// has no limit.
Log.e("Cal", "loadEventsInBackground() interrupted!");
}
}
/**
* Sends a request for the days with events to be marked. Loads "numDays"
* worth of days, starting at start, and fills in eventDays to express which
* days have events.
*
* @param startDay First day to check for events
* @param numDays Days following the start day to check
* @param eventDay Whether or not an event exists on that day
* @param uiCallback What to do when done (log data, redraw screen)
*/
void loadEventDaysInBackground(int startDay, int numDays, boolean[] eventDays,
final Runnable uiCallback)
{
// Send load request to the background thread
LoadEventDaysRequest request = new LoadEventDaysRequest(startDay, numDays,
eventDays, uiCallback);
try {
mLoaderQueue.put(request);
} catch (InterruptedException ex) {
// The put() method fails with InterruptedException if the
// queue is full. This should never happen because the queue
// has no limit.
Log.e("Cal", "loadEventDaysInBackground() interrupted!");
}
}
}

View File

@ -16,8 +16,6 @@
package com.android.calendar;
import org.sufficientlysecure.standalonecalendar.R;
import android.content.Context;
import android.content.res.Resources;
import android.text.format.DateUtils;
@ -28,6 +26,8 @@ import com.android.calendarcommon2.EventRecurrence;
import java.util.Calendar;
import ws.xsoh.etar.R;
public class EventRecurrenceFormatter
{

View File

@ -16,8 +16,6 @@
package com.android.calendar;
import org.sufficientlysecure.standalonecalendar.R;
import android.content.Context;
import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
@ -27,6 +25,8 @@ import android.widget.ImageButton;
import android.widget.LinearLayout;
import android.widget.TextView;
import ws.xsoh.etar.R;
public class ExpandableTextView extends LinearLayout implements OnClickListener {
TextView mTv;
@ -110,6 +110,13 @@ public class ExpandableTextView extends LinearLayout implements OnClickListener
mButton.setOnClickListener(this);
}
public CharSequence getText() {
if (mTv == null) {
return "";
}
return mTv.getText();
}
public void setText(String text) {
mRelayout = true;
if (mTv == null) {
@ -119,11 +126,4 @@ public class ExpandableTextView extends LinearLayout implements OnClickListener
mTv.setText(trimmedText);
this.setVisibility(trimmedText.length() == 0 ? View.GONE : View.VISIBLE);
}
public CharSequence getText() {
if (mTv == null) {
return "";
}
return mTv.getText();
}
}

View File

@ -16,8 +16,6 @@
package com.android.calendar;
import org.sufficientlysecure.standalonecalendar.R;
import android.content.Context;
import android.content.res.AssetManager;
import android.os.Bundle;

View File

@ -16,8 +16,6 @@
package com.android.calendar;
import org.sufficientlysecure.standalonecalendar.R;
import android.app.Activity;
import android.app.FragmentManager;
import android.app.backup.BackupManager;
@ -50,50 +48,37 @@ import android.widget.Toast;
import com.android.calendar.alerts.AlertReceiver;
import com.android.calendar.event.EventViewUtils;
import com.android.timezonepicker.TimeZoneInfo;
import com.android.timezonepicker.TimeZonePickerDialog;
import com.android.timezonepicker.TimeZonePickerDialog.OnTimeZoneSetListener;
import com.android.timezonepicker.TimeZonePickerUtils;
import ws.xsoh.etar.R;
public class GeneralPreferences extends PreferenceFragment implements
OnSharedPreferenceChangeListener, OnPreferenceChangeListener, OnTimeZoneSetListener {
// The name of the shared preferences file. This name must be maintained for historical
// reasons, as it's what PreferenceManager assigned the first time the file was created.
static final String SHARED_PREFS_NAME = "com.android.calendar_preferences";
static final String SHARED_PREFS_NAME_NO_BACKUP = "com.android.calendar_preferences_no_backup";
private static final String FRAG_TAG_TIME_ZONE_PICKER = "TimeZonePicker";
// Preference keys
public static final String KEY_HIDE_DECLINED = "preferences_hide_declined";
public static final String KEY_WEEK_START_DAY = "preferences_week_start_day";
public static final String KEY_SHOW_WEEK_NUM = "preferences_show_week_num";
public static final String KEY_DAYS_PER_WEEK = "preferences_days_per_week";
public static final String KEY_SKIP_SETUP = "preferences_skip_setup";
public static final String KEY_CLEAR_SEARCH_HISTORY = "preferences_clear_search_history";
public static final String KEY_ALERTS_CATEGORY = "preferences_alerts_category";
public static final String KEY_ALERTS = "preferences_alerts";
public static final String KEY_ALERTS_VIBRATE = "preferences_alerts_vibrate";
public static final String KEY_ALERTS_RINGTONE = "preferences_alerts_ringtone";
public static final String KEY_ALERTS_POPUP = "preferences_alerts_popup";
public static final String KEY_SHOW_CONTROLS = "preferences_show_controls";
public static final String KEY_DEFAULT_REMINDER = "preferences_default_reminder";
public static final int NO_REMINDER = -1;
public static final String NO_REMINDER_STRING = "-1";
public static final int REMINDER_DEFAULT_TIME = 10; // in minutes
public static final String KEY_USE_CUSTOM_SNOOZE_DELAY = "preferences_custom_snooze_delay";
public static final String KEY_DEFAULT_SNOOZE_DELAY = "preferences_default_snooze_delay";
public static final int SNOOZE_DELAY_DEFAULT_TIME = 5; // in minutes
public static final String KEY_DEFAULT_CELL_HEIGHT = "preferences_default_cell_height";
public static final String KEY_VERSION = "preferences_version";
/** Key to SharePreference for default view (CalendarController.ViewType) */
public static final String KEY_START_VIEW = "preferred_startView";
/**
@ -102,28 +87,29 @@ public class GeneralPreferences extends PreferenceFragment implements
*/
public static final String KEY_DETAILED_VIEW = "preferred_detailedView";
public static final String KEY_DEFAULT_CALENDAR = "preference_defaultCalendar";
// These must be in sync with the array preferences_week_start_day_values
public static final String WEEK_START_DEFAULT = "-1";
public static final String WEEK_START_SATURDAY = "7";
public static final String WEEK_START_SUNDAY = "1";
public static final String WEEK_START_MONDAY = "2";
// These keys are kept to enable migrating users from previous versions
private static final String KEY_ALERTS_TYPE = "preferences_alerts_type";
private static final String ALERT_TYPE_ALERTS = "0";
private static final String ALERT_TYPE_STATUS_BAR = "1";
private static final String ALERT_TYPE_OFF = "2";
static final String KEY_HOME_TZ_ENABLED = "preferences_home_tz_enabled";
static final String KEY_HOME_TZ = "preferences_home_tz";
// Default preference values
public static final int DEFAULT_START_VIEW = CalendarController.ViewType.WEEK;
public static final int DEFAULT_DETAILED_VIEW = CalendarController.ViewType.DAY;
public static final boolean DEFAULT_SHOW_WEEK_NUM = false;
// This should match the XML file.
public static final String DEFAULT_RINGTONE = "content://settings/system/notification_sound";
// The name of the shared preferences file. This name must be maintained for historical
// reasons, as it's what PreferenceManager assigned the first time the file was created.
static final String SHARED_PREFS_NAME = "com.android.calendar_preferences";
static final String SHARED_PREFS_NAME_NO_BACKUP = "com.android.calendar_preferences_no_backup";
static final String KEY_HOME_TZ_ENABLED = "preferences_home_tz_enabled";
static final String KEY_HOME_TZ = "preferences_home_tz";
private static final String FRAG_TAG_TIME_ZONE_PICKER = "TimeZonePicker";
// These keys are kept to enable migrating users from previous versions
private static final String KEY_ALERTS_TYPE = "preferences_alerts_type";
private static final String ALERT_TYPE_ALERTS = "0";
private static final String ALERT_TYPE_STATUS_BAR = "1";
private static final String ALERT_TYPE_OFF = "2";
CheckBoxPreference mAlert;
CheckBoxPreference mVibrate;
RingtonePreference mRingtone;

View File

@ -17,16 +17,6 @@
package com.android.calendar;
import org.sufficientlysecure.standalonecalendar.R;
import static android.provider.CalendarContract.Attendees.ATTENDEE_STATUS;
import static android.provider.CalendarContract.Attendees.ATTENDEE_STATUS_ACCEPTED;
import static android.provider.CalendarContract.Attendees.ATTENDEE_STATUS_DECLINED;
import static android.provider.CalendarContract.Attendees.ATTENDEE_STATUS_NONE;
import static android.provider.CalendarContract.Attendees.ATTENDEE_STATUS_TENTATIVE;
import static android.provider.CalendarContract.EXTRA_EVENT_BEGIN_TIME;
import static android.provider.CalendarContract.EXTRA_EVENT_END_TIME;
import android.app.Activity;
import android.content.ActivityNotFoundException;
import android.content.ContentUris;
@ -43,10 +33,17 @@ import android.util.Log;
import com.android.calendarcommon2.DateException;
import com.android.calendarcommon2.Duration;
public class GoogleCalendarUriIntentFilter extends Activity {
private static final String TAG = "GoogleCalendarUriIntentFilter";
static final boolean debug = false;
import static android.provider.CalendarContract.Attendees.ATTENDEE_STATUS;
import static android.provider.CalendarContract.Attendees.ATTENDEE_STATUS_ACCEPTED;
import static android.provider.CalendarContract.Attendees.ATTENDEE_STATUS_DECLINED;
import static android.provider.CalendarContract.Attendees.ATTENDEE_STATUS_NONE;
import static android.provider.CalendarContract.Attendees.ATTENDEE_STATUS_TENTATIVE;
import static android.provider.CalendarContract.EXTRA_EVENT_BEGIN_TIME;
import static android.provider.CalendarContract.EXTRA_EVENT_END_TIME;
public class GoogleCalendarUriIntentFilter extends Activity {
static final boolean debug = false;
private static final String TAG = "GoogleCalendarUriIntentFilter";
private static final int EVENT_INDEX_ID = 0;
private static final int EVENT_INDEX_START = 1;
private static final int EVENT_INDEX_END = 2;

View File

@ -15,8 +15,6 @@
*/
package com.android.calendar;
import org.sufficientlysecure.standalonecalendar.R;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.drawable.Drawable;
@ -25,6 +23,8 @@ import android.util.Log;
import android.view.Gravity;
import android.widget.Button;
import ws.xsoh.etar.R;
/**
* <p>
* A button with more than two states. When the button is pressed

View File

@ -16,10 +16,7 @@
package com.android.calendar;
import org.sufficientlysecure.standalonecalendar.R;
import android.app.Activity;
import android.app.Dialog;
import android.app.TimePickerDialog;
import android.content.ComponentName;
import android.content.Intent;
@ -37,14 +34,9 @@ import android.text.format.Time;
import android.util.Log;
import android.widget.TimePicker;
public class OtherPreferences extends PreferenceFragment implements OnPreferenceChangeListener{
private static final String TAG = "CalendarOtherPreferences";
// The name of the shared preferences file. This name must be maintained for
// historical reasons, as it's what PreferenceManager assigned the first
// time the file was created.
static final String SHARED_PREFS_NAME = "com.android.calendar_preferences";
import ws.xsoh.etar.R;
public class OtherPreferences extends PreferenceFragment implements OnPreferenceChangeListener {
// Must be the same keys that are used in the other_preferences.xml file.
public static final String KEY_OTHER_COPY_DB = "preferences_copy_db";
public static final String KEY_OTHER_QUIET_HOURS = "preferences_reminders_quiet_hours";
@ -62,12 +54,15 @@ public class OtherPreferences extends PreferenceFragment implements OnPreferenc
public static final String KEY_OTHER_QUIET_HOURS_END_MINUTE =
"preferences_reminders_quiet_hours_end_minute";
public static final String KEY_OTHER_1 = "preferences_tardis_1";
public static final int QUIET_HOURS_DEFAULT_START_HOUR = 22;
public static final int QUIET_HOURS_DEFAULT_START_MINUTE = 0;
public static final int QUIET_HOURS_DEFAULT_END_HOUR = 8;
public static final int QUIET_HOURS_DEFAULT_END_MINUTE = 0;
// The name of the shared preferences file. This name must be maintained for
// historical reasons, as it's what PreferenceManager assigned the first
// time the file was created.
static final String SHARED_PREFS_NAME = "com.android.calendar_preferences";
private static final String TAG = "CalendarOtherPreferences";
private static final int START_LISTENER = 1;
private static final int END_LISTENER = 2;
private static final String format24Hour = "%H:%M";
@ -177,40 +172,6 @@ public class OtherPreferences extends PreferenceFragment implements OnPreferenc
return true;
}
private class TimeSetListener implements TimePickerDialog.OnTimeSetListener {
private int mListenerId;
public TimeSetListener(int listenerId) {
mListenerId = listenerId;
}
@Override
public void onTimeSet(TimePicker view, int hourOfDay, int minute) {
mTimePickerDialog = null;
SharedPreferences prefs = getPreferenceManager().getSharedPreferences();
SharedPreferences.Editor editor = prefs.edit();
String summary = formatTime(hourOfDay, minute);
switch (mListenerId) {
case (START_LISTENER):
mQuietHoursStart.setSummary(summary);
editor.putInt(KEY_OTHER_QUIET_HOURS_START_HOUR, hourOfDay);
editor.putInt(KEY_OTHER_QUIET_HOURS_START_MINUTE, minute);
break;
case (END_LISTENER):
mQuietHoursEnd.setSummary(summary);
editor.putInt(KEY_OTHER_QUIET_HOURS_END_HOUR, hourOfDay);
editor.putInt(KEY_OTHER_QUIET_HOURS_END_MINUTE, minute);
break;
default:
Log.d(TAG, "Set time for unknown listener: "+mListenerId);
}
editor.commit();
}
}
/**
* @param hourOfDay the hour of the day (0-24)
* @param minute
@ -250,4 +211,38 @@ public class OtherPreferences extends PreferenceFragment implements OnPreferenc
}
}
}
private class TimeSetListener implements TimePickerDialog.OnTimeSetListener {
private int mListenerId;
public TimeSetListener(int listenerId) {
mListenerId = listenerId;
}
@Override
public void onTimeSet(TimePicker view, int hourOfDay, int minute) {
mTimePickerDialog = null;
SharedPreferences prefs = getPreferenceManager().getSharedPreferences();
SharedPreferences.Editor editor = prefs.edit();
String summary = formatTime(hourOfDay, minute);
switch (mListenerId) {
case (START_LISTENER):
mQuietHoursStart.setSummary(summary);
editor.putInt(KEY_OTHER_QUIET_HOURS_START_HOUR, hourOfDay);
editor.putInt(KEY_OTHER_QUIET_HOURS_START_MINUTE, minute);
break;
case (END_LISTENER):
mQuietHoursEnd.setSummary(summary);
editor.putInt(KEY_OTHER_QUIET_HOURS_END_HOUR, hourOfDay);
editor.putInt(KEY_OTHER_QUIET_HOURS_END_MINUTE, minute);
break;
default:
Log.d(TAG, "Set time for unknown listener: " + mListenerId);
}
editor.commit();
}
}
}

View File

@ -16,8 +16,6 @@
package com.android.calendar;
import org.sufficientlysecure.standalonecalendar.R;
import android.app.Activity;
import android.os.Bundle;
import android.preference.EditTextPreference;
@ -29,6 +27,8 @@ import android.util.Log;
import java.util.Arrays;
import ws.xsoh.etar.R;
/**
* Fragment to facilitate editing of quick responses when emailing guests
*

View File

@ -16,13 +16,11 @@
package com.android.calendar;
import org.sufficientlysecure.standalonecalendar.R;
import com.android.ex.chips.BaseRecipientAdapter;
import android.accounts.Account;
import android.content.Context;
import com.android.ex.chips.BaseRecipientAdapter;
public class RecipientAdapter extends BaseRecipientAdapter {
public RecipientAdapter(Context context) {
super(context);

View File

@ -43,7 +43,7 @@ import com.android.calendar.CalendarController.EventType;
import com.android.calendar.CalendarController.ViewType;
import com.android.calendar.agenda.AgendaFragment;
import org.sufficientlysecure.standalonecalendar.R;
import ws.xsoh.etar.R;
import static android.provider.CalendarContract.EXTRA_EVENT_BEGIN_TIME;
import static android.provider.CalendarContract.EXTRA_EVENT_END_TIME;

View File

@ -16,8 +16,6 @@
package com.android.calendar;
import org.sufficientlysecure.standalonecalendar.R;
import android.content.Context;
import android.graphics.Color;
import android.util.AttributeSet;
@ -63,65 +61,30 @@ public class StickyHeaderListView extends FrameLayout implements OnScrollListene
protected View mDummyHeader = null; // A invisible header used when a section has no header
protected ListView mListView = null;
protected ListView.OnScrollListener mListener = null;
private int mSeparatorWidth;
private View mSeparatorView;
private int mLastStickyHeaderHeight = 0;
// This code is needed only if dataset changes do not force a call to OnScroll
// protected DataSetObserver mListDataObserver = null;
protected int mCurrentSectionPos = -1; // Position of section that has its header on the
// top of the view
protected int mNextSectionPosition = -1; // Position of next section's header
protected int mListViewHeadersCount = 0;
// This code is needed only if dataset changes do not force a call to OnScroll
// protected DataSetObserver mListDataObserver = null;
private int mSeparatorWidth;
private View mSeparatorView;
private int mLastStickyHeaderHeight = 0;
/**
* Interface that must be implemented by the ListView adapter to provide headers locations
* and number of items under each header.
* Constructor
*
* @param context - application context.
* @param attrs - layout attributes.
*/
public interface HeaderIndexer {
/**
* Calculates the position of the header of a specific item in the adapter's data set.
* For example: Assuming you have a list with albums and songs names:
* Album A, song 1, song 2, ...., song 10, Album B, song 1, ..., song 7. A call to
* this method with the position of song 5 in Album B, should return the position
* of Album B.
* @param position - Position of the item in the ListView dataset
* @return Position of header. -1 if the is no header
*/
int getHeaderPositionFromItemPosition(int position);
/**
* Calculates the number of items in the section defined by the header (not including
* the header).
* For example: A list with albums and songs, the method should return
* the number of songs names (without the album name).
*
* @param headerPosition - the value returned by 'getHeaderPositionFromItemPosition'
* @return Number of items. -1 on error.
*/
int getHeaderItemsNumber(int headerPosition);
public StickyHeaderListView(Context context, AttributeSet attrs) {
super(context, attrs);
mContext = context;
// This code is needed only if dataset changes do not force a call to OnScroll
// createDataListener();
}
/***
*
* Interface that is used to update the sticky header's height
*
*/
public interface HeaderHeightListener {
/***
* Updated a change in the sticky header's size
*
* @param height - new height of sticky header
*/
void OnHeaderHeightChanged(int height);
}
/**
* Sets the adapter to be used by the class to get views of headers
*
@ -181,29 +144,6 @@ public class StickyHeaderListView extends FrameLayout implements OnScrollListene
mHeaderHeightListener = listener;
}
// This code is needed only if dataset changes do not force a call to OnScroll
// protected void createDataListener() {
// mListDataObserver = new DataSetObserver() {
// @Override
// public void onChanged() {
// onDataChanged();
// }
// };
// }
/**
* Constructor
*
* @param context - application context.
* @param attrs - layout attributes.
*/
public StickyHeaderListView(Context context, AttributeSet attrs) {
super(context, attrs);
mContext = context;
// This code is needed only if dataset changes do not force a call to OnScroll
// createDataListener();
}
/**
* Scroll status changes listener
*
@ -217,6 +157,16 @@ public class StickyHeaderListView extends FrameLayout implements OnScrollListene
}
}
// This code is needed only if dataset changes do not force a call to OnScroll
// protected void createDataListener() {
// mListDataObserver = new DataSetObserver() {
// @Override
// public void onChanged() {
// onDataChanged();
// }
// };
// }
/**
* Scroll events listener
*
@ -361,13 +311,6 @@ public class StickyHeaderListView extends FrameLayout implements OnScrollListene
mDoHeaderReset = true;
}
// Resets the sticky header when the adapter data set was changed
// This code is needed only if dataset changes do not force a call to OnScroll
// protected void onDataChanged() {
// Should do a call to updateStickyHeader if needed
// }
private void setChildViews() {
// Find a child ListView (if any)
@ -394,4 +337,54 @@ public class StickyHeaderListView extends FrameLayout implements OnScrollListene
mChildViewsCreated = true;
}
/**
* Interface that must be implemented by the ListView adapter to provide headers locations
* and number of items under each header.
*/
public interface HeaderIndexer {
/**
* Calculates the position of the header of a specific item in the adapter's data set.
* For example: Assuming you have a list with albums and songs names:
* Album A, song 1, song 2, ...., song 10, Album B, song 1, ..., song 7. A call to
* this method with the position of song 5 in Album B, should return the position
* of Album B.
*
* @param position - Position of the item in the ListView dataset
* @return Position of header. -1 if the is no header
*/
int getHeaderPositionFromItemPosition(int position);
/**
* Calculates the number of items in the section defined by the header (not including
* the header).
* For example: A list with albums and songs, the method should return
* the number of songs names (without the album name).
*
* @param headerPosition - the value returned by 'getHeaderPositionFromItemPosition'
* @return Number of items. -1 on error.
*/
int getHeaderItemsNumber(int headerPosition);
}
// Resets the sticky header when the adapter data set was changed
// This code is needed only if dataset changes do not force a call to OnScroll
// protected void onDataChanged() {
// Should do a call to updateStickyHeader if needed
// }
/***
* Interface that is used to update the sticky header's height
*/
public interface HeaderHeightListener {
/***
* Updated a change in the sticky header's size
*
* @param height - new height of sticky header
*/
void OnHeaderHeightChanged(int height);
}
}

View File

@ -53,8 +53,6 @@ import com.android.calendar.CalendarController.ViewType;
import com.android.calendar.CalendarEventModel.ReminderEntry;
import com.android.calendar.CalendarUtils.TimeZoneUtils;
import org.sufficientlysecure.standalonecalendar.R;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Calendar;
@ -71,6 +69,8 @@ import java.util.TimeZone;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import ws.xsoh.etar.R;
import static android.provider.CalendarContract.EXTRA_EVENT_BEGIN_TIME;
public class Utils {

View File

@ -31,13 +31,14 @@ import android.widget.ResourceCursorAdapter;
import android.widget.TextView;
import com.android.calendar.ColorChipView;
import org.sufficientlysecure.standalonecalendar.R;
import com.android.calendar.Utils;
import java.util.Formatter;
import java.util.Locale;
import java.util.TimeZone;
import ws.xsoh.etar.R;
public class AgendaAdapter extends ResourceCursorAdapter {
private final String mNoTitleLabel;
private final Resources mResources;
@ -48,37 +49,15 @@ public class AgendaAdapter extends ResourceCursorAdapter {
// Note: Formatter is not thread safe. Fine for now as it is only used by the main thread.
private final Formatter mFormatter;
private final StringBuilder mStringBuilder;
private float mScale;
private int COLOR_CHIP_ALL_DAY_HEIGHT;
private int COLOR_CHIP_HEIGHT;
private final Runnable mTZUpdater = new Runnable() {
@Override
public void run() {
notifyDataSetChanged();
}
};
static class ViewHolder {
public static final int DECLINED_RESPONSE = 0;
public static final int TENTATIVE_RESPONSE = 1;
public static final int ACCEPTED_RESPONSE = 2;
/* Event */
TextView title;
TextView when;
TextView where;
View selectedMarker;
LinearLayout textContainer;
long instanceId;
ColorChipView colorChip;
long startTimeMilli;
boolean allDay;
boolean grayed;
int julianDay;
}
private float mScale;
private int COLOR_CHIP_ALL_DAY_HEIGHT;
private int COLOR_CHIP_HEIGHT;
public AgendaAdapter(Context context, int resource) {
super(context, resource, null);
@ -263,6 +242,26 @@ public class AgendaAdapter extends ResourceCursorAdapter {
}
}
static class ViewHolder {
public static final int DECLINED_RESPONSE = 0;
public static final int TENTATIVE_RESPONSE = 1;
public static final int ACCEPTED_RESPONSE = 2;
/* Event */
TextView title;
TextView when;
TextView where;
View selectedMarker;
LinearLayout textContainer;
long instanceId;
ColorChipView colorChip;
long startTimeMilli;
boolean allDay;
boolean grayed;
int julianDay;
}
/*
public static void updateReminder(View view, Context context, long begin, long eventId) {
ContentResolver cr = context.getContentResolver();

View File

@ -28,7 +28,6 @@ import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.TextView;
import org.sufficientlysecure.standalonecalendar.R;
import com.android.calendar.Utils;
import com.android.calendar.agenda.AgendaWindowAdapter.DayAdapterInfo;
@ -38,29 +37,22 @@ import java.util.Iterator;
import java.util.LinkedList;
import java.util.Locale;
import ws.xsoh.etar.R;
public class AgendaByDayAdapter extends BaseAdapter {
static final int TYPE_LAST = 2;
private static final int TYPE_DAY = 0;
private static final int TYPE_MEETING = 1;
static final int TYPE_LAST = 2;
private final Context mContext;
private final AgendaAdapter mAgendaAdapter;
private final LayoutInflater mInflater;
// Note: Formatter is not thread safe. Fine for now as it is only used by the main thread.
private final Formatter mFormatter;
private final StringBuilder mStringBuilder;
private ArrayList<RowInfo> mRowInfo;
private int mTodayJulianDay;
private Time mTmpTime;
private String mTimeZone;
// Note: Formatter is not thread safe. Fine for now as it is only used by the main thread.
private final Formatter mFormatter;
private final StringBuilder mStringBuilder;
static class ViewHolder {
TextView dayView;
TextView dateView;
int julianDay;
boolean grayed;
}
private final Runnable mTZUpdater = new Runnable() {
@Override
public void run() {
@ -94,7 +86,6 @@ public class AgendaByDayAdapter extends BaseAdapter {
return mRowInfo.get(position).mEventStartTimeMilli;
}
// Returns the position of a header of a specific item
public int getHeaderPosition(int position) {
if (mRowInfo == null || position >= mRowInfo.size()) {
@ -431,69 +422,6 @@ public class AgendaByDayAdapter extends BaseAdapter {
mRowInfo = rowInfo;
}
private static class RowInfo {
// mType is either a day header (TYPE_DAY) or an event (TYPE_MEETING)
final int mType;
final int mDay; // Julian day
final int mPosition; // cursor position (not used for TYPE_DAY)
// This is used to mark a day header as the first day with events that is "today"
// or later. This flag is used by the adapter to create a view with a visual separator
// between the past and the present/future
boolean mFirstDayAfterYesterday;
final long mEventId;
final long mEventStartTimeMilli;
final long mEventEndTimeMilli;
final long mInstanceId;
final boolean mAllDay;
RowInfo(int type, int julianDay, int position, long id, long startTime, long endTime,
long instanceId, boolean allDay) {
mType = type;
mDay = julianDay;
mPosition = position;
mEventId = id;
mEventStartTimeMilli = startTime;
mEventEndTimeMilli = endTime;
mFirstDayAfterYesterday = false;
mInstanceId = instanceId;
mAllDay = allDay;
}
RowInfo(int type, int julianDay) {
mType = type;
mDay = julianDay;
mPosition = 0;
mEventId = 0;
mEventStartTimeMilli = 0;
mEventEndTimeMilli = 0;
mFirstDayAfterYesterday = false;
mInstanceId = -1;
mAllDay = false;
}
}
private static class MultipleDayInfo {
final int mPosition;
final int mEndDay;
final long mEventId;
long mEventStartTimeMilli;
long mEventEndTimeMilli;
final long mInstanceId;
final boolean mAllDay;
MultipleDayInfo(int position, int endDay, long id, long startTime, long endTime,
long instanceId, boolean allDay) {
mPosition = position;
mEndDay = endDay;
mEventId = id;
mEventStartTimeMilli = startTime;
mEventEndTimeMilli = endTime;
mInstanceId = instanceId;
mAllDay = allDay;
}
}
/**
* Finds the position in the cursor of the event that best matches the time and Id.
* It will try to find the event that has the specified id and start time, if such event
@ -588,7 +516,6 @@ public class AgendaByDayAdapter extends BaseAdapter {
return minIndex;
}
/**
* Returns a flag indicating if this position is the first day after "yesterday" that has
* events in it.
@ -681,4 +608,74 @@ public class AgendaByDayAdapter extends BaseAdapter {
}
return true;
}
static class ViewHolder {
TextView dayView;
TextView dateView;
int julianDay;
boolean grayed;
}
private static class RowInfo {
// mType is either a day header (TYPE_DAY) or an event (TYPE_MEETING)
final int mType;
final int mDay; // Julian day
final int mPosition; // cursor position (not used for TYPE_DAY)
final long mEventId;
final long mEventStartTimeMilli;
final long mEventEndTimeMilli;
final long mInstanceId;
final boolean mAllDay;
// This is used to mark a day header as the first day with events that is "today"
// or later. This flag is used by the adapter to create a view with a visual separator
// between the past and the present/future
boolean mFirstDayAfterYesterday;
RowInfo(int type, int julianDay, int position, long id, long startTime, long endTime,
long instanceId, boolean allDay) {
mType = type;
mDay = julianDay;
mPosition = position;
mEventId = id;
mEventStartTimeMilli = startTime;
mEventEndTimeMilli = endTime;
mFirstDayAfterYesterday = false;
mInstanceId = instanceId;
mAllDay = allDay;
}
RowInfo(int type, int julianDay) {
mType = type;
mDay = julianDay;
mPosition = 0;
mEventId = 0;
mEventStartTimeMilli = 0;
mEventEndTimeMilli = 0;
mFirstDayAfterYesterday = false;
mInstanceId = -1;
mAllDay = false;
}
}
private static class MultipleDayInfo {
final int mPosition;
final int mEndDay;
final long mEventId;
final long mInstanceId;
final boolean mAllDay;
long mEventStartTimeMilli;
long mEventEndTimeMilli;
MultipleDayInfo(int position, int endDay, long id, long startTime, long endTime,
long instanceId, boolean allDay) {
mPosition = position;
mEndDay = endDay;
mEventId = id;
mEventStartTimeMilli = startTime;
mEventEndTimeMilli = endTime;
mInstanceId = instanceId;
mAllDay = allDay;
}
}
}

View File

@ -40,26 +40,35 @@ import com.android.calendar.CalendarController.EventType;
import com.android.calendar.CalendarController.ViewType;
import com.android.calendar.EventInfoFragment;
import com.android.calendar.GeneralPreferences;
import org.sufficientlysecure.standalonecalendar.R;
import com.android.calendar.StickyHeaderListView;
import com.android.calendar.Utils;
import java.util.Date;
import ws.xsoh.etar.R;
public class AgendaFragment extends Fragment implements CalendarController.EventHandler,
OnScrollListener {
private static final String TAG = AgendaFragment.class.getSimpleName();
private static boolean DEBUG = false;
protected static final String BUNDLE_KEY_RESTORE_TIME = "key_restore_time";
protected static final String BUNDLE_KEY_RESTORE_INSTANCE_ID = "key_restore_instance_id";
private static final String TAG = AgendaFragment.class.getSimpleName();
private static boolean DEBUG = false;
private final Time mTime;
private final long mInitialTimeMillis;
// Tracks the time of the top visible view in order to send UPDATE_TITLE messages to the action
// bar.
int mJulianDayOnTop = -1;
private AgendaListView mAgendaListView;
private Activity mActivity;
private final Time mTime;
private String mTimeZone;
private final long mInitialTimeMillis;
private final Runnable mTZUpdater = new Runnable() {
@Override
public void run() {
mTimeZone = Utils.getTimeZone(getActivity(), this);
mTime.switchTimezone(mTimeZone);
}
};
private boolean mShowEventDetailsWithAgenda;
private CalendarController mController;
private EventInfoFragment mEventFragment;
@ -71,26 +80,13 @@ public class AgendaFragment extends Fragment implements CalendarController.Event
private AgendaWindowAdapter mAdapter = null;
private boolean mForceReplace = true;
private long mLastShownEventId = -1;
// Tracks the time of the top visible view in order to send UPDATE_TITLE messages to the action
// bar.
int mJulianDayOnTop = -1;
private final Runnable mTZUpdater = new Runnable() {
@Override
public void run() {
mTimeZone = Utils.getTimeZone(getActivity(), this);
mTime.switchTimezone(mTimeZone);
}
};
private long mLastHandledEventId = -1;
private Time mLastHandledEventTime = null;
public AgendaFragment() {
this(0, false);
}
// timeMillis - time of first event to show
// usedForSearch - indicates if this fragment is used in the search fragment
public AgendaFragment(long timeMillis, boolean usedForSearch) {
@ -326,7 +322,7 @@ public class AgendaFragment extends Fragment implements CalendarController.Event
return;
}
mAgendaListView.goTo(mTime, event.id, mQuery, false,
((event.extraLong & CalendarController.EXTRA_GOTO_TODAY) != 0 &&
((event.extraLong & CalendarController.EXTRA_GOTO_TODAY) != 0 &&
mShowEventDetailsWithAgenda) ? true : false);
AgendaAdapter.ViewHolder vh = mAgendaListView.getSelectedViewHolder();
// Make sure that on the first time the event info is shown to recreate it
@ -359,8 +355,6 @@ public class AgendaFragment extends Fragment implements CalendarController.Event
return EventType.GO_TO | EventType.EVENTS_CHANGED | ((mUsedForSearch)?EventType.SEARCH:0);
}
private long mLastHandledEventId = -1;
private Time mLastHandledEventTime = null;
@Override
public void handleEvent(EventInfo event) {
if (event.eventType == EventType.GO_TO) {

View File

@ -16,15 +16,6 @@
package com.android.calendar.agenda;
import com.android.calendar.CalendarController;
import com.android.calendar.CalendarController.EventType;
import com.android.calendar.DeleteEventHelper;
import org.sufficientlysecure.standalonecalendar.R;
import com.android.calendar.Utils;
import com.android.calendar.agenda.AgendaAdapter.ViewHolder;
import com.android.calendar.agenda.AgendaWindowAdapter.DayAdapterInfo;
import com.android.calendar.agenda.AgendaWindowAdapter.AgendaItem;
import android.content.Context;
import android.graphics.Rect;
import android.os.Handler;
@ -38,6 +29,16 @@ import android.widget.AdapterView.OnItemClickListener;
import android.widget.ListView;
import android.widget.TextView;
import com.android.calendar.CalendarController;
import com.android.calendar.CalendarController.EventType;
import com.android.calendar.DeleteEventHelper;
import com.android.calendar.Utils;
import com.android.calendar.agenda.AgendaAdapter.ViewHolder;
import com.android.calendar.agenda.AgendaWindowAdapter.AgendaItem;
import com.android.calendar.agenda.AgendaWindowAdapter.DayAdapterInfo;
import ws.xsoh.etar.R;
public class AgendaListView extends ListView implements OnItemClickListener {
private static final String TAG = "AgendaListView";
@ -49,9 +50,6 @@ public class AgendaListView extends ListView implements OnItemClickListener {
private Context mContext;
private String mTimeZone;
private Time mTime;
private boolean mShowEventDetailsWithAgenda;
private Handler mHandler = null;
private final Runnable mTZUpdater = new Runnable() {
@Override
public void run() {
@ -59,7 +57,8 @@ public class AgendaListView extends ListView implements OnItemClickListener {
mTime.switchTimezone(mTimeZone);
}
};
private boolean mShowEventDetailsWithAgenda;
private Handler mHandler = null;
// runs every midnight and refreshes the view in order to update the past/present
// separator
private final Runnable mMidnightUpdater = new Runnable() {

View File

@ -44,7 +44,6 @@ import android.widget.TextView;
import com.android.calendar.CalendarController;
import com.android.calendar.CalendarController.EventType;
import com.android.calendar.CalendarController.ViewType;
import org.sufficientlysecure.standalonecalendar.R;
import com.android.calendar.StickyHeaderListView;
import com.android.calendar.Utils;
@ -55,6 +54,8 @@ import java.util.LinkedList;
import java.util.Locale;
import java.util.concurrent.ConcurrentLinkedQueue;
import ws.xsoh.etar.R;
/*
Bugs Bugs Bugs:
- At rotation and launch time, the initial position is not set properly. This code is calling
@ -76,15 +77,6 @@ Check for leaks and excessive allocations
public class AgendaWindowAdapter extends BaseAdapter
implements StickyHeaderListView.HeaderIndexer, StickyHeaderListView.HeaderHeightListener{
static final boolean BASICLOG = false;
static final boolean DEBUGLOG = false;
private static final String TAG = "AgendaWindowAdapter";
private static final String AGENDA_SORT_ORDER =
CalendarContract.Instances.START_DAY + " ASC, " +
CalendarContract.Instances.BEGIN + " ASC, " +
CalendarContract.Events.TITLE + " ASC";
public static final int INDEX_INSTANCE_ID = 0;
public static final int INDEX_TITLE = 1;
public static final int INDEX_EVENT_LOCATION = 2;
@ -102,7 +94,13 @@ public class AgendaWindowAdapter extends BaseAdapter
public static final int INDEX_OWNER_ACCOUNT = 14;
public static final int INDEX_CAN_ORGANIZER_RESPOND= 15;
public static final int INDEX_TIME_ZONE = 16;
static final boolean BASICLOG = false;
static final boolean DEBUGLOG = false;
private static final String TAG = "AgendaWindowAdapter";
private static final String AGENDA_SORT_ORDER =
CalendarContract.Instances.START_DAY + " ASC, " +
CalendarContract.Instances.BEGIN + " ASC, " +
CalendarContract.Events.TITLE + " ASC";
private static final String[] PROJECTION = new String[] {
Instances._ID, // 0
Instances.TITLE, // 1
@ -122,13 +120,6 @@ public class AgendaWindowAdapter extends BaseAdapter
Instances.CAN_ORGANIZER_RESPOND, // 15
Instances.EVENT_TIMEZONE, // 16
};
static {
if (!Utils.isJellybeanOrLater()) {
PROJECTION[INDEX_COLOR] = Instances.CALENDAR_COLOR;
}
}
// Listview may have a bug where the index/position is not consistent when there's a header.
// position == positionInListView - OFF_BY_ONE_BUG
// TODO Need to look into this.
@ -138,73 +129,35 @@ public class AgendaWindowAdapter extends BaseAdapter
private static final int MIN_QUERY_DURATION = 7; // days
private static final int MAX_QUERY_DURATION = 60; // days
private static final int PREFETCH_BOUNDARY = 1;
/** Times to auto-expand/retry query after getting no data */
private static final int RETRIES_ON_NO_DATA = 1;
// Types of Query
private static final int QUERY_TYPE_OLDER = 0; // Query for older events
private static final int QUERY_TYPE_NEWER = 1; // Query for newer events
private static final int QUERY_TYPE_CLEAN = 2; // Delete everything and query around a date
static {
if (!Utils.isJellybeanOrLater()) {
PROJECTION[INDEX_COLOR] = Instances.CALENDAR_COLOR;
}
}
private final Context mContext;
private final Resources mResources;
private final QueryHandler mQueryHandler;
private final AgendaListView mAgendaListView;
/** The sum of the rows in all the adapters */
private int mRowCount;
/** The number of times we have queried and gotten no results back */
private int mEmptyCursorCount;
/** Cached value of the last used adapter */
private DayAdapterInfo mLastUsedInfo;
private final LinkedList<DayAdapterInfo> mAdapterInfos =
new LinkedList<DayAdapterInfo>();
private final ConcurrentLinkedQueue<QuerySpec> mQueryQueue =
new ConcurrentLinkedQueue<QuerySpec>();
private final TextView mHeaderView;
private final TextView mFooterView;
private boolean mDoneSettingUpHeaderFooter = false;
private final boolean mIsTabletConfig;
boolean mCleanQueryInitiated = false;
private int mStickyHeaderSize = 44; // Initial size big enough for it to work
/**
* When the user scrolled to the top, a query will be made for older events
* and this will be incremented. Don't make more requests if
* mOlderRequests > mOlderRequestsProcessed.
*/
private int mOlderRequests;
/** Number of "older" query that has been processed. */
private int mOlderRequestsProcessed;
/**
* When the user scrolled to the bottom, a query will be made for newer
* events and this will be incremented. Don't make more requests if
* mNewerRequests > mNewerRequestsProcessed.
*/
private int mNewerRequests;
/** Number of "newer" query that has been processed. */
private int mNewerRequestsProcessed;
// Note: Formatter is not thread safe. Fine for now as it is only used by the main thread.
private final Formatter mFormatter;
private final StringBuilder mStringBuilder;
private String mTimeZone;
// defines if to pop-up the current event when the agenda is first shown
private final boolean mShowEventOnStart;
private final Runnable mTZUpdater = new Runnable() {
@Override
public void run() {
mTimeZone = Utils.getTimeZone(mContext, this);
notifyDataSetChanged();
}
};
private final Handler mDataChangedHandler = new Handler();
private final Runnable mDataChangedRunnable = new Runnable() {
@Override
@ -212,129 +165,56 @@ public class AgendaWindowAdapter extends BaseAdapter
notifyDataSetChanged();
}
};
private boolean mShuttingDown;
private boolean mHideDeclined;
// Used to stop a fling motion if the ListView is set to a specific position
int mListViewScrollState = OnScrollListener.SCROLL_STATE_IDLE;
/** The current search query, or null if none */
private String mSearchQuery;
private long mSelectedInstanceId = -1;
private final int mSelectedItemBackgroundColor;
private final int mSelectedItemTextColor;
private final float mItemRightMargin;
// Types of Query
private static final int QUERY_TYPE_OLDER = 0; // Query for older events
private static final int QUERY_TYPE_NEWER = 1; // Query for newer events
private static final int QUERY_TYPE_CLEAN = 2; // Delete everything and query around a date
private static class QuerySpec {
long queryStartMillis;
Time goToTime;
int start;
int end;
String searchQuery;
int queryType;
long id;
public QuerySpec(int queryType) {
this.queryType = queryType;
id = -1;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + end;
result = prime * result + (int) (queryStartMillis ^ (queryStartMillis >>> 32));
result = prime * result + queryType;
result = prime * result + start;
if (searchQuery != null) {
result = prime * result + searchQuery.hashCode();
}
if (goToTime != null) {
long goToTimeMillis = goToTime.toMillis(false);
result = prime * result + (int) (goToTimeMillis ^ (goToTimeMillis >>> 32));
}
result = prime * result + (int)id;
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj) return true;
if (obj == null) return false;
if (getClass() != obj.getClass()) return false;
QuerySpec other = (QuerySpec) obj;
if (end != other.end || queryStartMillis != other.queryStartMillis
|| queryType != other.queryType || start != other.start
|| Utils.equals(searchQuery, other.searchQuery) || id != other.id) {
return false;
}
if (goToTime != null) {
if (goToTime.toMillis(false) != other.goToTime.toMillis(false)) {
return false;
}
} else {
if (other.goToTime != null) {
return false;
}
}
return true;
}
}
boolean mCleanQueryInitiated = false;
// Used to stop a fling motion if the ListView is set to a specific position
int mListViewScrollState = OnScrollListener.SCROLL_STATE_IDLE;
/**
* Class representing a list item within the Agenda view. Could be either an instance of an
* event, or a header marking the specific day.
*
* The begin and end times of an AgendaItem should always be in local time, even if the event
* is all day. buildAgendaItemFromCursor() converts each event to local time.
* The sum of the rows in all the adapters
*/
static class AgendaItem {
long begin;
long end;
long id;
int startDay;
boolean allDay;
}
static class DayAdapterInfo {
Cursor cursor;
AgendaByDayAdapter dayAdapter;
int start; // start day of the cursor's coverage
int end; // end day of the cursor's coverage
int offset; // offset in position in the list view
int size; // dayAdapter.getCount()
public DayAdapterInfo(Context context) {
dayAdapter = new AgendaByDayAdapter(context);
}
private int mRowCount;
/**
* The number of times we have queried and gotten no results back
*/
private int mEmptyCursorCount;
/**
* Cached value of the last used adapter
*/
private DayAdapterInfo mLastUsedInfo;
private boolean mDoneSettingUpHeaderFooter = false;
private int mStickyHeaderSize = 44; // Initial size big enough for it to work
/**
* When the user scrolled to the top, a query will be made for older events
* and this will be incremented. Don't make more requests if
* mOlderRequests > mOlderRequestsProcessed.
*/
private int mOlderRequests;
/** Number of "older" query that has been processed. */
private int mOlderRequestsProcessed;
/**
* When the user scrolled to the bottom, a query will be made for newer
* events and this will be incremented. Don't make more requests if
* mNewerRequests > mNewerRequestsProcessed.
*/
private int mNewerRequests;
/** Number of "newer" query that has been processed. */
private int mNewerRequestsProcessed;
private String mTimeZone;
private final Runnable mTZUpdater = new Runnable() {
@Override
public String toString() {
// Static class, so the time in this toString will not reflect the
// home tz settings. This should only affect debugging.
Time time = new Time();
StringBuilder sb = new StringBuilder();
time.setJulianDay(start);
time.normalize(false);
sb.append("Start:").append(time.toString());
time.setJulianDay(end);
time.normalize(false);
sb.append(" End:").append(time.toString());
sb.append(" Offset:").append(offset);
sb.append(" Size:").append(size);
return sb.toString();
public void run() {
mTimeZone = Utils.getTimeZone(mContext, this);
notifyDataSetChanged();
}
}
};
private boolean mShuttingDown;
private boolean mHideDeclined;
/** The current search query, or null if none */
private String mSearchQuery;
private long mSelectedInstanceId = -1;
private AgendaAdapter.ViewHolder mSelectedVH = null;
public AgendaWindowAdapter(Context context,
AgendaListView agendaListView, boolean showEventOnStart) {
@ -369,6 +249,25 @@ public class AgendaWindowAdapter extends BaseAdapter
mAgendaListView.addHeaderView(mHeaderView);
}
static String getViewTitle(View x) {
String title = "";
if (x != null) {
Object yy = x.getTag();
if (yy instanceof AgendaAdapter.ViewHolder) {
TextView tv = ((AgendaAdapter.ViewHolder) yy).title;
if (tv != null) {
title = (String) tv.getText();
}
} else if (yy != null) {
TextView dateView = ((AgendaByDayAdapter.ViewHolder) yy).dateView;
if (dateView != null) {
title = (String) dateView.getText();
}
}
}
return title;
}
// Method in Adapter
@Override
public int getViewTypeCount() {
@ -529,8 +428,6 @@ public class AgendaWindowAdapter extends BaseAdapter
return v;
}
private AgendaAdapter.ViewHolder mSelectedVH = null;
private int findEventPositionNearestTime(Time time, long id) {
DayAdapterInfo info = getAdapterInfoByTime(time);
int pos = -1;
@ -954,6 +851,221 @@ public class AgendaWindowAdapter extends BaseAdapter
formatDateString(end)));
}
public void onResume() {
mTZUpdater.run();
}
public void setHideDeclinedEvents(boolean hideDeclined) {
mHideDeclined = hideDeclined;
}
public void setSelectedView(View v) {
if (v != null) {
Object vh = v.getTag();
if (vh instanceof AgendaAdapter.ViewHolder) {
mSelectedVH = (AgendaAdapter.ViewHolder) vh;
if (mSelectedInstanceId != mSelectedVH.instanceId) {
mSelectedInstanceId = mSelectedVH.instanceId;
notifyDataSetChanged();
}
}
}
}
public AgendaAdapter.ViewHolder getSelectedViewHolder() {
return mSelectedVH;
}
public long getSelectedInstanceId() {
return mSelectedInstanceId;
}
public void setSelectedInstanceId(long selectedInstanceId) {
mSelectedInstanceId = selectedInstanceId;
mSelectedVH = null;
}
private long findInstanceIdFromPosition(int position) {
DayAdapterInfo info = getAdapterInfoByPosition(position);
if (info != null) {
return info.dayAdapter.getInstanceId(position - info.offset);
}
return -1;
}
private long findStartTimeFromPosition(int position) {
DayAdapterInfo info = getAdapterInfoByPosition(position);
if (info != null) {
return info.dayAdapter.getStartTime(position - info.offset);
}
return -1;
}
private Cursor getCursorByPosition(int position) {
DayAdapterInfo info = getAdapterInfoByPosition(position);
if (info != null) {
return info.cursor;
}
return null;
}
private int getCursorPositionByPosition(int position) {
DayAdapterInfo info = getAdapterInfoByPosition(position);
if (info != null) {
return info.dayAdapter.getCursorPosition(position - info.offset);
}
return -1;
}
// Returns the location of the day header of a specific event specified in the position
// in the adapter
@Override
public int getHeaderPositionFromItemPosition(int position) {
// For phone configuration, return -1 so there will be no sticky header
if (!mIsTabletConfig) {
return -1;
}
DayAdapterInfo info = getAdapterInfoByPosition(position);
if (info != null) {
int pos = info.dayAdapter.getHeaderPosition(position - info.offset);
return (pos != -1) ? (pos + info.offset) : -1;
}
return -1;
}
// Returns the number of events for a specific day header
@Override
public int getHeaderItemsNumber(int headerPosition) {
if (headerPosition < 0 || !mIsTabletConfig) {
return -1;
}
DayAdapterInfo info = getAdapterInfoByPosition(headerPosition);
if (info != null) {
return info.dayAdapter.getHeaderItemsCount(headerPosition - info.offset);
}
return -1;
}
@Override
public void OnHeaderHeightChanged(int height) {
mStickyHeaderSize = height;
}
public int getStickyHeaderHeight() {
return mStickyHeaderSize;
}
// Implementation of HeaderIndexer interface for StickyHeeaderListView
public void setScrollState(int state) {
mListViewScrollState = state;
}
private static class QuerySpec {
long queryStartMillis;
Time goToTime;
int start;
int end;
String searchQuery;
int queryType;
long id;
public QuerySpec(int queryType) {
this.queryType = queryType;
id = -1;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + end;
result = prime * result + (int) (queryStartMillis ^ (queryStartMillis >>> 32));
result = prime * result + queryType;
result = prime * result + start;
if (searchQuery != null) {
result = prime * result + searchQuery.hashCode();
}
if (goToTime != null) {
long goToTimeMillis = goToTime.toMillis(false);
result = prime * result + (int) (goToTimeMillis ^ (goToTimeMillis >>> 32));
}
result = prime * result + (int) id;
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj) return true;
if (obj == null) return false;
if (getClass() != obj.getClass()) return false;
QuerySpec other = (QuerySpec) obj;
if (end != other.end || queryStartMillis != other.queryStartMillis
|| queryType != other.queryType || start != other.start
|| Utils.equals(searchQuery, other.searchQuery) || id != other.id) {
return false;
}
if (goToTime != null) {
if (goToTime.toMillis(false) != other.goToTime.toMillis(false)) {
return false;
}
} else {
if (other.goToTime != null) {
return false;
}
}
return true;
}
}
/**
* Class representing a list item within the Agenda view. Could be either an instance of an
* event, or a header marking the specific day.
* <p/>
* The begin and end times of an AgendaItem should always be in local time, even if the event
* is all day. buildAgendaItemFromCursor() converts each event to local time.
*/
static class AgendaItem {
long begin;
long end;
long id;
int startDay;
boolean allDay;
}
static class DayAdapterInfo {
Cursor cursor;
AgendaByDayAdapter dayAdapter;
int start; // start day of the cursor's coverage
int end; // end day of the cursor's coverage
int offset; // offset in position in the list view
int size; // dayAdapter.getCount()
public DayAdapterInfo(Context context) {
dayAdapter = new AgendaByDayAdapter(context);
}
@Override
public String toString() {
// Static class, so the time in this toString will not reflect the
// home tz settings. This should only affect debugging.
Time time = new Time();
StringBuilder sb = new StringBuilder();
time.setJulianDay(start);
time.normalize(false);
sb.append("Start:").append(time.toString());
time.setJulianDay(end);
time.normalize(false);
sb.append(" End:").append(time.toString());
sb.append(" Offset:").append(offset);
sb.append(" Size:").append(size);
return sb.toString();
}
}
private class QueryHandler extends AsyncQueryHandler {
public QueryHandler(ContentResolver cr) {
@ -1277,136 +1389,4 @@ public class AgendaWindowAdapter extends BaseAdapter
}
}
}
static String getViewTitle(View x) {
String title = "";
if (x != null) {
Object yy = x.getTag();
if (yy instanceof AgendaAdapter.ViewHolder) {
TextView tv = ((AgendaAdapter.ViewHolder) yy).title;
if (tv != null) {
title = (String) tv.getText();
}
} else if (yy != null) {
TextView dateView = ((AgendaByDayAdapter.ViewHolder) yy).dateView;
if (dateView != null) {
title = (String) dateView.getText();
}
}
}
return title;
}
public void onResume() {
mTZUpdater.run();
}
public void setHideDeclinedEvents(boolean hideDeclined) {
mHideDeclined = hideDeclined;
}
public void setSelectedView(View v) {
if (v != null) {
Object vh = v.getTag();
if (vh instanceof AgendaAdapter.ViewHolder) {
mSelectedVH = (AgendaAdapter.ViewHolder) vh;
if (mSelectedInstanceId != mSelectedVH.instanceId) {
mSelectedInstanceId = mSelectedVH.instanceId;
notifyDataSetChanged();
}
}
}
}
public AgendaAdapter.ViewHolder getSelectedViewHolder() {
return mSelectedVH;
}
public long getSelectedInstanceId() {
return mSelectedInstanceId;
}
public void setSelectedInstanceId(long selectedInstanceId) {
mSelectedInstanceId = selectedInstanceId;
mSelectedVH = null;
}
private long findInstanceIdFromPosition(int position) {
DayAdapterInfo info = getAdapterInfoByPosition(position);
if (info != null) {
return info.dayAdapter.getInstanceId(position - info.offset);
}
return -1;
}
private long findStartTimeFromPosition(int position) {
DayAdapterInfo info = getAdapterInfoByPosition(position);
if (info != null) {
return info.dayAdapter.getStartTime(position - info.offset);
}
return -1;
}
private Cursor getCursorByPosition(int position) {
DayAdapterInfo info = getAdapterInfoByPosition(position);
if (info != null) {
return info.cursor;
}
return null;
}
private int getCursorPositionByPosition(int position) {
DayAdapterInfo info = getAdapterInfoByPosition(position);
if (info != null) {
return info.dayAdapter.getCursorPosition(position - info.offset);
}
return -1;
}
// Implementation of HeaderIndexer interface for StickyHeeaderListView
// Returns the location of the day header of a specific event specified in the position
// in the adapter
@Override
public int getHeaderPositionFromItemPosition(int position) {
// For phone configuration, return -1 so there will be no sticky header
if (!mIsTabletConfig) {
return -1;
}
DayAdapterInfo info = getAdapterInfoByPosition(position);
if (info != null) {
int pos = info.dayAdapter.getHeaderPosition(position - info.offset);
return (pos != -1)?(pos + info.offset):-1;
}
return -1;
}
// Returns the number of events for a specific day header
@Override
public int getHeaderItemsNumber(int headerPosition) {
if (headerPosition < 0 || !mIsTabletConfig) {
return -1;
}
DayAdapterInfo info = getAdapterInfoByPosition(headerPosition);
if (info != null) {
return info.dayAdapter.getHeaderItemsCount(headerPosition - info.offset);
}
return -1;
}
@Override
public void OnHeaderHeightChanged(int height) {
mStickyHeaderSize = height;
}
public int getStickyHeaderHeight() {
return mStickyHeaderSize;
}
public void setScrollState(int state) {
mListViewScrollState = state;
}
}

View File

@ -39,20 +39,32 @@ import android.widget.ListView;
import com.android.calendar.AsyncQueryService;
import com.android.calendar.EventInfoActivity;
import org.sufficientlysecure.standalonecalendar.R;
import com.android.calendar.Utils;
import com.android.calendar.alerts.GlobalDismissManager.AlarmId;
import java.util.LinkedList;
import java.util.List;
import ws.xsoh.etar.R;
/**
* The alert panel that pops up when there is a calendar event alarm.
* This activity is started by an intent that specifies an event id.
*/
public class AlertActivity extends Activity implements OnClickListener {
public static final int INDEX_ROW_ID = 0;
public static final int INDEX_TITLE = 1;
public static final int INDEX_EVENT_LOCATION = 2;
public static final int INDEX_ALL_DAY = 3;
public static final int INDEX_BEGIN = 4;
public static final int INDEX_END = 5;
public static final int INDEX_EVENT_ID = 6;
public static final int INDEX_COLOR = 7;
public static final int INDEX_RRULE = 8;
public static final int INDEX_HAS_ALARM = 9;
public static final int INDEX_STATE = 10;
public static final int INDEX_ALARM_TIME = 11;
private static final String TAG = "AlertActivity";
private static final String[] PROJECTION = new String[] {
CalendarAlerts._ID, // 0
CalendarAlerts.TITLE, // 1
@ -67,20 +79,6 @@ public class AlertActivity extends Activity implements OnClickListener {
CalendarAlerts.STATE, // 10
CalendarAlerts.ALARM_TIME, // 11
};
public static final int INDEX_ROW_ID = 0;
public static final int INDEX_TITLE = 1;
public static final int INDEX_EVENT_LOCATION = 2;
public static final int INDEX_ALL_DAY = 3;
public static final int INDEX_BEGIN = 4;
public static final int INDEX_END = 5;
public static final int INDEX_EVENT_ID = 6;
public static final int INDEX_COLOR = 7;
public static final int INDEX_RRULE = 8;
public static final int INDEX_HAS_ALARM = 9;
public static final int INDEX_STATE = 10;
public static final int INDEX_ALARM_TIME = 11;
private static final String SELECTION = CalendarAlerts.STATE + "=?";
private static final String[] SELECTIONARG = new String[] {
Integer.toString(CalendarAlerts.STATE_FIRED)
@ -90,8 +88,39 @@ public class AlertActivity extends Activity implements OnClickListener {
private QueryHandler mQueryHandler;
private Cursor mCursor;
private ListView mListView;
private Button mDismissAllButton;
private final OnItemClickListener mViewListener = new OnItemClickListener() {
@SuppressLint("NewApi")
@Override
public void onItemClick(AdapterView<?> parent, View view, int position,
long i) {
AlertActivity alertActivity = AlertActivity.this;
Cursor cursor = alertActivity.getItemForView(view);
long alarmId = cursor.getLong(INDEX_ROW_ID);
long eventId = cursor.getLong(AlertActivity.INDEX_EVENT_ID);
long startMillis = cursor.getLong(AlertActivity.INDEX_BEGIN);
// Mark this alarm as DISMISSED
dismissAlarm(alarmId, eventId, startMillis);
// build an intent and task stack to start EventInfoActivity with AllInOneActivity
// as the parent activity rooted to home.
long endMillis = cursor.getLong(AlertActivity.INDEX_END);
Intent eventIntent = AlertUtils.buildEventViewIntent(AlertActivity.this, eventId,
startMillis, endMillis);
if (Utils.isJellybeanOrLater()) {
TaskStackBuilder.create(AlertActivity.this).addParentStack(EventInfoActivity.class)
.addNextIntent(eventIntent).startActivities();
} else {
alertActivity.startActivity(eventIntent);
}
alertActivity.finish();
}
};
private Button mDismissAllButton;
private void dismissFiredAlarms() {
ContentValues values = new ContentValues(1 /* size */);
@ -145,65 +174,6 @@ public class AlertActivity extends Activity implements OnClickListener {
}.execute(alarmIds);
}
private class QueryHandler extends AsyncQueryService {
public QueryHandler(Context context) {
super(context);
}
@Override
protected void onQueryComplete(int token, Object cookie, Cursor cursor) {
// Only set mCursor if the Activity is not finishing. Otherwise close the cursor.
if (!isFinishing()) {
mCursor = cursor;
mAdapter.changeCursor(cursor);
mListView.setSelection(cursor.getCount() - 1);
// The results are in, enable the buttons
mDismissAllButton.setEnabled(true);
} else {
cursor.close();
}
}
@Override
protected void onUpdateComplete(int token, Object cookie, int result) {
// Ignore
}
}
private final OnItemClickListener mViewListener = new OnItemClickListener() {
@SuppressLint("NewApi")
@Override
public void onItemClick(AdapterView<?> parent, View view, int position,
long i) {
AlertActivity alertActivity = AlertActivity.this;
Cursor cursor = alertActivity.getItemForView(view);
long alarmId = cursor.getLong(INDEX_ROW_ID);
long eventId = cursor.getLong(AlertActivity.INDEX_EVENT_ID);
long startMillis = cursor.getLong(AlertActivity.INDEX_BEGIN);
// Mark this alarm as DISMISSED
dismissAlarm(alarmId, eventId, startMillis);
// build an intent and task stack to start EventInfoActivity with AllInOneActivity
// as the parent activity rooted to home.
long endMillis = cursor.getLong(AlertActivity.INDEX_END);
Intent eventIntent = AlertUtils.buildEventViewIntent(AlertActivity.this, eventId,
startMillis, endMillis);
if (Utils.isJellybeanOrLater()) {
TaskStackBuilder.create(AlertActivity.this).addParentStack(EventInfoActivity.class)
.addNextIntent(eventIntent).startActivities();
} else {
alertActivity.startActivity(eventIntent);
}
alertActivity.finish();
}
};
@Override
protected void onCreate(Bundle icicle) {
super.onCreate(icicle);
@ -292,4 +262,30 @@ public class AlertActivity extends Activity implements OnClickListener {
}
return (Cursor) mListView.getAdapter().getItem(index);
}
private class QueryHandler extends AsyncQueryService {
public QueryHandler(Context context) {
super(context);
}
@Override
protected void onQueryComplete(int token, Object cookie, Cursor cursor) {
// Only set mCursor if the Activity is not finishing. Otherwise close the cursor.
if (!isFinishing()) {
mCursor = cursor;
mAdapter.changeCursor(cursor);
mListView.setSelection(cursor.getCount() - 1);
// The results are in, enable the buttons
mDismissAllButton.setEnabled(true);
} else {
cursor.close();
}
}
@Override
protected void onUpdateComplete(int token, Object cookie, int result) {
// Ignore
}
}
}

View File

@ -16,9 +16,6 @@
package com.android.calendar.alerts;
import org.sufficientlysecure.standalonecalendar.R;
import com.android.calendar.Utils;
import android.content.Context;
import android.content.res.Resources;
import android.database.Cursor;
@ -30,9 +27,13 @@ import android.view.View;
import android.widget.ResourceCursorAdapter;
import android.widget.TextView;
import com.android.calendar.Utils;
import java.util.Locale;
import java.util.TimeZone;
import ws.xsoh.etar.R;
public class AlertAdapter extends ResourceCursorAdapter {
private static AlertActivity alertActivity;
@ -46,39 +47,6 @@ public class AlertAdapter extends ResourceCursorAdapter {
alertActivity = activity;
}
@Override
public void bindView(View view, Context context, Cursor cursor) {
View square = view.findViewById(R.id.color_square);
int color = Utils.getDisplayColorFromColor(cursor.getInt(AlertActivity.INDEX_COLOR));
square.setBackgroundColor(color);
// Repeating info
View repeatContainer = view.findViewById(R.id.repeat_icon);
String rrule = cursor.getString(AlertActivity.INDEX_RRULE);
if (!TextUtils.isEmpty(rrule)) {
repeatContainer.setVisibility(View.VISIBLE);
} else {
repeatContainer.setVisibility(View.GONE);
}
/*
// Reminder
boolean hasAlarm = cursor.getInt(AlertActivity.INDEX_HAS_ALARM) != 0;
if (hasAlarm) {
AgendaAdapter.updateReminder(view, context, cursor.getLong(AlertActivity.INDEX_BEGIN),
cursor.getLong(AlertActivity.INDEX_EVENT_ID));
}
*/
String eventName = cursor.getString(AlertActivity.INDEX_TITLE);
String location = cursor.getString(AlertActivity.INDEX_EVENT_LOCATION);
long startMillis = cursor.getLong(AlertActivity.INDEX_BEGIN);
long endMillis = cursor.getLong(AlertActivity.INDEX_END);
boolean allDay = cursor.getInt(AlertActivity.INDEX_ALL_DAY) != 0;
updateView(context, view, eventName, location, startMillis, endMillis, allDay);
}
public static void updateView(Context context, View view, String eventName, String location,
long startMillis, long endMillis, boolean allDay) {
Resources res = context.getResources();
@ -146,6 +114,39 @@ public class AlertAdapter extends ResourceCursorAdapter {
}
}
@Override
public void bindView(View view, Context context, Cursor cursor) {
View square = view.findViewById(R.id.color_square);
int color = Utils.getDisplayColorFromColor(cursor.getInt(AlertActivity.INDEX_COLOR));
square.setBackgroundColor(color);
// Repeating info
View repeatContainer = view.findViewById(R.id.repeat_icon);
String rrule = cursor.getString(AlertActivity.INDEX_RRULE);
if (!TextUtils.isEmpty(rrule)) {
repeatContainer.setVisibility(View.VISIBLE);
} else {
repeatContainer.setVisibility(View.GONE);
}
/*
// Reminder
boolean hasAlarm = cursor.getInt(AlertActivity.INDEX_HAS_ALARM) != 0;
if (hasAlarm) {
AgendaAdapter.updateReminder(view, context, cursor.getLong(AlertActivity.INDEX_BEGIN),
cursor.getLong(AlertActivity.INDEX_EVENT_ID));
}
*/
String eventName = cursor.getString(AlertActivity.INDEX_TITLE);
String location = cursor.getString(AlertActivity.INDEX_EVENT_LOCATION);
long startMillis = cursor.getLong(AlertActivity.INDEX_BEGIN);
long endMillis = cursor.getLong(AlertActivity.INDEX_END);
boolean allDay = cursor.getInt(AlertActivity.INDEX_ALL_DAY) != 0;
updateView(context, view, eventName, location, startMillis, endMillis, allDay);
}
@Override
protected void onContentChanged () {
super.onContentChanged();

View File

@ -43,7 +43,6 @@ import android.util.Log;
import android.view.View;
import android.widget.RemoteViews;
import org.sufficientlysecure.standalonecalendar.R;
import com.android.calendar.Utils;
import com.android.calendar.alerts.AlertService.NotificationWrapper;
@ -51,6 +50,8 @@ import java.util.ArrayList;
import java.util.List;
import java.util.regex.Pattern;
import ws.xsoh.etar.R;
/**
* Receives android.intent.action.EVENT_REMINDER intents and handles
* event reminders. The intent URI specifies an alert id in the
@ -66,113 +67,52 @@ import java.util.regex.Pattern;
* -n "com.android.calendar/.alerts.AlertReceiver"
*/
public class AlertReceiver extends BroadcastReceiver {
// The broadcast for notification refreshes scheduled by the app. This is to
// distinguish the EVENT_REMINDER broadcast sent by the provider.
public static final String EVENT_REMINDER_APP_ACTION =
"com.android.calendar.EVENT_REMINDER_APP";
public static final String ACTION_DISMISS_OLD_REMINDERS = "removeOldReminders";
static final Object mStartingServiceSync = new Object();
private static final String TAG = "AlertReceiver";
private static final String DELETE_ALL_ACTION = "com.android.calendar.DELETEALL";
private static final String MAP_ACTION = "com.android.calendar.MAP";
private static final String CALL_ACTION = "com.android.calendar.CALL";
private static final String MAIL_ACTION = "com.android.calendar.MAIL";
private static final String EXTRA_EVENT_ID = "eventid";
// The broadcast for notification refreshes scheduled by the app. This is to
// distinguish the EVENT_REMINDER broadcast sent by the provider.
public static final String EVENT_REMINDER_APP_ACTION =
"com.android.calendar.EVENT_REMINDER_APP";
static final Object mStartingServiceSync = new Object();
static PowerManager.WakeLock mStartingService;
private static final Pattern mBlankLinePattern = Pattern.compile("^\\s*$[\n\r]",
Pattern.MULTILINE);
public static final String ACTION_DISMISS_OLD_REMINDERS = "removeOldReminders";
private static final int NOTIFICATION_DIGEST_MAX_LENGTH = 3;
private static final String GEO_PREFIX = "geo:";
private static final String TEL_PREFIX = "tel:";
private static final int MAX_NOTIF_ACTIONS = 3;
private static final String[] ATTENDEES_PROJECTION = new String[]{
Attendees.ATTENDEE_EMAIL, // 0
Attendees.ATTENDEE_STATUS, // 1
};
private static final int ATTENDEES_INDEX_EMAIL = 0;
private static final int ATTENDEES_INDEX_STATUS = 1;
private static final String ATTENDEES_WHERE = Attendees.EVENT_ID + "=?";
private static final String ATTENDEES_SORT_ORDER = Attendees.ATTENDEE_NAME + " ASC, "
+ Attendees.ATTENDEE_EMAIL + " ASC";
private static final String[] EVENT_PROJECTION = new String[]{
Calendars.OWNER_ACCOUNT, // 0
Calendars.ACCOUNT_NAME, // 1
Events.TITLE, // 2
Events.ORGANIZER, // 3
};
private static final int EVENT_INDEX_OWNER_ACCOUNT = 0;
private static final int EVENT_INDEX_ACCOUNT_NAME = 1;
private static final int EVENT_INDEX_TITLE = 2;
private static final int EVENT_INDEX_ORGANIZER = 3;
static PowerManager.WakeLock mStartingService;
private static Handler sAsyncHandler;
static {
HandlerThread thr = new HandlerThread("AlertReceiver async");
thr.start();
sAsyncHandler = new Handler(thr.getLooper());
}
@Override
public void onReceive(final Context context, final Intent intent) {
if (AlertService.DEBUG) {
Log.d(TAG, "onReceive: a=" + intent.getAction() + " " + intent.toString());
}
if (DELETE_ALL_ACTION.equals(intent.getAction())) {
// The user has dismissed a digest notification.
// TODO Grab a wake lock here?
Intent serviceIntent = new Intent(context, DismissAlarmsService.class);
context.startService(serviceIntent);
} else if (MAP_ACTION.equals(intent.getAction())) {
// Try starting the map action.
// If no map location is found (something changed since the notification was originally
// fired), update the notifications to express this change.
final long eventId = intent.getLongExtra(EXTRA_EVENT_ID, -1);
if (eventId != -1) {
URLSpan[] urlSpans = getURLSpans(context, eventId);
Intent geoIntent = createMapActivityIntent(context, urlSpans);
if (geoIntent != null) {
// Location was successfully found, so dismiss the shade and start maps.
context.startActivity(geoIntent);
closeNotificationShade(context);
} else {
// No location was found, so update all notifications.
// Our alert service does not currently allow us to specify only one
// specific notification to refresh.
AlertService.updateAlertNotification(context);
}
}
} else if (CALL_ACTION.equals(intent.getAction())) {
// Try starting the call action.
// If no call location is found (something changed since the notification was originally
// fired), update the notifications to express this change.
final long eventId = intent.getLongExtra(EXTRA_EVENT_ID, -1);
if (eventId != -1) {
URLSpan[] urlSpans = getURLSpans(context, eventId);
Intent callIntent = createCallActivityIntent(context, urlSpans);
if (callIntent != null) {
// Call location was successfully found, so dismiss the shade and start dialer.
context.startActivity(callIntent);
closeNotificationShade(context);
} else {
// No call location was found, so update all notifications.
// Our alert service does not currently allow us to specify only one
// specific notification to refresh.
AlertService.updateAlertNotification(context);
}
}
} else if (MAIL_ACTION.equals(intent.getAction())) {
closeNotificationShade(context);
// Now start the email intent.
final long eventId = intent.getLongExtra(EXTRA_EVENT_ID, -1);
if (eventId != -1) {
Intent i = new Intent(context, QuickResponseActivity.class);
i.putExtra(QuickResponseActivity.EXTRA_EVENT_ID, eventId);
i.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
context.startActivity(i);
}
} else {
Intent i = new Intent();
i.setClass(context, AlertService.class);
i.putExtras(intent);
i.putExtra("action", intent.getAction());
Uri uri = intent.getData();
// This intent might be a BOOT_COMPLETED so it might not have a Uri.
if (uri != null) {
i.putExtra("uri", uri.toString());
}
beginStartingService(context, i);
}
}
/**
* Start the service to process the current event notifications, acquiring
* the wake lock before returning to ensure that the service will run.
@ -595,32 +535,6 @@ public class AlertReceiver extends BroadcastReceiver {
return nw;
}
private void closeNotificationShade(Context context) {
Intent closeNotificationShadeIntent = new Intent(Intent.ACTION_CLOSE_SYSTEM_DIALOGS);
context.sendBroadcast(closeNotificationShadeIntent);
}
private static final String[] ATTENDEES_PROJECTION = new String[] {
Attendees.ATTENDEE_EMAIL, // 0
Attendees.ATTENDEE_STATUS, // 1
};
private static final int ATTENDEES_INDEX_EMAIL = 0;
private static final int ATTENDEES_INDEX_STATUS = 1;
private static final String ATTENDEES_WHERE = Attendees.EVENT_ID + "=?";
private static final String ATTENDEES_SORT_ORDER = Attendees.ATTENDEE_NAME + " ASC, "
+ Attendees.ATTENDEE_EMAIL + " ASC";
private static final String[] EVENT_PROJECTION = new String[] {
Calendars.OWNER_ACCOUNT, // 0
Calendars.ACCOUNT_NAME, // 1
Events.TITLE, // 2
Events.ORGANIZER, // 3
};
private static final int EVENT_INDEX_OWNER_ACCOUNT = 0;
private static final int EVENT_INDEX_ACCOUNT_NAME = 1;
private static final int EVENT_INDEX_TITLE = 2;
private static final int EVENT_INDEX_ORGANIZER = 3;
private static Cursor getEventCursor(Context context, long eventId) {
return context.getContentResolver().query(
ContentUris.withAppendedId(Events.CONTENT_URI, eventId), EVENT_PROJECTION,
@ -889,4 +803,84 @@ public class AlertReceiver extends BroadcastReceiver {
// No tel link was found, so return null;
return null;
}
@Override
public void onReceive(final Context context, final Intent intent) {
if (AlertService.DEBUG) {
Log.d(TAG, "onReceive: a=" + intent.getAction() + " " + intent.toString());
}
if (DELETE_ALL_ACTION.equals(intent.getAction())) {
// The user has dismissed a digest notification.
// TODO Grab a wake lock here?
Intent serviceIntent = new Intent(context, DismissAlarmsService.class);
context.startService(serviceIntent);
} else if (MAP_ACTION.equals(intent.getAction())) {
// Try starting the map action.
// If no map location is found (something changed since the notification was originally
// fired), update the notifications to express this change.
final long eventId = intent.getLongExtra(EXTRA_EVENT_ID, -1);
if (eventId != -1) {
URLSpan[] urlSpans = getURLSpans(context, eventId);
Intent geoIntent = createMapActivityIntent(context, urlSpans);
if (geoIntent != null) {
// Location was successfully found, so dismiss the shade and start maps.
context.startActivity(geoIntent);
closeNotificationShade(context);
} else {
// No location was found, so update all notifications.
// Our alert service does not currently allow us to specify only one
// specific notification to refresh.
AlertService.updateAlertNotification(context);
}
}
} else if (CALL_ACTION.equals(intent.getAction())) {
// Try starting the call action.
// If no call location is found (something changed since the notification was originally
// fired), update the notifications to express this change.
final long eventId = intent.getLongExtra(EXTRA_EVENT_ID, -1);
if (eventId != -1) {
URLSpan[] urlSpans = getURLSpans(context, eventId);
Intent callIntent = createCallActivityIntent(context, urlSpans);
if (callIntent != null) {
// Call location was successfully found, so dismiss the shade and start dialer.
context.startActivity(callIntent);
closeNotificationShade(context);
} else {
// No call location was found, so update all notifications.
// Our alert service does not currently allow us to specify only one
// specific notification to refresh.
AlertService.updateAlertNotification(context);
}
}
} else if (MAIL_ACTION.equals(intent.getAction())) {
closeNotificationShade(context);
// Now start the email intent.
final long eventId = intent.getLongExtra(EXTRA_EVENT_ID, -1);
if (eventId != -1) {
Intent i = new Intent(context, QuickResponseActivity.class);
i.putExtra(QuickResponseActivity.EXTRA_EVENT_ID, eventId);
i.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
context.startActivity(i);
}
} else {
Intent i = new Intent();
i.setClass(context, AlertService.class);
i.putExtras(intent);
i.putExtra("action", intent.getAction());
Uri uri = intent.getData();
// This intent might be a BOOT_COMPLETED so it might not have a Uri.
if (uri != null) {
i.putExtra("uri", uri.toString());
}
beginStartingService(context, i);
}
}
private void closeNotificationShade(Context context) {
Intent closeNotificationShadeIntent = new Intent(Intent.ACTION_CLOSE_SYSTEM_DIALOGS);
context.sendBroadcast(closeNotificationShadeIntent);
}
}

View File

@ -44,7 +44,6 @@ import android.util.Log;
import com.android.calendar.GeneralPreferences;
import com.android.calendar.OtherPreferences;
import org.sufficientlysecure.standalonecalendar.R;
import com.android.calendar.Utils;
import java.util.ArrayList;
@ -52,16 +51,15 @@ import java.util.HashMap;
import java.util.List;
import java.util.TimeZone;
import ws.xsoh.etar.R;
/**
* This service is used to handle calendar event reminders.
*/
public class AlertService extends Service {
// Hard limit to the number of notifications displayed.
public static final int MAX_NOTIFICATIONS = 20;
static final boolean DEBUG = true;
private static final String TAG = "AlertService";
private volatile Looper mServiceLooper;
private volatile ServiceHandler mServiceHandler;
static final String[] ALERT_PROJECTION = new String[] {
CalendarAlerts._ID, // 0
CalendarAlerts.EVENT_ID, // 1
@ -76,7 +74,7 @@ public class AlertService extends Service {
CalendarAlerts.END, // 10
CalendarAlerts.DESCRIPTION, // 11
};
private static final String TAG = "AlertService";
private static final int ALERT_INDEX_ID = 0;
private static final int ALERT_INDEX_EVENT_ID = 1;
private static final int ALERT_INDEX_STATE = 2;
@ -89,28 +87,18 @@ public class AlertService extends Service {
private static final int ALERT_INDEX_BEGIN = 9;
private static final int ALERT_INDEX_END = 10;
private static final int ALERT_INDEX_DESCRIPTION = 11;
private static final String ACTIVE_ALERTS_SELECTION = "(" + CalendarAlerts.STATE + "=? OR "
+ CalendarAlerts.STATE + "=?) AND " + CalendarAlerts.ALARM_TIME + "<=";
private static final String[] ACTIVE_ALERTS_SELECTION_ARGS = new String[] {
Integer.toString(CalendarAlerts.STATE_FIRED),
Integer.toString(CalendarAlerts.STATE_SCHEDULED)
};
private static final String ACTIVE_ALERTS_SORT = "begin DESC, end DESC";
private static final String DISMISS_OLD_SELECTION = CalendarAlerts.END + "<? AND "
+ CalendarAlerts.STATE + "=?";
private static final int MINUTE_MS = 60 * 1000;
// The grace period before changing a notification's priority bucket.
private static final int MIN_DEPRIORITIZE_GRACE_PERIOD_MS = 15 * MINUTE_MS;
// Hard limit to the number of notifications displayed.
public static final int MAX_NOTIFICATIONS = 20;
// Shared prefs key for storing whether the EVENT_REMINDER event from the provider
// was ever received. Some OEMs modified this provider broadcast, so we had to
// do the alarm scheduling here in the app, for the unbundled app's reminders to work.
@ -118,132 +106,23 @@ public class AlertService extends Service {
// alarm scheduling.
private static final String PROVIDER_REMINDER_PREF_KEY =
"preference_received_provider_reminder_broadcast";
private static final String SORT_ORDER_ALARMTIME_ASC =
CalendarContract.CalendarAlerts.ALARM_TIME + " ASC";
private static final String WHERE_RESCHEDULE_MISSED_ALARMS =
CalendarContract.CalendarAlerts.STATE
+ "="
+ CalendarContract.CalendarAlerts.STATE_SCHEDULED
+ " AND "
+ CalendarContract.CalendarAlerts.ALARM_TIME
+ "<?"
+ " AND "
+ CalendarContract.CalendarAlerts.ALARM_TIME
+ ">?"
+ " AND "
+ CalendarContract.CalendarAlerts.END + ">=?";
private static Boolean sReceivedProviderReminderBroadcast = null;
// Added wrapper for testing
public static class NotificationWrapper {
Notification mNotification;
long mEventId;
long mBegin;
long mEnd;
ArrayList<NotificationWrapper> mNw;
public NotificationWrapper(Notification n, int notificationId, long eventId,
long startMillis, long endMillis, boolean doPopup) {
mNotification = n;
mEventId = eventId;
mBegin = startMillis;
mEnd = endMillis;
// popup?
// notification id?
}
public NotificationWrapper(Notification n) {
mNotification = n;
}
public void add(NotificationWrapper nw) {
if (mNw == null) {
mNw = new ArrayList<NotificationWrapper>();
}
mNw.add(nw);
}
}
// Added wrapper for testing
public static class NotificationMgrWrapper extends NotificationMgr {
NotificationManager mNm;
public NotificationMgrWrapper(NotificationManager nm) {
mNm = nm;
}
@Override
public void cancel(int id) {
mNm.cancel(id);
}
@Override
public void notify(int id, NotificationWrapper nw) {
mNm.notify(id, nw.mNotification);
}
}
void processMessage(Message msg) {
Bundle bundle = (Bundle) msg.obj;
// On reboot, update the notification bar with the contents of the
// CalendarAlerts table.
String action = bundle.getString("action");
if (DEBUG) {
Log.d(TAG, bundle.getLong(android.provider.CalendarContract.CalendarAlerts.ALARM_TIME)
+ " Action = " + action);
}
// Some OEMs had changed the provider's EVENT_REMINDER broadcast to their own event,
// which broke our unbundled app's reminders. So we added backup alarm scheduling to the
// app, but we know we can turn it off if we ever receive the EVENT_REMINDER broadcast.
boolean providerReminder = action.equals(
android.provider.CalendarContract.ACTION_EVENT_REMINDER);
if (providerReminder) {
if (sReceivedProviderReminderBroadcast == null) {
sReceivedProviderReminderBroadcast = Utils.getSharedPreference(this,
PROVIDER_REMINDER_PREF_KEY, false);
}
if (!sReceivedProviderReminderBroadcast) {
sReceivedProviderReminderBroadcast = true;
Log.d(TAG, "Setting key " + PROVIDER_REMINDER_PREF_KEY + " to: true");
Utils.setSharedPreference(this, PROVIDER_REMINDER_PREF_KEY, true);
}
}
if (providerReminder ||
action.equals(Intent.ACTION_PROVIDER_CHANGED) ||
action.equals(android.provider.CalendarContract.ACTION_EVENT_REMINDER) ||
action.equals(AlertReceiver.EVENT_REMINDER_APP_ACTION) ||
action.equals(Intent.ACTION_LOCALE_CHANGED)) {
// b/7652098: Add a delay after the provider-changed event before refreshing
// notifications to help issue with the unbundled app installed on HTC having
// stale notifications.
if (action.equals(Intent.ACTION_PROVIDER_CHANGED)) {
try {
Thread.sleep(5000);
} catch (Exception e) {
// Ignore.
}
}
updateAlertNotification(this);
} else if (action.equals(Intent.ACTION_BOOT_COMPLETED)) {
// The provider usually initiates this setting up of alarms on startup,
// but there was a bug (b/7221716) where a race condition caused this step to be
// skipped, resulting in missed alarms. This is a stopgap to minimize this bug
// for devices that don't have the provider fix, by initiating this a 2nd time here.
// However, it would still theoretically be possible to hit the race condition
// the 2nd time and still miss alarms.
//
// TODO: Remove this when the provider fix is rolled out everywhere.
Intent intent = new Intent();
intent.setClass(this, InitAlarmsService.class);
startService(intent);
} else if (action.equals(Intent.ACTION_TIME_CHANGED)) {
doTimeChanged();
} else if (action.equals(AlertReceiver.ACTION_DISMISS_OLD_REMINDERS)) {
dismissOldAlerts(this);
} else {
Log.w(TAG, "Invalid action: " + action);
}
// Schedule the alarm for the next upcoming reminder, if not done by the provider.
if (sReceivedProviderReminderBroadcast == null || !sReceivedProviderReminderBroadcast) {
Log.d(TAG, "Scheduling next alarm with AlarmScheduler. "
+ "sEventReminderReceived: " + sReceivedProviderReminderBroadcast);
AlarmScheduler.scheduleNextAlarm(this);
}
}
private volatile Looper mServiceLooper;
private volatile ServiceHandler mServiceHandler;
static void dismissOldAlerts(Context context) {
ContentResolver cr = context.getContentResolver();
@ -895,29 +774,6 @@ public class AlertService extends Service {
return tickerText;
}
static class NotificationInfo {
String eventName;
String location;
String description;
long startMillis;
long endMillis;
long eventId;
boolean allDay;
boolean newAlert;
NotificationInfo(String eventName, String location, String description, long startMillis,
long endMillis, long eventId, boolean allDay, boolean newAlert) {
this.eventName = eventName;
this.location = location;
this.description = description;
this.startMillis = startMillis;
this.endMillis = endMillis;
this.eventId = eventId;
this.newAlert = newAlert;
this.allDay = allDay;
}
}
private static void addNotificationOptions(NotificationWrapper nw, boolean quietUpdate,
String tickerText, boolean defaultVibrate, String reminderRingtone,
boolean showLights) {
@ -950,79 +806,6 @@ public class AlertService extends Service {
}
}
/* package */ static class NotificationPrefs {
boolean quietUpdate;
private Context context;
private SharedPreferences prefs;
// These are lazily initialized, do not access any of the following directly; use getters.
private int doPopup = -1;
private int defaultVibrate = -1;
private String ringtone = null;
private static final String EMPTY_RINGTONE = "";
NotificationPrefs(Context context, SharedPreferences prefs, boolean quietUpdate) {
this.context = context;
this.prefs = prefs;
this.quietUpdate = quietUpdate;
}
private boolean getDoPopup() {
if (doPopup < 0) {
if (prefs.getBoolean(GeneralPreferences.KEY_ALERTS_POPUP, false)) {
doPopup = 1;
} else {
doPopup = 0;
}
}
return doPopup == 1;
}
private boolean getDefaultVibrate() {
if (defaultVibrate < 0) {
defaultVibrate = Utils.getDefaultVibrate(context, prefs) ? 1 : 0;
}
return defaultVibrate == 1;
}
private String getRingtoneAndSilence() {
if (ringtone == null) {
if (quietUpdate) {
ringtone = EMPTY_RINGTONE;
} else {
ringtone = Utils.getRingTonePreference(context);
}
}
String retVal = ringtone;
ringtone = EMPTY_RINGTONE;
return retVal;
}
}
private void doTimeChanged() {
ContentResolver cr = getContentResolver();
// TODO Move this into Provider
rescheduleMissedAlarms(cr, this, AlertUtils.createAlarmManager(this));
updateAlertNotification(this);
}
private static final String SORT_ORDER_ALARMTIME_ASC =
CalendarContract.CalendarAlerts.ALARM_TIME + " ASC";
private static final String WHERE_RESCHEDULE_MISSED_ALARMS =
CalendarContract.CalendarAlerts.STATE
+ "="
+ CalendarContract.CalendarAlerts.STATE_SCHEDULED
+ " AND "
+ CalendarContract.CalendarAlerts.ALARM_TIME
+ "<?"
+ " AND "
+ CalendarContract.CalendarAlerts.ALARM_TIME
+ ">?"
+ " AND "
+ CalendarContract.CalendarAlerts.END + ">=?";
/**
* Searches the CalendarAlerts table for alarms that should have fired but
* have not and then reschedules them. This method can be called at boot
@ -1074,18 +857,86 @@ public class AlertService extends Service {
}
}
private final class ServiceHandler extends Handler {
public ServiceHandler(Looper looper) {
super(looper);
void processMessage(Message msg) {
Bundle bundle = (Bundle) msg.obj;
// On reboot, update the notification bar with the contents of the
// CalendarAlerts table.
String action = bundle.getString("action");
if (DEBUG) {
Log.d(TAG, bundle.getLong(android.provider.CalendarContract.CalendarAlerts.ALARM_TIME)
+ " Action = " + action);
}
@Override
public void handleMessage(Message msg) {
processMessage(msg);
// NOTE: We MUST not call stopSelf() directly, since we need to
// make sure the wake lock acquired by AlertReceiver is released.
AlertReceiver.finishStartingService(AlertService.this, msg.arg1);
// Some OEMs had changed the provider's EVENT_REMINDER broadcast to their own event,
// which broke our unbundled app's reminders. So we added backup alarm scheduling to the
// app, but we know we can turn it off if we ever receive the EVENT_REMINDER broadcast.
boolean providerReminder = action.equals(
android.provider.CalendarContract.ACTION_EVENT_REMINDER);
if (providerReminder) {
if (sReceivedProviderReminderBroadcast == null) {
sReceivedProviderReminderBroadcast = Utils.getSharedPreference(this,
PROVIDER_REMINDER_PREF_KEY, false);
}
if (!sReceivedProviderReminderBroadcast) {
sReceivedProviderReminderBroadcast = true;
Log.d(TAG, "Setting key " + PROVIDER_REMINDER_PREF_KEY + " to: true");
Utils.setSharedPreference(this, PROVIDER_REMINDER_PREF_KEY, true);
}
}
if (providerReminder ||
action.equals(Intent.ACTION_PROVIDER_CHANGED) ||
action.equals(android.provider.CalendarContract.ACTION_EVENT_REMINDER) ||
action.equals(AlertReceiver.EVENT_REMINDER_APP_ACTION) ||
action.equals(Intent.ACTION_LOCALE_CHANGED)) {
// b/7652098: Add a delay after the provider-changed event before refreshing
// notifications to help issue with the unbundled app installed on HTC having
// stale notifications.
if (action.equals(Intent.ACTION_PROVIDER_CHANGED)) {
try {
Thread.sleep(5000);
} catch (Exception e) {
// Ignore.
}
}
updateAlertNotification(this);
} else if (action.equals(Intent.ACTION_BOOT_COMPLETED)) {
// The provider usually initiates this setting up of alarms on startup,
// but there was a bug (b/7221716) where a race condition caused this step to be
// skipped, resulting in missed alarms. This is a stopgap to minimize this bug
// for devices that don't have the provider fix, by initiating this a 2nd time here.
// However, it would still theoretically be possible to hit the race condition
// the 2nd time and still miss alarms.
//
// TODO: Remove this when the provider fix is rolled out everywhere.
Intent intent = new Intent();
intent.setClass(this, InitAlarmsService.class);
startService(intent);
} else if (action.equals(Intent.ACTION_TIME_CHANGED)) {
doTimeChanged();
} else if (action.equals(AlertReceiver.ACTION_DISMISS_OLD_REMINDERS)) {
dismissOldAlerts(this);
} else {
Log.w(TAG, "Invalid action: " + action);
}
// Schedule the alarm for the next upcoming reminder, if not done by the provider.
if (sReceivedProviderReminderBroadcast == null || !sReceivedProviderReminderBroadcast) {
Log.d(TAG, "Scheduling next alarm with AlarmScheduler. "
+ "sEventReminderReceived: " + sReceivedProviderReminderBroadcast);
AlarmScheduler.scheduleNextAlarm(this);
}
}
private void doTimeChanged() {
ContentResolver cr = getContentResolver();
// TODO Move this into Provider
rescheduleMissedAlarms(cr, this, AlertUtils.createAlarmManager(this));
updateAlertNotification(this);
}
@Override
@ -1121,4 +972,139 @@ public class AlertService extends Service {
public IBinder onBind(Intent intent) {
return null;
}
// Added wrapper for testing
public static class NotificationWrapper {
Notification mNotification;
long mEventId;
long mBegin;
long mEnd;
ArrayList<NotificationWrapper> mNw;
public NotificationWrapper(Notification n, int notificationId, long eventId,
long startMillis, long endMillis, boolean doPopup) {
mNotification = n;
mEventId = eventId;
mBegin = startMillis;
mEnd = endMillis;
// popup?
// notification id?
}
public NotificationWrapper(Notification n) {
mNotification = n;
}
public void add(NotificationWrapper nw) {
if (mNw == null) {
mNw = new ArrayList<NotificationWrapper>();
}
mNw.add(nw);
}
}
// Added wrapper for testing
public static class NotificationMgrWrapper extends NotificationMgr {
NotificationManager mNm;
public NotificationMgrWrapper(NotificationManager nm) {
mNm = nm;
}
@Override
public void cancel(int id) {
mNm.cancel(id);
}
@Override
public void notify(int id, NotificationWrapper nw) {
mNm.notify(id, nw.mNotification);
}
}
static class NotificationInfo {
String eventName;
String location;
String description;
long startMillis;
long endMillis;
long eventId;
boolean allDay;
boolean newAlert;
NotificationInfo(String eventName, String location, String description, long startMillis,
long endMillis, long eventId, boolean allDay, boolean newAlert) {
this.eventName = eventName;
this.location = location;
this.description = description;
this.startMillis = startMillis;
this.endMillis = endMillis;
this.eventId = eventId;
this.newAlert = newAlert;
this.allDay = allDay;
}
}
/* package */ static class NotificationPrefs {
private static final String EMPTY_RINGTONE = "";
boolean quietUpdate;
private Context context;
private SharedPreferences prefs;
// These are lazily initialized, do not access any of the following directly; use getters.
private int doPopup = -1;
private int defaultVibrate = -1;
private String ringtone = null;
NotificationPrefs(Context context, SharedPreferences prefs, boolean quietUpdate) {
this.context = context;
this.prefs = prefs;
this.quietUpdate = quietUpdate;
}
private boolean getDoPopup() {
if (doPopup < 0) {
if (prefs.getBoolean(GeneralPreferences.KEY_ALERTS_POPUP, false)) {
doPopup = 1;
} else {
doPopup = 0;
}
}
return doPopup == 1;
}
private boolean getDefaultVibrate() {
if (defaultVibrate < 0) {
defaultVibrate = Utils.getDefaultVibrate(context, prefs) ? 1 : 0;
}
return defaultVibrate == 1;
}
private String getRingtoneAndSilence() {
if (ringtone == null) {
if (quietUpdate) {
ringtone = EMPTY_RINGTONE;
} else {
ringtone = Utils.getRingTonePreference(context);
}
}
String retVal = ringtone;
ringtone = EMPTY_RINGTONE;
return retVal;
}
}
private final class ServiceHandler extends Handler {
public ServiceHandler(Looper looper) {
super(looper);
}
@Override
public void handleMessage(Message msg) {
processMessage(msg);
// NOTE: We MUST not call stopSelf() directly, since we need to
// make sure the wake lock acquired by AlertReceiver is released.
AlertReceiver.finishStartingService(AlertService.this, msg.arg1);
}
}
}

View File

@ -33,21 +33,18 @@ import android.text.format.Time;
import android.util.Log;
import com.android.calendar.EventInfoActivity;
import org.sufficientlysecure.standalonecalendar.R;
import com.android.calendar.Utils;
import java.util.Locale;
import java.util.Map;
import java.util.TimeZone;
public class AlertUtils {
private static final String TAG = "AlertUtils";
static final boolean DEBUG = true;
import ws.xsoh.etar.R;
public class AlertUtils {
// We use one notification id for the expired events notification. All
// other notifications (the 'active' future/concurrent ones) use a unique ID.
public static final int EXPIRED_GROUP_NOTIFICATION_ID = 0;
public static final String EVENT_ID_KEY = "eventid";
public static final String SHOW_EVENT_KEY = "showevent";
public static final String EVENT_START_KEY = "eventstart";
@ -56,28 +53,25 @@ public class AlertUtils {
public static final String EVENT_IDS_KEY = "eventids";
public static final String SNOOZE_DELAY_KEY = "snoozedelay";
public static final String EVENT_STARTS_KEY = "starts";
// A flag for using local storage to save alert state instead of the alerts DB table.
// This allows the unbundled app to run alongside other calendar apps without eating
// alerts from other apps.
static boolean BYPASS_DB = true;
static final boolean DEBUG = true;
private static final String TAG = "AlertUtils";
// SharedPrefs table name for storing fired alerts. This prevents other installed
// Calendar apps from eating the alerts.
private static final String ALERTS_SHARED_PREFS_NAME = "calendar_alerts";
// Keyname prefix for the alerts data in SharedPrefs. The key will contain a combo
// of event ID, begin time, and alarm time. The value will be the fired time.
private static final String KEY_FIRED_ALERT_PREFIX = "preference_alert_";
// The last time the SharedPrefs was scanned and flushed of old alerts data.
private static final String KEY_LAST_FLUSH_TIME_MS = "preference_flushTimeMs";
// The # of days to save alert states in the shared prefs table, before flushing. This
// can be any value, since AlertService will also check for a recent alertTime before
// ringing the alert.
private static final int FLUSH_INTERVAL_DAYS = 1;
private static final int FLUSH_INTERVAL_MS = FLUSH_INTERVAL_DAYS * 24 * 60 * 60 * 1000;
// A flag for using local storage to save alert state instead of the alerts DB table.
// This allows the unbundled app to run alongside other calendar apps without eating
// alerts from other apps.
static boolean BYPASS_DB = true;
/**
* Creates an AlarmManagerInterface that wraps a real AlarmManager. The alarm code

View File

@ -34,7 +34,6 @@ import android.util.Pair;
import com.android.calendar.CloudNotificationBackplane;
import com.android.calendar.ExtensionsFactory;
import org.sufficientlysecure.standalonecalendar.R;
import java.io.IOException;
import java.util.HashMap;
@ -44,16 +43,18 @@ import java.util.List;
import java.util.Map;
import java.util.Set;
import ws.xsoh.etar.R;
/**
* Utilities for managing notification dismissal across devices.
*/
public class GlobalDismissManager extends BroadcastReceiver {
private static final String TAG = "GlobalDismissManager";
private static final String GOOGLE_ACCOUNT_TYPE = "com.google";
private static final String GLOBAL_DISMISS_MANAGER_PREFS = "com.android.calendar.alerts.GDM";
private static final String ACCOUNT_KEY = "known_accounts";
public static final String KEY_PREFIX = "com.android.calendar.alerts.";
public static final String SYNC_ID = KEY_PREFIX + "sync_id";
public static final String START_TIME = KEY_PREFIX + "start_time";
public static final String ACCOUNT_NAME = KEY_PREFIX + "account_name";
public static final String DISMISS_INTENT = KEY_PREFIX + "DISMISS";
protected static final long FOUR_WEEKS = 60 * 60 * 24 * 7 * 4;
static final String[] EVENT_PROJECTION = new String[] {
Events._ID,
Events.CALENDAR_ID
@ -67,26 +68,15 @@ public class GlobalDismissManager extends BroadcastReceiver {
Calendars.ACCOUNT_NAME,
Calendars.ACCOUNT_TYPE
};
public static final String KEY_PREFIX = "com.android.calendar.alerts.";
public static final String SYNC_ID = KEY_PREFIX + "sync_id";
public static final String START_TIME = KEY_PREFIX + "start_time";
public static final String ACCOUNT_NAME = KEY_PREFIX + "account_name";
public static final String DISMISS_INTENT = KEY_PREFIX + "DISMISS";
public static class AlarmId {
public long mEventId;
public long mStart;
public AlarmId(long id, long start) {
mEventId = id;
mStart = start;
}
}
private static final String TAG = "GlobalDismissManager";
private static final String GOOGLE_ACCOUNT_TYPE = "com.google";
private static final String GLOBAL_DISMISS_MANAGER_PREFS = "com.android.calendar.alerts.GDM";
private static final String ACCOUNT_KEY = "known_accounts";
/**
* Look for unknown accounts in a set of events and associate with them.
* Returns immediately, processing happens in the background.
*
*
* @param context application context
* @param eventIds IDs for events that have posted notifications that may be
* dismissed.
@ -156,7 +146,7 @@ public class GlobalDismissManager extends BroadcastReceiver {
/**
* Globally dismiss notifications that are backed by the same events.
*
*
* @param context application context
* @param alarmIds Unique identifiers for events that have been dismissed by the user.
* @return true if notification_sender_id is available
@ -254,7 +244,7 @@ public class GlobalDismissManager extends BroadcastReceiver {
/**
* build a selection over a set of row IDs
*
*
* @param ids row IDs to select
* @param key row name for the table
* @return a selection string suitable for a resolver query.
@ -382,4 +372,14 @@ public class GlobalDismissManager extends BroadcastReceiver {
}
}.execute(new Pair<Context, Intent>(context, intent));
}
public static class AlarmId {
public long mEventId;
public long mStart;
public AlarmId(long id, long start) {
mEventId = id;
mStart = start;
}
}
}

View File

@ -26,11 +26,12 @@ import android.widget.AdapterView.OnItemClickListener;
import android.widget.ArrayAdapter;
import android.widget.Toast;
import org.sufficientlysecure.standalonecalendar.R;
import com.android.calendar.Utils;
import java.util.Arrays;
import ws.xsoh.etar.R;
/**
* Activity which displays when the user wants to email guests from notifications.
*
@ -39,11 +40,10 @@ import java.util.Arrays;
*
*/
public class QuickResponseActivity extends ListActivity implements OnItemClickListener {
private static final String TAG = "QuickResponseActivity";
public static final String EXTRA_EVENT_ID = "eventId";
private String[] mResponses = null;
private static final String TAG = "QuickResponseActivity";
static long mEventId;
private String[] mResponses = null;
@Override
protected void onCreate(Bundle icicle) {

View File

@ -23,9 +23,10 @@ import android.content.DialogInterface;
import android.content.Intent;
import android.widget.TimePicker;
import org.sufficientlysecure.standalonecalendar.R;
import com.android.calendar.Utils;
import ws.xsoh.etar.R;
public class SnoozeDelayActivity extends Activity implements
TimePickerDialog.OnTimeSetListener, DialogInterface.OnCancelListener {
private static final int DIALOG_DELAY = 1;

View File

@ -16,44 +16,45 @@
package com.android.calendar.event;
import com.android.calendar.CalendarEventModel.Attendee;
import com.android.calendar.ContactsAsyncHelper;
import org.sufficientlysecure.standalonecalendar.R;
import com.android.calendar.Utils;
import com.android.calendar.event.EditEventHelper.AttendeeItem;
import com.android.common.Rfc822Validator;
import android.content.AsyncQueryHandler;
import android.content.ContentResolver;
import android.content.ContentUris;
import android.content.Context;
import android.content.res.Resources;
import android.database.Cursor;
import android.graphics.ColorMatrix;
import android.graphics.ColorMatrixColorFilter;
import android.graphics.Paint;
import android.graphics.drawable.Drawable;
import android.net.Uri;
import android.provider.CalendarContract.Attendees;
import android.provider.ContactsContract.CommonDataKinds.Email;
import android.provider.ContactsContract.CommonDataKinds.Identity;
import android.provider.ContactsContract.Contacts;
import android.provider.ContactsContract.Data;
import android.provider.ContactsContract.RawContacts;
import android.text.TextUtils;
import android.text.util.Rfc822Token;
import android.util.AttributeSet;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.ImageButton;
import android.widget.LinearLayout;
import android.widget.QuickContactBadge;
import android.widget.TextView;
import android.content.AsyncQueryHandler;
import android.content.ContentResolver;
import android.content.ContentUris;
import android.content.Context;
import android.content.res.Resources;
import android.database.Cursor;
import android.graphics.ColorMatrix;
import android.graphics.ColorMatrixColorFilter;
import android.graphics.Paint;
import android.graphics.drawable.Drawable;
import android.net.Uri;
import android.provider.CalendarContract.Attendees;
import android.provider.ContactsContract.CommonDataKinds.Email;
import android.provider.ContactsContract.CommonDataKinds.Identity;
import android.provider.ContactsContract.Contacts;
import android.provider.ContactsContract.Data;
import android.provider.ContactsContract.RawContacts;
import android.text.TextUtils;
import android.text.util.Rfc822Token;
import android.util.AttributeSet;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.ImageButton;
import android.widget.LinearLayout;
import android.widget.QuickContactBadge;
import android.widget.TextView;
import com.android.calendar.CalendarEventModel.Attendee;
import com.android.calendar.ContactsAsyncHelper;
import com.android.calendar.Utils;
import com.android.calendar.event.EditEventHelper.AttendeeItem;
import com.android.common.Rfc822Validator;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedHashSet;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedHashSet;
import ws.xsoh.etar.R;
public class AttendeesView extends LinearLayout implements View.OnClickListener {
private static final String TAG = "AttendeesView";
@ -88,17 +89,15 @@ public class AttendeesView extends LinearLayout implements View.OnClickListener
private final View mDividerForNoResponse;
private final int mNoResponsePhotoAlpha;
private final int mDefaultPhotoAlpha;
// Cache for loaded photos
HashMap<String, Drawable> mRecycledPhotos;
private Rfc822Validator mValidator;
// Number of attendees responding or not responding.
private int mYes;
private int mNo;
private int mMaybe;
private int mNoResponse;
// Cache for loaded photos
HashMap<String, Drawable> mRecycledPhotos;
public AttendeesView(Context context, AttributeSet attrs) {
super(context, attrs);
mContext = context;
@ -408,6 +407,22 @@ public class AttendeesView extends LinearLayout implements View.OnClickListener
return ((AttendeeItem) view.getTag()).mRemoved;
}
public Attendee getItem(int index) {
final View view = getChildAt(index);
if (view instanceof TextView) { // divider
return null;
}
return ((AttendeeItem) view.getTag()).mAttendee;
}
@Override
public void onClick(View view) {
// Button corresponding to R.id.contact_remove.
final AttendeeItem item = (AttendeeItem) view.getTag();
item.mRemoved = !item.mRemoved;
updateAttendeeView(item);
}
// TODO put this into a Loader for auto-requeries
private class PresenceQueryHandler extends AsyncQueryHandler {
public PresenceQueryHandler(ContentResolver cr) {
@ -467,20 +482,4 @@ public class AttendeesView extends LinearLayout implements View.OnClickListener
}
}
}
public Attendee getItem(int index) {
final View view = getChildAt(index);
if (view instanceof TextView) { // divider
return null;
}
return ((AttendeeItem) view.getTag()).mAttendee;
}
@Override
public void onClick(View view) {
// Button corresponding to R.id.contact_remove.
final AttendeeItem item = (AttendeeItem) view.getTag();
item.mRemoved = !item.mRemoved;
updateAttendeeView(item);
}
}

View File

@ -44,9 +44,10 @@ import com.android.calendar.CalendarController;
import com.android.calendar.CalendarController.EventType;
import com.android.calendar.CalendarEventModel;
import com.android.calendar.GeneralPreferences;
import org.sufficientlysecure.standalonecalendar.R;
import com.android.calendar.Utils;
import ws.xsoh.etar.R;
/**
* Allows the user to quickly create a new all-day event from the calendar's month view.
@ -83,24 +84,6 @@ public class CreateEventDialogFragment extends DialogFragment implements TextWat
private long mCalendarId = -1;
private String mCalendarOwner;
private class CalendarQueryService extends AsyncQueryService {
/**
* @param context
*/
public CalendarQueryService(Context context) {
super(context);
}
@Override
public void onQueryComplete(int token, Object cookie, Cursor cursor) {
setDefaultCalendarView(cursor);
if (cursor != null) {
cursor.close();
}
}
}
public CreateEventDialogFragment() {
// Empty constructor required for DialogFragment.
}
@ -322,4 +305,22 @@ public class CreateEventDialogFragment extends DialogFragment implements TextWat
mAccountName.setText(accountName);
}
}
private class CalendarQueryService extends AsyncQueryService {
/**
* @param context
*/
public CalendarQueryService(Context context) {
super(context);
}
@Override
public void onQueryComplete(int token, Object cookie, Cursor cursor) {
setDefaultCalendarView(cursor);
if (cursor != null) {
cursor.close();
}
}
}
}

View File

@ -32,10 +32,10 @@ import com.android.calendar.CalendarController.EventInfo;
import com.android.calendar.CalendarEventModel.ReminderEntry;
import com.android.calendar.Utils;
import org.sufficientlysecure.standalonecalendar.R;
import java.util.ArrayList;
import ws.xsoh.etar.R;
import static android.provider.CalendarContract.EXTRA_EVENT_ALL_DAY;
import static android.provider.CalendarContract.EXTRA_EVENT_BEGIN_TIME;
import static android.provider.CalendarContract.EXTRA_EVENT_END_TIME;

View File

@ -66,12 +66,12 @@ import com.android.calendar.Utils;
import com.android.colorpicker.ColorPickerSwatch.OnColorSelectedListener;
import com.android.colorpicker.HsvColorComparator;
import org.sufficientlysecure.standalonecalendar.R;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collections;
import ws.xsoh.etar.R;
public class EditEventFragment extends Fragment implements EventHandler, OnColorSelectedListener {
private static final String TAG = "EditEventActivity";
private static final String COLOR_PICKER_DIALOG_TAG = "ColorPickerDialog";

View File

@ -74,7 +74,6 @@ import com.android.calendar.EmailAddressAdapter;
import com.android.calendar.EventInfoFragment;
import com.android.calendar.EventRecurrenceFormatter;
import com.android.calendar.GeneralPreferences;
import org.sufficientlysecure.standalonecalendar.R;
import com.android.calendar.RecipientAdapter;
import com.android.calendar.Utils;
import com.android.calendar.event.EditEventHelper.EditDoneRunnable;
@ -102,6 +101,8 @@ import java.util.HashMap;
import java.util.Locale;
import java.util.TimeZone;
import ws.xsoh.etar.R;
public class EditEventView implements View.OnClickListener, DialogInterface.OnCancelListener,
DialogInterface.OnClickListener, OnItemSelectedListener,
RecurrencePickerDialog.OnRecurrenceSetListener,
@ -115,7 +116,18 @@ public class EditEventView implements View.OnClickListener, DialogInterface.OnCa
private static final String FRAG_TAG_TIME_PICKER = "timePickerDialogFragment";
private static final String FRAG_TAG_TIME_ZONE_PICKER = "timeZonePickerDialogFragment";
private static final String FRAG_TAG_RECUR_PICKER = "recurrencePickerDialogFragment";
private static StringBuilder mSB = new StringBuilder(50);
private static Formatter mF = new Formatter(mSB, Locale.getDefault());
/**
* From com.google.android.gm.ComposeActivity Implements special address
* cleanup rules: The first space key entry following an "@" symbol that is
* followed by any combination of letters and symbols, including one+ dots
* and zero commas, should insert an extra comma (followed by the space).
*/
private static InputFilter[] sRecipientFilters = new InputFilter[]{new Rfc822InputFilter()};
public boolean mIsMultipane;
public boolean mTimeSelectedWasStartTime;
public boolean mDateSelectedWasStartDate;
ArrayList<View> mEditOnlyList = new ArrayList<View>();
ArrayList<View> mEditViewList = new ArrayList<View>();
ArrayList<View> mViewOnlyList = new ArrayList<View>();
@ -160,10 +172,7 @@ public class EditEventView implements View.OnClickListener, DialogInterface.OnCa
View mAttendeesGroup;
View mStartHomeGroup;
View mEndHomeGroup;
private int[] mOriginalPadding = new int[4];
public boolean mIsMultipane;
private ProgressDialog mLoadingCalendarsDialog;
private AlertDialog mNoCalendarsDialog;
private DialogFragment mTimezoneDialog;
@ -174,20 +183,15 @@ public class EditEventView implements View.OnClickListener, DialogInterface.OnCa
private Cursor mCalendarsCursor;
private AccountSpecifier mAddressAdapter;
private Rfc822Validator mEmailValidator;
public boolean mTimeSelectedWasStartTime;
public boolean mDateSelectedWasStartDate;
private TimePickerDialog mStartTimePickerDialog;
private TimePickerDialog mEndTimePickerDialog;
private DatePickerDialog mDatePickerDialog;
/**
* Contents of the "minutes" spinner. This has default values from the XML file, augmented
* with any additional values that were already associated with the event.
*/
private ArrayList<Integer> mReminderMinuteValues;
private ArrayList<String> mReminderMinuteLabels;
/**
* Contents of the "methods" spinner. The "values" list specifies the method constant
* (e.g. {@link Reminders#METHOD_ALERT}) associated with the labels. Any methods that
@ -195,7 +199,6 @@ public class EditEventView implements View.OnClickListener, DialogInterface.OnCa
*/
private ArrayList<Integer> mReminderMethodValues;
private ArrayList<String> mReminderMethodLabels;
/**
* Contents of the "availability" spinner. The "values" list specifies the
* type constant (e.g. {@link Events#AVAILABILITY_BUSY}) associated with the
@ -208,189 +211,216 @@ public class EditEventView implements View.OnClickListener, DialogInterface.OnCa
private boolean mAvailabilityExplicitlySet;
private boolean mAllDayChangingAvailability;
private int mAvailabilityCurrentlySelected;
private int mDefaultReminderMinutes;
private boolean mSaveAfterQueryComplete = false;
private TimeZonePickerUtils mTzPickerUtils;
private Time mStartTime;
private Time mEndTime;
private String mTimezone;
private boolean mAllDay = false;
private int mModification = EditEventHelper.MODIFY_UNINITIALIZED;
private EventRecurrence mEventRecurrence = new EventRecurrence();
private ArrayList<LinearLayout> mReminderItems = new ArrayList<LinearLayout>(0);
private ArrayList<ReminderEntry> mUnsupportedReminders = new ArrayList<ReminderEntry>();
private String mRrule;
private static StringBuilder mSB = new StringBuilder(50);
private static Formatter mF = new Formatter(mSB, Locale.getDefault());
public EditEventView(Activity activity, View view, EditDoneRunnable done,
boolean timeSelectedWasStartTime, boolean dateSelectedWasStartDate) {
/* This class is used to update the time buttons. */
private class TimeListener implements OnTimeSetListener {
private View mView;
mActivity = activity;
mView = view;
mDone = done;
public TimeListener(View view) {
mView = view;
}
// cache top level view elements
mLoadingMessage = (TextView) view.findViewById(R.id.loading_message);
mScrollView = (ScrollView) view.findViewById(R.id.scroll_view);
@Override
public void onTimeSet(RadialPickerLayout view, int hourOfDay, int minute) {
// Cache the member variables locally to avoid inner class overhead.
Time startTime = mStartTime;
Time endTime = mEndTime;
// cache all the widgets
mCalendarsSpinner = (Spinner) view.findViewById(R.id.calendars_spinner);
mTitleTextView = (TextView) view.findViewById(R.id.title);
mLocationTextView = (AutoCompleteTextView) view.findViewById(R.id.location);
mDescriptionTextView = (TextView) view.findViewById(R.id.description);
mTimezoneLabel = (TextView) view.findViewById(R.id.timezone_label);
mStartDateButton = (Button) view.findViewById(R.id.start_date);
mEndDateButton = (Button) view.findViewById(R.id.end_date);
mWhenView = (TextView) mView.findViewById(R.id.when);
mTimezoneTextView = (TextView) mView.findViewById(R.id.timezone_textView);
mStartTimeButton = (Button) view.findViewById(R.id.start_time);
mEndTimeButton = (Button) view.findViewById(R.id.end_time);
mTimezoneButton = (Button) view.findViewById(R.id.timezone_button);
mTimezoneButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
showTimezoneDialog();
}
});
mTimezoneRow = view.findViewById(R.id.timezone_button_row);
mStartTimeHome = (TextView) view.findViewById(R.id.start_time_home_tz);
mStartDateHome = (TextView) view.findViewById(R.id.start_date_home_tz);
mEndTimeHome = (TextView) view.findViewById(R.id.end_time_home_tz);
mEndDateHome = (TextView) view.findViewById(R.id.end_date_home_tz);
mAllDayCheckBox = (CheckBox) view.findViewById(R.id.is_all_day);
mRruleButton = (Button) view.findViewById(R.id.rrule);
mAvailabilitySpinner = (Spinner) view.findViewById(R.id.availability);
mAccessLevelSpinner = (Spinner) view.findViewById(R.id.visibility);
mCalendarSelectorGroup = view.findViewById(R.id.calendar_selector_group);
mCalendarSelectorWrapper = view.findViewById(R.id.calendar_selector_wrapper);
mCalendarStaticGroup = view.findViewById(R.id.calendar_group);
mRemindersGroup = view.findViewById(R.id.reminders_row);
mResponseGroup = view.findViewById(R.id.response_row);
mOrganizerGroup = view.findViewById(R.id.organizer_row);
mAttendeesGroup = view.findViewById(R.id.add_attendees_row);
mLocationGroup = view.findViewById(R.id.where_row);
mDescriptionGroup = view.findViewById(R.id.description_row);
mStartHomeGroup = view.findViewById(R.id.from_row_home_tz);
mEndHomeGroup = view.findViewById(R.id.to_row_home_tz);
mAttendeesList = (MultiAutoCompleteTextView) view.findViewById(R.id.attendees);
// Cache the start and end millis so that we limit the number
// of calls to normalize() and toMillis(), which are fairly
// expensive.
long startMillis;
long endMillis;
if (mView == mStartTimeButton) {
// The start time was changed.
int hourDuration = endTime.hour - startTime.hour;
int minuteDuration = endTime.minute - startTime.minute;
mColorPickerNewEvent = view.findViewById(R.id.change_color_new_event);
mColorPickerExistingEvent = view.findViewById(R.id.change_color_existing_event);
startTime.hour = hourOfDay;
startTime.minute = minute;
startMillis = startTime.normalize(true);
// Also update the end time to keep the duration constant.
endTime.hour = hourOfDay + hourDuration;
endTime.minute = minute + minuteDuration;
// Update tz in case the start time switched from/to DLS
populateTimezone(startMillis);
} else {
// The end time was changed.
startMillis = startTime.toMillis(true);
endTime.hour = hourOfDay;
endTime.minute = minute;
// Move to the start time if the end time is before the start
// time.
if (endTime.before(startTime)) {
endTime.monthDay = startTime.monthDay + 1;
mTitleTextView.setTag(mTitleTextView.getBackground());
mLocationTextView.setTag(mLocationTextView.getBackground());
mLocationAdapter = new EventLocationAdapter(activity);
mLocationTextView.setAdapter(mLocationAdapter);
mLocationTextView.setOnEditorActionListener(new OnEditorActionListener() {
@Override
public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
if (actionId == EditorInfo.IME_ACTION_DONE) {
// Dismiss the suggestions dropdown. Return false so the other
// side effects still occur (soft keyboard going away, etc.).
mLocationTextView.dismissDropDown();
}
return false;
}
});
mAvailabilityExplicitlySet = false;
mAllDayChangingAvailability = false;
mAvailabilityCurrentlySelected = -1;
mAvailabilitySpinner.setOnItemSelectedListener(
new OnItemSelectedListener() {
@Override
public void onItemSelected(AdapterView<?> parent,
View view, int position, long id) {
// The spinner's onItemSelected gets called while it is being
// initialized to the first item, and when we explicitly set it
// in the allDay checkbox toggling, so we need these checks to
// find out when the spinner is actually being clicked.
// Set the initial selection.
if (mAvailabilityCurrentlySelected == -1) {
mAvailabilityCurrentlySelected = position;
}
if (mAvailabilityCurrentlySelected != position &&
!mAllDayChangingAvailability) {
mAvailabilityExplicitlySet = true;
} else {
mAvailabilityCurrentlySelected = position;
mAllDayChangingAvailability = false;
}
// Call populateTimezone if we support end time zone as well
}
endMillis = endTime.normalize(true);
@Override
public void onNothingSelected(AdapterView<?> arg0) {
}
});
setDate(mEndDateButton, endMillis);
setTime(mStartTimeButton, startMillis);
setTime(mEndTimeButton, endMillis);
updateHomeTime();
mDescriptionTextView.setTag(mDescriptionTextView.getBackground());
mAttendeesList.setTag(mAttendeesList.getBackground());
mOriginalPadding[0] = mLocationTextView.getPaddingLeft();
mOriginalPadding[1] = mLocationTextView.getPaddingTop();
mOriginalPadding[2] = mLocationTextView.getPaddingRight();
mOriginalPadding[3] = mLocationTextView.getPaddingBottom();
mEditViewList.add(mTitleTextView);
mEditViewList.add(mLocationTextView);
mEditViewList.add(mDescriptionTextView);
mEditViewList.add(mAttendeesList);
mViewOnlyList.add(view.findViewById(R.id.when_row));
mViewOnlyList.add(view.findViewById(R.id.timezone_textview_row));
mEditOnlyList.add(view.findViewById(R.id.all_day_row));
mEditOnlyList.add(view.findViewById(R.id.availability_row));
mEditOnlyList.add(view.findViewById(R.id.visibility_row));
mEditOnlyList.add(view.findViewById(R.id.from_row));
mEditOnlyList.add(view.findViewById(R.id.to_row));
mEditOnlyList.add(mTimezoneRow);
mEditOnlyList.add(mStartHomeGroup);
mEditOnlyList.add(mEndHomeGroup);
mResponseRadioGroup = (RadioGroup) view.findViewById(R.id.response_value);
mRemindersContainer = (LinearLayout) view.findViewById(R.id.reminder_items_container);
mTimezone = Utils.getTimeZone(activity, null);
mIsMultipane = activity.getResources().getBoolean(R.bool.tablet_config);
mStartTime = new Time(mTimezone);
mEndTime = new Time(mTimezone);
mEmailValidator = new Rfc822Validator(null);
initMultiAutoCompleteTextView((RecipientEditTextView) mAttendeesList);
// Display loading screen
setModel(null);
FragmentManager fm = activity.getFragmentManager();
RecurrencePickerDialog rpd = (RecurrencePickerDialog) fm
.findFragmentByTag(FRAG_TAG_RECUR_PICKER);
if (rpd != null) {
rpd.setOnRecurrenceSetListener(this);
}
TimeZonePickerDialog tzpd = (TimeZonePickerDialog) fm
.findFragmentByTag(FRAG_TAG_TIME_ZONE_PICKER);
if (tzpd != null) {
tzpd.setOnTimeZoneSetListener(this);
}
TimePickerDialog tpd = (TimePickerDialog) fm.findFragmentByTag(FRAG_TAG_TIME_PICKER);
if (tpd != null) {
View v;
mTimeSelectedWasStartTime = timeSelectedWasStartTime;
if (timeSelectedWasStartTime) {
v = mStartTimeButton;
} else {
v = mEndTimeButton;
}
tpd.setOnTimeSetListener(new TimeListener(v));
}
mDatePickerDialog = (DatePickerDialog) fm.findFragmentByTag(FRAG_TAG_DATE_PICKER);
if (mDatePickerDialog != null) {
View v;
mDateSelectedWasStartDate = dateSelectedWasStartDate;
if (dateSelectedWasStartDate) {
v = mStartDateButton;
} else {
v = mEndDateButton;
}
mDatePickerDialog.setOnDateSetListener(new DateListener(v));
}
}
private class TimeClickListener implements View.OnClickListener {
private Time mTime;
/**
* Loads an integer array asset into a list.
*/
private static ArrayList<Integer> loadIntegerArray(Resources r, int resNum) {
int[] vals = r.getIntArray(resNum);
int size = vals.length;
ArrayList<Integer> list = new ArrayList<Integer>(size);
public TimeClickListener(Time time) {
mTime = time;
for (int i = 0; i < size; i++) {
list.add(vals[i]);
}
@Override
public void onClick(View v) {
TimePickerDialog dialog;
if (v == mStartTimeButton) {
mTimeSelectedWasStartTime = true;
if (mStartTimePickerDialog == null) {
mStartTimePickerDialog = TimePickerDialog.newInstance(new TimeListener(v),
mTime.hour, mTime.minute, DateFormat.is24HourFormat(mActivity));
} else {
mStartTimePickerDialog.setStartTime(mTime.hour, mTime.minute);
}
dialog = mStartTimePickerDialog;
} else {
mTimeSelectedWasStartTime = false;
if (mEndTimePickerDialog == null) {
mEndTimePickerDialog = TimePickerDialog.newInstance(new TimeListener(v),
mTime.hour, mTime.minute, DateFormat.is24HourFormat(mActivity));
} else {
mEndTimePickerDialog.setStartTime(mTime.hour, mTime.minute);
}
dialog = mEndTimePickerDialog;
}
final FragmentManager fm = mActivity.getFragmentManager();
fm.executePendingTransactions();
if (dialog != null && !dialog.isAdded()) {
dialog.show(fm, FRAG_TAG_TIME_PICKER);
}
}
return list;
}
private class DateListener implements OnDateSetListener {
View mView;
public DateListener(View view) {
mView = view;
}
@Override
public void onDateSet(DatePickerDialog view, int year, int month, int monthDay) {
Log.d(TAG, "onDateSet: " + year + " " + month + " " + monthDay);
// Cache the member variables locally to avoid inner class overhead.
Time startTime = mStartTime;
Time endTime = mEndTime;
// Cache the start and end millis so that we limit the number
// of calls to normalize() and toMillis(), which are fairly
// expensive.
long startMillis;
long endMillis;
if (mView == mStartDateButton) {
// The start date was changed.
int yearDuration = endTime.year - startTime.year;
int monthDuration = endTime.month - startTime.month;
int monthDayDuration = endTime.monthDay - startTime.monthDay;
startTime.year = year;
startTime.month = month;
startTime.monthDay = monthDay;
startMillis = startTime.normalize(true);
// Also update the end date to keep the duration constant.
endTime.year = year + yearDuration;
endTime.month = month + monthDuration;
endTime.monthDay = monthDay + monthDayDuration;
endMillis = endTime.normalize(true);
// If the start date has changed then update the repeats.
populateRepeats();
// Update tz in case the start time switched from/to DLS
populateTimezone(startMillis);
} else {
// The end date was changed.
startMillis = startTime.toMillis(true);
endTime.year = year;
endTime.month = month;
endTime.monthDay = monthDay;
endMillis = endTime.normalize(true);
// Do not allow an event to have an end time before the start
// time.
if (endTime.before(startTime)) {
endTime.set(startTime);
endMillis = startMillis;
}
// Call populateTimezone if we support end time zone as well
}
setDate(mStartDateButton, startMillis);
setDate(mEndDateButton, endMillis);
setTime(mEndTimeButton, endMillis); // In case end time had to be
// reset
updateHomeTime();
}
/**
* Loads a String array asset into a list.
*/
private static ArrayList<String> loadStringArray(Resources r, int resNum) {
String[] labels = r.getStringArray(resNum);
ArrayList<String> list = new ArrayList<String>(Arrays.asList(labels));
return list;
}
// Fills in the date and time fields
@ -489,65 +519,6 @@ public class EditEventView implements View.OnClickListener, DialogInterface.OnCa
mRruleButton.setEnabled(enabled);
}
private class DateClickListener implements View.OnClickListener {
private Time mTime;
public DateClickListener(Time time) {
mTime = time;
}
@Override
public void onClick(View v) {
if (v == mStartDateButton) {
mDateSelectedWasStartDate = true;
} else {
mDateSelectedWasStartDate = false;
}
final DateListener listener = new DateListener(v);
if (mDatePickerDialog != null) {
mDatePickerDialog.dismiss();
}
mDatePickerDialog = DatePickerDialog.newInstance(listener,
mTime.year, mTime.month, mTime.monthDay);
mDatePickerDialog.setFirstDayOfWeek(Utils.getFirstDayOfWeekAsCalendar(mActivity));
mDatePickerDialog.setYearRange(Utils.YEAR_MIN, Utils.YEAR_MAX);
mDatePickerDialog.show(mActivity.getFragmentManager(), FRAG_TAG_DATE_PICKER);
}
}
public static class CalendarsAdapter extends ResourceCursorAdapter {
public CalendarsAdapter(Context context, int resourceId, Cursor c) {
super(context, resourceId, c);
setDropDownViewResource(R.layout.calendars_dropdown_item);
}
@Override
public void bindView(View view, Context context, Cursor cursor) {
View colorBar = view.findViewById(R.id.color);
int colorColumn = cursor.getColumnIndexOrThrow(Calendars.CALENDAR_COLOR);
int nameColumn = cursor.getColumnIndexOrThrow(Calendars.CALENDAR_DISPLAY_NAME);
int ownerColumn = cursor.getColumnIndexOrThrow(Calendars.OWNER_ACCOUNT);
if (colorBar != null) {
colorBar.setBackgroundColor(Utils.getDisplayColorFromColor(cursor
.getInt(colorColumn)));
}
TextView name = (TextView) view.findViewById(R.id.calendar_name);
if (name != null) {
String displayName = cursor.getString(nameColumn);
name.setText(displayName);
TextView accountName = (TextView) view.findViewById(R.id.account_name);
if (accountName != null) {
accountName.setText(cursor.getString(ownerColumn));
accountName.setVisibility(TextView.VISIBLE);
}
}
}
}
/**
* Does prep steps for saving a calendar event.
*
@ -757,204 +728,6 @@ public class EditEventView implements View.OnClickListener, DialogInterface.OnCa
return true;
}
public EditEventView(Activity activity, View view, EditDoneRunnable done,
boolean timeSelectedWasStartTime, boolean dateSelectedWasStartDate) {
mActivity = activity;
mView = view;
mDone = done;
// cache top level view elements
mLoadingMessage = (TextView) view.findViewById(R.id.loading_message);
mScrollView = (ScrollView) view.findViewById(R.id.scroll_view);
// cache all the widgets
mCalendarsSpinner = (Spinner) view.findViewById(R.id.calendars_spinner);
mTitleTextView = (TextView) view.findViewById(R.id.title);
mLocationTextView = (AutoCompleteTextView) view.findViewById(R.id.location);
mDescriptionTextView = (TextView) view.findViewById(R.id.description);
mTimezoneLabel = (TextView) view.findViewById(R.id.timezone_label);
mStartDateButton = (Button) view.findViewById(R.id.start_date);
mEndDateButton = (Button) view.findViewById(R.id.end_date);
mWhenView = (TextView) mView.findViewById(R.id.when);
mTimezoneTextView = (TextView) mView.findViewById(R.id.timezone_textView);
mStartTimeButton = (Button) view.findViewById(R.id.start_time);
mEndTimeButton = (Button) view.findViewById(R.id.end_time);
mTimezoneButton = (Button) view.findViewById(R.id.timezone_button);
mTimezoneButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
showTimezoneDialog();
}
});
mTimezoneRow = view.findViewById(R.id.timezone_button_row);
mStartTimeHome = (TextView) view.findViewById(R.id.start_time_home_tz);
mStartDateHome = (TextView) view.findViewById(R.id.start_date_home_tz);
mEndTimeHome = (TextView) view.findViewById(R.id.end_time_home_tz);
mEndDateHome = (TextView) view.findViewById(R.id.end_date_home_tz);
mAllDayCheckBox = (CheckBox) view.findViewById(R.id.is_all_day);
mRruleButton = (Button) view.findViewById(R.id.rrule);
mAvailabilitySpinner = (Spinner) view.findViewById(R.id.availability);
mAccessLevelSpinner = (Spinner) view.findViewById(R.id.visibility);
mCalendarSelectorGroup = view.findViewById(R.id.calendar_selector_group);
mCalendarSelectorWrapper = view.findViewById(R.id.calendar_selector_wrapper);
mCalendarStaticGroup = view.findViewById(R.id.calendar_group);
mRemindersGroup = view.findViewById(R.id.reminders_row);
mResponseGroup = view.findViewById(R.id.response_row);
mOrganizerGroup = view.findViewById(R.id.organizer_row);
mAttendeesGroup = view.findViewById(R.id.add_attendees_row);
mLocationGroup = view.findViewById(R.id.where_row);
mDescriptionGroup = view.findViewById(R.id.description_row);
mStartHomeGroup = view.findViewById(R.id.from_row_home_tz);
mEndHomeGroup = view.findViewById(R.id.to_row_home_tz);
mAttendeesList = (MultiAutoCompleteTextView) view.findViewById(R.id.attendees);
mColorPickerNewEvent = view.findViewById(R.id.change_color_new_event);
mColorPickerExistingEvent = view.findViewById(R.id.change_color_existing_event);
mTitleTextView.setTag(mTitleTextView.getBackground());
mLocationTextView.setTag(mLocationTextView.getBackground());
mLocationAdapter = new EventLocationAdapter(activity);
mLocationTextView.setAdapter(mLocationAdapter);
mLocationTextView.setOnEditorActionListener(new OnEditorActionListener() {
@Override
public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
if (actionId == EditorInfo.IME_ACTION_DONE) {
// Dismiss the suggestions dropdown. Return false so the other
// side effects still occur (soft keyboard going away, etc.).
mLocationTextView.dismissDropDown();
}
return false;
}
});
mAvailabilityExplicitlySet = false;
mAllDayChangingAvailability = false;
mAvailabilityCurrentlySelected = -1;
mAvailabilitySpinner.setOnItemSelectedListener(
new OnItemSelectedListener() {
@Override
public void onItemSelected(AdapterView<?> parent,
View view, int position, long id) {
// The spinner's onItemSelected gets called while it is being
// initialized to the first item, and when we explicitly set it
// in the allDay checkbox toggling, so we need these checks to
// find out when the spinner is actually being clicked.
// Set the initial selection.
if (mAvailabilityCurrentlySelected == -1) {
mAvailabilityCurrentlySelected = position;
}
if (mAvailabilityCurrentlySelected != position &&
!mAllDayChangingAvailability) {
mAvailabilityExplicitlySet = true;
} else {
mAvailabilityCurrentlySelected = position;
mAllDayChangingAvailability = false;
}
}
@Override
public void onNothingSelected(AdapterView<?> arg0) { }
});
mDescriptionTextView.setTag(mDescriptionTextView.getBackground());
mAttendeesList.setTag(mAttendeesList.getBackground());
mOriginalPadding[0] = mLocationTextView.getPaddingLeft();
mOriginalPadding[1] = mLocationTextView.getPaddingTop();
mOriginalPadding[2] = mLocationTextView.getPaddingRight();
mOriginalPadding[3] = mLocationTextView.getPaddingBottom();
mEditViewList.add(mTitleTextView);
mEditViewList.add(mLocationTextView);
mEditViewList.add(mDescriptionTextView);
mEditViewList.add(mAttendeesList);
mViewOnlyList.add(view.findViewById(R.id.when_row));
mViewOnlyList.add(view.findViewById(R.id.timezone_textview_row));
mEditOnlyList.add(view.findViewById(R.id.all_day_row));
mEditOnlyList.add(view.findViewById(R.id.availability_row));
mEditOnlyList.add(view.findViewById(R.id.visibility_row));
mEditOnlyList.add(view.findViewById(R.id.from_row));
mEditOnlyList.add(view.findViewById(R.id.to_row));
mEditOnlyList.add(mTimezoneRow);
mEditOnlyList.add(mStartHomeGroup);
mEditOnlyList.add(mEndHomeGroup);
mResponseRadioGroup = (RadioGroup) view.findViewById(R.id.response_value);
mRemindersContainer = (LinearLayout) view.findViewById(R.id.reminder_items_container);
mTimezone = Utils.getTimeZone(activity, null);
mIsMultipane = activity.getResources().getBoolean(R.bool.tablet_config);
mStartTime = new Time(mTimezone);
mEndTime = new Time(mTimezone);
mEmailValidator = new Rfc822Validator(null);
initMultiAutoCompleteTextView((RecipientEditTextView) mAttendeesList);
// Display loading screen
setModel(null);
FragmentManager fm = activity.getFragmentManager();
RecurrencePickerDialog rpd = (RecurrencePickerDialog) fm
.findFragmentByTag(FRAG_TAG_RECUR_PICKER);
if (rpd != null) {
rpd.setOnRecurrenceSetListener(this);
}
TimeZonePickerDialog tzpd = (TimeZonePickerDialog) fm
.findFragmentByTag(FRAG_TAG_TIME_ZONE_PICKER);
if (tzpd != null) {
tzpd.setOnTimeZoneSetListener(this);
}
TimePickerDialog tpd = (TimePickerDialog) fm.findFragmentByTag(FRAG_TAG_TIME_PICKER);
if (tpd != null) {
View v;
mTimeSelectedWasStartTime = timeSelectedWasStartTime;
if (timeSelectedWasStartTime) {
v = mStartTimeButton;
} else {
v = mEndTimeButton;
}
tpd.setOnTimeSetListener(new TimeListener(v));
}
mDatePickerDialog = (DatePickerDialog) fm.findFragmentByTag(FRAG_TAG_DATE_PICKER);
if (mDatePickerDialog != null) {
View v;
mDateSelectedWasStartDate = dateSelectedWasStartDate;
if (dateSelectedWasStartDate) {
v = mStartDateButton;
} else {
v = mEndDateButton;
}
mDatePickerDialog.setOnDateSetListener(new DateListener(v));
}
}
/**
* Loads an integer array asset into a list.
*/
private static ArrayList<Integer> loadIntegerArray(Resources r, int resNum) {
int[] vals = r.getIntArray(resNum);
int size = vals.length;
ArrayList<Integer> list = new ArrayList<Integer>(size);
for (int i = 0; i < size; i++) {
list.add(vals[i]);
}
return list;
}
/**
* Loads a String array asset into a list.
*/
private static ArrayList<String> loadStringArray(Resources r, int resNum) {
String[] labels = r.getStringArray(resNum);
ArrayList<String> list = new ArrayList<String>(Arrays.asList(labels));
return list;
}
private void prepareAvailability() {
Resources r = mActivity.getResources();
@ -1575,14 +1348,6 @@ public class EditEventView implements View.OnClickListener, DialogInterface.OnCa
return list;
}
/**
* From com.google.android.gm.ComposeActivity Implements special address
* cleanup rules: The first space key entry following an "@" symbol that is
* followed by any combination of letters and symbols, including one+ dots
* and zero commas, should insert an extra comma (followed by the space).
*/
private static InputFilter[] sRecipientFilters = new InputFilter[] { new Rfc822InputFilter() };
private void setDate(TextView view, long millis) {
int flags = DateUtils.FORMAT_SHOW_DATE | DateUtils.FORMAT_SHOW_YEAR
| DateUtils.FORMAT_SHOW_WEEKDAY | DateUtils.FORMAT_ABBREV_MONTH
@ -1841,4 +1606,227 @@ public class EditEventView implements View.OnClickListener, DialogInterface.OnCa
@Override
public void onNothingSelected(AdapterView<?> parent) {
}
public static class CalendarsAdapter extends ResourceCursorAdapter {
public CalendarsAdapter(Context context, int resourceId, Cursor c) {
super(context, resourceId, c);
setDropDownViewResource(R.layout.calendars_dropdown_item);
}
@Override
public void bindView(View view, Context context, Cursor cursor) {
View colorBar = view.findViewById(R.id.color);
int colorColumn = cursor.getColumnIndexOrThrow(Calendars.CALENDAR_COLOR);
int nameColumn = cursor.getColumnIndexOrThrow(Calendars.CALENDAR_DISPLAY_NAME);
int ownerColumn = cursor.getColumnIndexOrThrow(Calendars.OWNER_ACCOUNT);
if (colorBar != null) {
colorBar.setBackgroundColor(Utils.getDisplayColorFromColor(cursor
.getInt(colorColumn)));
}
TextView name = (TextView) view.findViewById(R.id.calendar_name);
if (name != null) {
String displayName = cursor.getString(nameColumn);
name.setText(displayName);
TextView accountName = (TextView) view.findViewById(R.id.account_name);
if (accountName != null) {
accountName.setText(cursor.getString(ownerColumn));
accountName.setVisibility(TextView.VISIBLE);
}
}
}
}
/* This class is used to update the time buttons. */
private class TimeListener implements OnTimeSetListener {
private View mView;
public TimeListener(View view) {
mView = view;
}
@Override
public void onTimeSet(RadialPickerLayout view, int hourOfDay, int minute) {
// Cache the member variables locally to avoid inner class overhead.
Time startTime = mStartTime;
Time endTime = mEndTime;
// Cache the start and end millis so that we limit the number
// of calls to normalize() and toMillis(), which are fairly
// expensive.
long startMillis;
long endMillis;
if (mView == mStartTimeButton) {
// The start time was changed.
int hourDuration = endTime.hour - startTime.hour;
int minuteDuration = endTime.minute - startTime.minute;
startTime.hour = hourOfDay;
startTime.minute = minute;
startMillis = startTime.normalize(true);
// Also update the end time to keep the duration constant.
endTime.hour = hourOfDay + hourDuration;
endTime.minute = minute + minuteDuration;
// Update tz in case the start time switched from/to DLS
populateTimezone(startMillis);
} else {
// The end time was changed.
startMillis = startTime.toMillis(true);
endTime.hour = hourOfDay;
endTime.minute = minute;
// Move to the start time if the end time is before the start
// time.
if (endTime.before(startTime)) {
endTime.monthDay = startTime.monthDay + 1;
}
// Call populateTimezone if we support end time zone as well
}
endMillis = endTime.normalize(true);
setDate(mEndDateButton, endMillis);
setTime(mStartTimeButton, startMillis);
setTime(mEndTimeButton, endMillis);
updateHomeTime();
}
}
private class TimeClickListener implements View.OnClickListener {
private Time mTime;
public TimeClickListener(Time time) {
mTime = time;
}
@Override
public void onClick(View v) {
TimePickerDialog dialog;
if (v == mStartTimeButton) {
mTimeSelectedWasStartTime = true;
if (mStartTimePickerDialog == null) {
mStartTimePickerDialog = TimePickerDialog.newInstance(new TimeListener(v),
mTime.hour, mTime.minute, DateFormat.is24HourFormat(mActivity));
} else {
mStartTimePickerDialog.setStartTime(mTime.hour, mTime.minute);
}
dialog = mStartTimePickerDialog;
} else {
mTimeSelectedWasStartTime = false;
if (mEndTimePickerDialog == null) {
mEndTimePickerDialog = TimePickerDialog.newInstance(new TimeListener(v),
mTime.hour, mTime.minute, DateFormat.is24HourFormat(mActivity));
} else {
mEndTimePickerDialog.setStartTime(mTime.hour, mTime.minute);
}
dialog = mEndTimePickerDialog;
}
final FragmentManager fm = mActivity.getFragmentManager();
fm.executePendingTransactions();
if (dialog != null && !dialog.isAdded()) {
dialog.show(fm, FRAG_TAG_TIME_PICKER);
}
}
}
private class DateListener implements OnDateSetListener {
View mView;
public DateListener(View view) {
mView = view;
}
@Override
public void onDateSet(DatePickerDialog view, int year, int month, int monthDay) {
Log.d(TAG, "onDateSet: " + year + " " + month + " " + monthDay);
// Cache the member variables locally to avoid inner class overhead.
Time startTime = mStartTime;
Time endTime = mEndTime;
// Cache the start and end millis so that we limit the number
// of calls to normalize() and toMillis(), which are fairly
// expensive.
long startMillis;
long endMillis;
if (mView == mStartDateButton) {
// The start date was changed.
int yearDuration = endTime.year - startTime.year;
int monthDuration = endTime.month - startTime.month;
int monthDayDuration = endTime.monthDay - startTime.monthDay;
startTime.year = year;
startTime.month = month;
startTime.monthDay = monthDay;
startMillis = startTime.normalize(true);
// Also update the end date to keep the duration constant.
endTime.year = year + yearDuration;
endTime.month = month + monthDuration;
endTime.monthDay = monthDay + monthDayDuration;
endMillis = endTime.normalize(true);
// If the start date has changed then update the repeats.
populateRepeats();
// Update tz in case the start time switched from/to DLS
populateTimezone(startMillis);
} else {
// The end date was changed.
startMillis = startTime.toMillis(true);
endTime.year = year;
endTime.month = month;
endTime.monthDay = monthDay;
endMillis = endTime.normalize(true);
// Do not allow an event to have an end time before the start
// time.
if (endTime.before(startTime)) {
endTime.set(startTime);
endMillis = startMillis;
}
// Call populateTimezone if we support end time zone as well
}
setDate(mStartDateButton, startMillis);
setDate(mEndDateButton, endMillis);
setTime(mEndTimeButton, endMillis); // In case end time had to be
// reset
updateHomeTime();
}
}
private class DateClickListener implements View.OnClickListener {
private Time mTime;
public DateClickListener(Time time) {
mTime = time;
}
@Override
public void onClick(View v) {
if (v == mStartDateButton) {
mDateSelectedWasStartDate = true;
} else {
mDateSelectedWasStartDate = false;
}
final DateListener listener = new DateListener(v);
if (mDatePickerDialog != null) {
mDatePickerDialog.dismiss();
}
mDatePickerDialog = DatePickerDialog.newInstance(listener,
mTime.year, mTime.month, mTime.monthDay);
mDatePickerDialog.setFirstDayOfWeek(Utils.getFirstDayOfWeekAsCalendar(mActivity));
mDatePickerDialog.setYearRange(Utils.YEAR_MIN, Utils.YEAR_MAX);
mDatePickerDialog.show(mActivity.getFragmentManager(), FRAG_TAG_DATE_PICKER);
}
}
}

View File

@ -20,9 +20,10 @@ import android.app.Dialog;
import android.content.DialogInterface;
import android.os.Bundle;
import org.sufficientlysecure.standalonecalendar.R;
import com.android.colorpicker.ColorPickerDialog;
import ws.xsoh.etar.R;
/**
* A dialog which displays event colors, with an additional button for the calendar color.
*/

View File

@ -39,8 +39,6 @@ import android.widget.Filterable;
import android.widget.ImageView;
import android.widget.TextView;
import org.sufficientlysecure.standalonecalendar.R;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.HashMap;
@ -50,6 +48,8 @@ import java.util.Map;
import java.util.TreeSet;
import java.util.concurrent.ExecutionException;
import ws.xsoh.etar.R;
// TODO: limit length of dropdown to stop at the soft keyboard
// TODO: history icon resize asset
@ -59,40 +59,6 @@ import java.util.concurrent.ExecutionException;
public class EventLocationAdapter extends ArrayAdapter<EventLocationAdapter.Result>
implements Filterable {
private static final String TAG = "EventLocationAdapter";
/**
* Internal class for containing info for an item in the auto-complete results.
*/
public static class Result {
private final String mName;
private final String mAddress;
// The default image resource for the icon. This will be null if there should
// be no icon (if multiple listings for a contact, only the first one should have the
// photo icon).
private final Integer mDefaultIcon;
// The contact photo to use for the icon. This will override the default icon.
private final Uri mContactPhotoUri;
public Result(String displayName, String address, Integer defaultIcon,
Uri contactPhotoUri) {
this.mName = displayName;
this.mAddress = address;
this.mDefaultIcon = defaultIcon;
this.mContactPhotoUri = contactPhotoUri;
}
/**
* This is the autocompleted text.
*/
@Override
public String toString() {
return mAddress;
}
}
private static ArrayList<Result> EMPTY_LIST = new ArrayList<Result>();
// Constants for contacts query:
// SELECT ... FROM view_data data WHERE ((data1 LIKE 'input%' OR data1 LIKE '%input%' OR
// display_name LIKE 'input%' OR display_name LIKE '%input%' )) ORDER BY display_name ASC
@ -120,7 +86,6 @@ public class EventLocationAdapter extends ArrayAdapter<EventLocationAdapter.Resu
.append(Contacts.DISPLAY_NAME)
.append(" LIKE ? )")
.toString();
// Constants for recent locations query (in Events table):
// SELECT ... FROM view_events WHERE (eventLocation LIKE 'input%') ORDER BY _id DESC
private static final String[] EVENT_PROJECTION = new String[] {
@ -134,11 +99,10 @@ public class EventLocationAdapter extends ArrayAdapter<EventLocationAdapter.Resu
private static final String LOCATION_WHERE = Events.VISIBLE + "=? AND "
+ Events.EVENT_LOCATION + " LIKE ?";
private static final int MAX_LOCATION_SUGGESTIONS = 4;
private static ArrayList<Result> EMPTY_LIST = new ArrayList<Result>();
private final ContentResolver mResolver;
private final LayoutInflater mInflater;
private final ArrayList<Result> mResultList = new ArrayList<Result>();
// The cache for contacts photos. We don't have to worry about clearing this, as a
// new adapter is created for every edit event.
private final Map<Uri, Bitmap> mPhotoCache = new HashMap<Uri, Bitmap>();
@ -153,6 +117,136 @@ public class EventLocationAdapter extends ArrayAdapter<EventLocationAdapter.Resu
mInflater = (LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
}
/**
* Matches the input string against contacts names and addresses.
*
* @param resolver The content resolver.
* @param input The user-typed input string.
* @param addressesRetVal The addresses in the returned result are also returned here
* for faster lookup. Pass in an empty set.
* @return Ordered list of all the matched results. If there are multiple address matches
* for the same contact, they will be listed together in individual items, with only
* the first item containing a name/icon.
*/
private static List<Result> queryContacts(ContentResolver resolver, String input,
HashSet<String> addressesRetVal) {
String where = null;
String[] whereArgs = null;
// Match any word in contact name or address.
if (!TextUtils.isEmpty(input)) {
where = CONTACTS_WHERE;
String param1 = input + "%";
String param2 = "% " + input + "%";
whereArgs = new String[]{param1, param2, param1, param2};
}
// Perform the query.
Cursor c = resolver.query(CommonDataKinds.StructuredPostal.CONTENT_URI,
CONTACTS_PROJECTION, where, whereArgs, Contacts.DISPLAY_NAME + " ASC");
// Process results. Group together addresses for the same contact.
try {
Map<String, List<Result>> nameToAddresses = new HashMap<String, List<Result>>();
c.moveToPosition(-1);
while (c.moveToNext()) {
String name = c.getString(CONTACTS_INDEX_DISPLAY_NAME);
String address = c.getString(CONTACTS_INDEX_ADDRESS);
if (name != null) {
List<Result> addressesForName = nameToAddresses.get(name);
Result result;
if (addressesForName == null) {
// Determine if there is a photo for the icon.
Uri contactPhotoUri = null;
if (c.getLong(CONTACTS_INDEX_PHOTO_ID) > 0) {
contactPhotoUri = ContentUris.withAppendedId(Contacts.CONTENT_URI,
c.getLong(CONTACTS_INDEX_CONTACT_ID));
}
// First listing for a distinct contact should have the name/icon.
addressesForName = new ArrayList<Result>();
nameToAddresses.put(name, addressesForName);
result = new Result(name, address, R.drawable.ic_contact_picture,
contactPhotoUri);
} else {
// Do not include name/icon in subsequent listings for the same contact.
result = new Result(null, address, null, null);
}
addressesForName.add(result);
addressesRetVal.add(address);
}
}
// Return the list of results.
List<Result> allResults = new ArrayList<Result>();
for (List<Result> result : nameToAddresses.values()) {
allResults.addAll(result);
}
return allResults;
} finally {
if (c != null) {
c.close();
}
}
}
/**
* Matches the input string against recent locations.
*/
private static List<Result> queryRecentLocations(ContentResolver resolver, String input) {
// TODO: also match each word in the address?
String filter = input == null ? "" : input + "%";
if (filter.isEmpty()) {
return null;
}
// Query all locations prefixed with the constraint. There is no way to insert
// 'DISTINCT' or 'GROUP BY' to get rid of dupes, so use post-processing to
// remove dupes. We will order query results by descending event ID to show
// results that were most recently inputed.
Cursor c = resolver.query(Events.CONTENT_URI, EVENT_PROJECTION, LOCATION_WHERE,
new String[]{"1", filter}, Events._ID + " DESC");
try {
List<Result> recentLocations = null;
if (c != null) {
// Post process query results.
recentLocations = processLocationsQueryResults(c);
}
return recentLocations;
} finally {
if (c != null) {
c.close();
}
}
}
/**
* Post-process the query results to return the first MAX_LOCATION_SUGGESTIONS
* unique locations in alphabetical order.
* <p/>
* TODO: Refactor to share code with the recent titles auto-complete.
*/
private static List<Result> processLocationsQueryResults(Cursor cursor) {
TreeSet<String> locations = new TreeSet<String>(String.CASE_INSENSITIVE_ORDER);
cursor.moveToPosition(-1);
// Remove dupes.
while ((locations.size() < MAX_LOCATION_SUGGESTIONS) && cursor.moveToNext()) {
String location = cursor.getString(EVENT_INDEX_LOCATION).trim();
locations.add(location);
}
// Copy the sorted results.
List<Result> results = new ArrayList<Result>();
for (String location : locations) {
results.add(new Result(null, location, R.drawable.ic_history_holo_light, null));
}
return results;
}
@Override
public int getCount() {
return mResultList.size();
@ -260,6 +354,38 @@ public class EventLocationAdapter extends ArrayAdapter<EventLocationAdapter.Resu
return new LocationFilter();
}
/**
* Internal class for containing info for an item in the auto-complete results.
*/
public static class Result {
private final String mName;
private final String mAddress;
// The default image resource for the icon. This will be null if there should
// be no icon (if multiple listings for a contact, only the first one should have the
// photo icon).
private final Integer mDefaultIcon;
// The contact photo to use for the icon. This will override the default icon.
private final Uri mContactPhotoUri;
public Result(String displayName, String address, Integer defaultIcon,
Uri contactPhotoUri) {
this.mName = displayName;
this.mAddress = address;
this.mDefaultIcon = defaultIcon;
this.mContactPhotoUri = contactPhotoUri;
}
/**
* This is the autocompleted text.
*/
@Override
public String toString() {
return mAddress;
}
}
/**
* Filter implementation for matching the input string against contacts info and
* recent locations.
@ -339,134 +465,4 @@ public class EventLocationAdapter extends ArrayAdapter<EventLocationAdapter.Resu
}
}
}
/**
* Matches the input string against contacts names and addresses.
*
* @param resolver The content resolver.
* @param input The user-typed input string.
* @param addressesRetVal The addresses in the returned result are also returned here
* for faster lookup. Pass in an empty set.
* @return Ordered list of all the matched results. If there are multiple address matches
* for the same contact, they will be listed together in individual items, with only
* the first item containing a name/icon.
*/
private static List<Result> queryContacts(ContentResolver resolver, String input,
HashSet<String> addressesRetVal) {
String where = null;
String[] whereArgs = null;
// Match any word in contact name or address.
if (!TextUtils.isEmpty(input)) {
where = CONTACTS_WHERE;
String param1 = input + "%";
String param2 = "% " + input + "%";
whereArgs = new String[] {param1, param2, param1, param2};
}
// Perform the query.
Cursor c = resolver.query(CommonDataKinds.StructuredPostal.CONTENT_URI,
CONTACTS_PROJECTION, where, whereArgs, Contacts.DISPLAY_NAME + " ASC");
// Process results. Group together addresses for the same contact.
try {
Map<String, List<Result>> nameToAddresses = new HashMap<String, List<Result>>();
c.moveToPosition(-1);
while (c.moveToNext()) {
String name = c.getString(CONTACTS_INDEX_DISPLAY_NAME);
String address = c.getString(CONTACTS_INDEX_ADDRESS);
if (name != null) {
List<Result> addressesForName = nameToAddresses.get(name);
Result result;
if (addressesForName == null) {
// Determine if there is a photo for the icon.
Uri contactPhotoUri = null;
if (c.getLong(CONTACTS_INDEX_PHOTO_ID) > 0) {
contactPhotoUri = ContentUris.withAppendedId(Contacts.CONTENT_URI,
c.getLong(CONTACTS_INDEX_CONTACT_ID));
}
// First listing for a distinct contact should have the name/icon.
addressesForName = new ArrayList<Result>();
nameToAddresses.put(name, addressesForName);
result = new Result(name, address, R.drawable.ic_contact_picture,
contactPhotoUri);
} else {
// Do not include name/icon in subsequent listings for the same contact.
result = new Result(null, address, null, null);
}
addressesForName.add(result);
addressesRetVal.add(address);
}
}
// Return the list of results.
List<Result> allResults = new ArrayList<Result>();
for (List<Result> result : nameToAddresses.values()) {
allResults.addAll(result);
}
return allResults;
} finally {
if (c != null) {
c.close();
}
}
}
/**
* Matches the input string against recent locations.
*/
private static List<Result> queryRecentLocations(ContentResolver resolver, String input) {
// TODO: also match each word in the address?
String filter = input == null ? "" : input + "%";
if (filter.isEmpty()) {
return null;
}
// Query all locations prefixed with the constraint. There is no way to insert
// 'DISTINCT' or 'GROUP BY' to get rid of dupes, so use post-processing to
// remove dupes. We will order query results by descending event ID to show
// results that were most recently inputed.
Cursor c = resolver.query(Events.CONTENT_URI, EVENT_PROJECTION, LOCATION_WHERE,
new String[] { "1", filter }, Events._ID + " DESC");
try {
List<Result> recentLocations = null;
if (c != null) {
// Post process query results.
recentLocations = processLocationsQueryResults(c);
}
return recentLocations;
} finally {
if (c != null) {
c.close();
}
}
}
/**
* Post-process the query results to return the first MAX_LOCATION_SUGGESTIONS
* unique locations in alphabetical order.
*
* TODO: Refactor to share code with the recent titles auto-complete.
*/
private static List<Result> processLocationsQueryResults(Cursor cursor) {
TreeSet<String> locations = new TreeSet<String>(String.CASE_INSENSITIVE_ORDER);
cursor.moveToPosition(-1);
// Remove dupes.
while ((locations.size() < MAX_LOCATION_SUGGESTIONS) && cursor.moveToNext()) {
String location = cursor.getString(EVENT_INDEX_LOCATION).trim();
locations.add(location);
}
// Copy the sorted results.
List<Result> results = new ArrayList<Result>();
for (String location : locations) {
results.add(new Result(null, location, R.drawable.ic_history_holo_light, null));
}
return results;
}
}

View File

@ -15,9 +15,6 @@
*/
package com.android.calendar.event;
import com.android.calendar.CalendarEventModel.ReminderEntry;
import org.sufficientlysecure.standalonecalendar.R;
import android.app.Activity;
import android.content.Context;
import android.content.res.Resources;
@ -30,8 +27,12 @@ import android.widget.ImageButton;
import android.widget.LinearLayout;
import android.widget.Spinner;
import com.android.calendar.CalendarEventModel.ReminderEntry;
import java.util.ArrayList;
import ws.xsoh.etar.R;
public class EventViewUtils {
private static final String TAG = "EventViewUtils";

View File

@ -36,11 +36,11 @@ import com.android.calendar.CalendarController.ViewType;
import com.android.calendar.Event;
import com.android.calendar.Utils;
import org.sufficientlysecure.standalonecalendar.R;
import java.util.ArrayList;
import java.util.HashMap;
import ws.xsoh.etar.R;
public class MonthByWeekAdapter extends SimpleWeeksAdapter {
public static final String WEEK_PARAMS_IS_MINI = "mini_month";
private static final String TAG = "MonthByWeekAdapter";

View File

@ -49,7 +49,6 @@ import com.android.calendar.CalendarController.EventInfo;
import com.android.calendar.CalendarController.EventType;
import com.android.calendar.CalendarController.ViewType;
import com.android.calendar.Event;
import org.sufficientlysecure.standalonecalendar.R;
import com.android.calendar.Utils;
import com.android.calendar.event.CreateEventDialogFragment;
@ -58,27 +57,17 @@ import java.util.Calendar;
import java.util.HashMap;
import java.util.List;
import ws.xsoh.etar.R;
public class MonthByWeekFragment extends SimpleDayPickerFragment implements
CalendarController.EventHandler, LoaderManager.LoaderCallbacks<Cursor>, OnScrollListener,
OnTouchListener {
private static final String TAG = "MonthFragment";
private static final String TAG_EVENT_DIALOG = "event_dialog";
private CreateEventDialogFragment mEventDialog;
// Selection and selection args for adding event queries
private static final String WHERE_CALENDARS_VISIBLE = Calendars.VISIBLE + "=1";
private static final String INSTANCES_SORT_ORDER = Instances.START_DAY + ","
+ Instances.START_MINUTE + "," + Instances.TITLE;
protected static boolean mShowDetailsInMonth = false;
protected float mMinimumTwoMonthFlingVelocity;
protected boolean mIsMiniMonth;
protected boolean mHideDeclined;
protected int mFirstLoadedJulianDay;
protected int mLastLoadedJulianDay;
private static final int WEEKS_BUFFER = 1;
// How long to wait after scroll stops before starting the loader
// Using scroll duration because scroll state changes don't update
@ -87,32 +76,8 @@ public class MonthByWeekFragment extends SimpleDayPickerFragment implements
// The minimum time between requeries of the data if the db is
// changing
private static final int LOADER_THROTTLE_DELAY = 500;
private CursorLoader mLoader;
private Uri mEventUri;
protected static boolean mShowDetailsInMonth = false;
private final Time mDesiredDay = new Time();
private volatile boolean mShouldLoad = true;
private boolean mUserScrolled = false;
private int mEventsLoadingDelay;
private boolean mShowCalendarControls;
private boolean mIsDetached;
private Handler mEventDialogHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
final FragmentManager manager = getFragmentManager();
if (manager != null) {
Time day = (Time) msg.obj;
mEventDialog = new CreateEventDialogFragment(day);
mEventDialog.show(manager, TAG_EVENT_DIALOG);
}
}
};
private final Runnable mTZUpdater = new Runnable() {
@Override
public void run() {
@ -129,8 +94,15 @@ public class MonthByWeekFragment extends SimpleDayPickerFragment implements
}
}
};
protected float mMinimumTwoMonthFlingVelocity;
protected boolean mIsMiniMonth;
protected boolean mHideDeclined;
protected int mFirstLoadedJulianDay;
protected int mLastLoadedJulianDay;
private CreateEventDialogFragment mEventDialog;
private CursorLoader mLoader;
private Uri mEventUri;
private volatile boolean mShouldLoad = true;
private final Runnable mUpdateLoader = new Runnable() {
@Override
public void run() {
@ -153,6 +125,10 @@ public class MonthByWeekFragment extends SimpleDayPickerFragment implements
}
}
};
private boolean mUserScrolled = false;
private int mEventsLoadingDelay;
private boolean mShowCalendarControls;
private boolean mIsDetached;
// Used to load the events when a delay is needed
Runnable mLoadingRunnable = new Runnable() {
@Override
@ -163,7 +139,28 @@ public class MonthByWeekFragment extends SimpleDayPickerFragment implements
}
}
};
private Handler mEventDialogHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
final FragmentManager manager = getFragmentManager();
if (manager != null) {
Time day = (Time) msg.obj;
mEventDialog = new CreateEventDialogFragment(day);
mEventDialog.show(manager, TAG_EVENT_DIALOG);
}
}
};
public MonthByWeekFragment() {
this(System.currentTimeMillis(), true);
}
public MonthByWeekFragment(long initialTime, boolean isMiniMonth) {
super(initialTime);
mIsMiniMonth = isMiniMonth;
}
/**
* Updates the uri used by the loader according to the current position of
@ -316,15 +313,6 @@ public class MonthByWeekFragment extends SimpleDayPickerFragment implements
mAdapter.setListView(mListView);
}
public MonthByWeekFragment() {
this(System.currentTimeMillis(), true);
}
public MonthByWeekFragment(long initialTime, boolean isMiniMonth) {
super(initialTime);
mIsMiniMonth = isMiniMonth;
}
@Override
protected void setUpHeader() {
if (mIsMiniMonth) {

View File

@ -44,8 +44,6 @@ import android.view.accessibility.AccessibilityManager;
import com.android.calendar.Event;
import com.android.calendar.Utils;
import org.sufficientlysecure.standalonecalendar.R;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Formatter;
@ -54,6 +52,8 @@ import java.util.Iterator;
import java.util.List;
import java.util.Locale;
import ws.xsoh.etar.R;
public class MonthWeekEventsView extends SimpleWeekView {
public static final String VIEW_PARAMS_ORIENTATION = "orientation";

View File

@ -16,9 +16,6 @@
package com.android.calendar.month;
import org.sufficientlysecure.standalonecalendar.R;
import com.android.calendar.Utils;
import android.app.Activity;
import android.app.ListFragment;
import android.content.Context;
@ -40,10 +37,14 @@ import android.widget.AbsListView.OnScrollListener;
import android.widget.ListView;
import android.widget.TextView;
import com.android.calendar.Utils;
import java.util.Calendar;
import java.util.HashMap;
import java.util.Locale;
import ws.xsoh.etar.R;
/**
* <p>
* This displays a titled list of weeks with selectable days. It can be
@ -55,9 +56,8 @@ import java.util.Locale;
*/
public class SimpleDayPickerFragment extends ListFragment implements OnScrollListener {
private static final String TAG = "MonthFragment";
private static final String KEY_CURRENT_TIME = "current_time";
// The number of days to display in each week
public static final int DAYS_PER_WEEK = 7;
// Affects when the month selection will change while scrolling up
protected static final int SCROLL_HYST_WEEKS = 2;
// How long the GoTo fling animation should last
@ -65,41 +65,34 @@ public class SimpleDayPickerFragment extends ListFragment implements OnScrollLis
// How long to wait after receiving an onScrollStateChanged notification
// before acting on it
protected static final int SCROLL_CHANGE_DELAY = 40;
// The number of days to display in each week
public static final int DAYS_PER_WEEK = 7;
// The size of the month name displayed above the week list
protected static final int MINI_MONTH_NAME_TEXT_SIZE = 18;
private static final String TAG = "MonthFragment";
private static final String KEY_CURRENT_TIME = "current_time";
public static int LIST_TOP_OFFSET = -1; // so that the top line will be under the separator
private static float mScale = 0;
protected int WEEK_MIN_VISIBLE_HEIGHT = 12;
protected int BOTTOM_BUFFER = 20;
protected int mSaturdayColor = 0;
protected int mSundayColor = 0;
protected int mDayNameColor = 0;
// You can override these numbers to get a different appearance
protected int mNumWeeks = 6;
protected boolean mShowWeekNumber = false;
protected int mDaysPerWeek = 7;
// These affect the scroll speed and feel
protected float mFriction = 1.0f;
protected Context mContext;
protected Handler mHandler;
protected float mMinimumFlingVelocity;
// highlighted time
protected Time mSelectedDay = new Time();
protected SimpleWeeksAdapter mAdapter;
protected ListView mListView;
protected ViewGroup mDayNamesHeader;
protected String[] mDayLabels;
// disposable variable used for time calculations
protected Time mTempTime = new Time();
private static float mScale = 0;
// When the week starts; numbered like Time.<WEEKDAY> (e.g. SUNDAY=0).
protected int mFirstDayOfWeek;
// The first day of the focus month
@ -141,7 +134,7 @@ public class SimpleDayPickerFragment extends ListFragment implements OnScrollLis
}
}
};
protected ScrollStateRunnable mScrollStateChangedRunnable = new ScrollStateRunnable();
// This allows us to update our position when a day is tapped
protected DataSetObserver mObserver = new DataSetObserver() {
@Override
@ -578,8 +571,6 @@ public class SimpleDayPickerFragment extends ListFragment implements OnScrollLis
mScrollStateChangedRunnable.doScrollStateChange(view, scrollState);
}
protected ScrollStateRunnable mScrollStateChangedRunnable = new ScrollStateRunnable();
protected class ScrollStateRunnable implements Runnable {
private int mNewState;

View File

@ -16,9 +16,6 @@
package com.android.calendar.month;
import org.sufficientlysecure.standalonecalendar.R;
import com.android.calendar.Utils;
import android.app.Service;
import android.content.Context;
import android.content.res.Resources;
@ -35,10 +32,14 @@ import android.view.View;
import android.view.accessibility.AccessibilityEvent;
import android.view.accessibility.AccessibilityManager;
import com.android.calendar.Utils;
import java.security.InvalidParameterException;
import java.text.NumberFormat;
import java.util.HashMap;
import ws.xsoh.etar.R;
/**
* <p>
* This is a dynamic view for drawing a single week. It can be configured to
@ -48,17 +49,16 @@ import java.util.HashMap;
* </p>
*/
public class SimpleWeekView extends View {
private static final String TAG = "MonthView";
/**
* This sets the height of this week in pixels
*/
public static final String VIEW_PARAMS_HEIGHT = "height";
/**
* These params can be passed into the view to control how it appears.
* {@link #VIEW_PARAMS_WEEK} is the only required field, though the default
* values are unlikely to fit most layouts correctly.
*/
/**
* This sets the height of this week in pixels
*/
public static final String VIEW_PARAMS_HEIGHT = "height";
/**
* This specifies the position (or weeks since the epoch) of this week,
* calculated using {@link Utils#getWeeksSinceEpochFromJulianDay}
@ -88,15 +88,14 @@ public class SimpleWeekView extends View {
* If this month should display week numbers. false if 0, true otherwise.
*/
public static final String VIEW_PARAMS_SHOW_WK_NUM = "show_wk_num";
protected static int DEFAULT_HEIGHT = 32;
protected static int MIN_HEIGHT = 10;
protected static final int DEFAULT_SELECTED_DAY = -1;
protected static final int DEFAULT_WEEK_START = Time.SUNDAY;
protected static final int DEFAULT_NUM_DAYS = 7;
protected static final int DEFAULT_SHOW_WK_NUM = 0;
protected static final int DEFAULT_FOCUS_MONTH = -1;
private static final String TAG = "MonthView";
protected static int DEFAULT_HEIGHT = 32;
protected static int MIN_HEIGHT = 10;
protected static int DAY_SEPARATOR_WIDTH = 1;
protected static int MINI_DAY_NUMBER_TEXT_SIZE = 14;
@ -166,6 +165,7 @@ public class SimpleWeekView extends View {
protected int mDaySeparatorColor;
protected int mTodayOutlineColor;
protected int mWeekNumColor;
Time mLastHoverTime = null;
public SimpleWeekView(Context context) {
super(context);
@ -547,6 +547,4 @@ public class SimpleWeekView extends View {
}
return true;
}
Time mLastHoverTime = null;
}

View File

@ -54,7 +54,6 @@ import android.widget.TextView;
import android.widget.Toast;
import android.widget.ToggleButton;
import org.sufficientlysecure.standalonecalendar.R;
import com.android.calendar.Utils;
import com.android.calendarcommon2.EventRecurrence;
import com.android.datetimepicker.date.DatePickerDialog;
@ -64,209 +63,33 @@ import java.util.ArrayList;
import java.util.Arrays;
import java.util.Calendar;
import ws.xsoh.etar.R;
public class RecurrencePickerDialog extends DialogFragment implements OnItemSelectedListener,
OnCheckedChangeListener, OnClickListener,
android.widget.RadioGroup.OnCheckedChangeListener, DatePickerDialog.OnDateSetListener {
public static final String BUNDLE_START_TIME_MILLIS = "bundle_event_start_time";
public static final String BUNDLE_TIME_ZONE = "bundle_event_time_zone";
public static final String BUNDLE_RRULE = "bundle_event_rrule";
private static final String TAG = "RecurrencePickerDialog";
// in dp's
private static final int MIN_SCREEN_WIDTH_FOR_SINGLE_ROW_WEEK = 450;
// Update android:maxLength in EditText as needed
private static final int INTERVAL_MAX = 99;
private static final int INTERVAL_DEFAULT = 1;
// Update android:maxLength in EditText as needed
private static final int COUNT_MAX = 730;
private static final int COUNT_DEFAULT = 5;
private DatePickerDialog mDatePickerDialog;
private class RecurrenceModel implements Parcelable {
// Should match EventRecurrence.DAILY, etc
static final int FREQ_DAILY = 0;
static final int FREQ_WEEKLY = 1;
static final int FREQ_MONTHLY = 2;
static final int FREQ_YEARLY = 3;
static final int END_NEVER = 0;
static final int END_BY_DATE = 1;
static final int END_BY_COUNT = 2;
static final int MONTHLY_BY_DATE = 0;
static final int MONTHLY_BY_NTH_DAY_OF_WEEK = 1;
static final int STATE_NO_RECURRENCE = 0;
static final int STATE_RECURRENCE = 1;
int recurrenceState;
/**
* FREQ: Repeat pattern
*
* @see FREQ_DAILY
* @see FREQ_WEEKLY
* @see FREQ_MONTHLY
* @see FREQ_YEARLY
*/
int freq = FREQ_WEEKLY;
/**
* INTERVAL: Every n days/weeks/months/years. n >= 1
*/
int interval = INTERVAL_DEFAULT;
/**
* UNTIL and COUNT: How does the the event end?
*
* @see END_NEVER
* @see END_BY_DATE
* @see END_BY_COUNT
* @see untilDate
* @see untilCount
*/
int end;
/**
* UNTIL: Date of the last recurrence. Used when until == END_BY_DATE
*/
Time endDate;
/**
* COUNT: Times to repeat. Use when until == END_BY_COUNT
*/
int endCount = COUNT_DEFAULT;
/**
* BYDAY: Days of the week to be repeated. Sun = 0, Mon = 1, etc
*/
boolean[] weeklyByDayOfWeek = new boolean[7];
/**
* BYDAY AND BYMONTHDAY: How to repeat monthly events? Same date of the
* month or Same nth day of week.
*
* @see MONTHLY_BY_DATE
* @see MONTHLY_BY_NTH_DAY_OF_WEEK
*/
int monthlyRepeat;
/**
* Day of the month to repeat. Used when monthlyRepeat ==
* MONTHLY_BY_DATE
*/
int monthlyByMonthDay;
/**
* Day of the week to repeat. Used when monthlyRepeat ==
* MONTHLY_BY_NTH_DAY_OF_WEEK
*/
int monthlyByDayOfWeek;
/**
* Nth day of the week to repeat. Used when monthlyRepeat ==
* MONTHLY_BY_NTH_DAY_OF_WEEK 0=undefined, 1=1st, 2=2nd, etc
*/
int monthlyByNthDayOfWeek;
/*
* (generated method)
*/
@Override
public String toString() {
return "Model [freq=" + freq + ", interval=" + interval + ", end=" + end + ", endDate="
+ endDate + ", endCount=" + endCount + ", weeklyByDayOfWeek="
+ Arrays.toString(weeklyByDayOfWeek) + ", monthlyRepeat=" + monthlyRepeat
+ ", monthlyByMonthDay=" + monthlyByMonthDay + ", monthlyByDayOfWeek="
+ monthlyByDayOfWeek + ", monthlyByNthDayOfWeek=" + monthlyByNthDayOfWeek + "]";
}
@Override
public int describeContents() {
return 0;
}
public RecurrenceModel() {
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeInt(freq);
dest.writeInt(interval);
dest.writeInt(end);
dest.writeInt(endDate.year);
dest.writeInt(endDate.month);
dest.writeInt(endDate.monthDay);
dest.writeInt(endCount);
dest.writeBooleanArray(weeklyByDayOfWeek);
dest.writeInt(monthlyRepeat);
dest.writeInt(monthlyByMonthDay);
dest.writeInt(monthlyByDayOfWeek);
dest.writeInt(monthlyByNthDayOfWeek);
dest.writeInt(recurrenceState);
}
}
class minMaxTextWatcher implements TextWatcher {
private int mMin;
private int mMax;
private int mDefault;
public minMaxTextWatcher(int min, int defaultInt, int max) {
mMin = min;
mMax = max;
mDefault = defaultInt;
}
@Override
public void afterTextChanged(Editable s) {
boolean updated = false;
int value;
try {
value = Integer.parseInt(s.toString());
} catch (NumberFormatException e) {
value = mDefault;
}
if (value < mMin) {
value = mMin;
updated = true;
} else if (value > mMax) {
updated = true;
value = mMax;
}
// Update UI
if (updated) {
s.clear();
s.append(Integer.toString(value));
}
updateDoneButtonState();
onChange(value);
}
/** Override to be called after each key stroke */
void onChange(int value) {
}
@Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
}
@Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
}
}
private Resources mResources;
private EventRecurrence mRecurrence = new EventRecurrence();
private Time mTime = new Time(); // TODO timezone?
private RecurrenceModel mModel = new RecurrenceModel();
private Toast mToast;
private static final int[] mFreqModelToEventRecurrence = {
EventRecurrence.DAILY,
EventRecurrence.WEEKLY,
EventRecurrence.MONTHLY,
EventRecurrence.YEARLY
};
private static final String BUNDLE_MODEL = "bundle_model";
private static final String BUNDLE_END_COUNT_HAS_FOCUS = "bundle_end_count_has_focus";
private static final String FRAG_TAG_DATE_PICKER = "tag_date_picker_frag";
private final int[] TIME_DAY_TO_CALENDAR_DAY = new int[] {
Calendar.SUNDAY,
Calendar.MONDAY,
@ -276,51 +99,34 @@ public class RecurrencePickerDialog extends DialogFragment implements OnItemSele
Calendar.FRIDAY,
Calendar.SATURDAY,
};
private DatePickerDialog mDatePickerDialog;
// Call mStringBuilder.setLength(0) before formatting any string or else the
// formatted text will accumulate.
// private final StringBuilder mStringBuilder = new StringBuilder();
// private Formatter mFormatter = new Formatter(mStringBuilder);
private Resources mResources;
private EventRecurrence mRecurrence = new EventRecurrence();
private Time mTime = new Time(); // TODO timezone?
private RecurrenceModel mModel = new RecurrenceModel();
private Toast mToast;
private View mView;
private Spinner mFreqSpinner;
private static final int[] mFreqModelToEventRecurrence = {
EventRecurrence.DAILY,
EventRecurrence.WEEKLY,
EventRecurrence.MONTHLY,
EventRecurrence.YEARLY
};
public static final String BUNDLE_START_TIME_MILLIS = "bundle_event_start_time";
public static final String BUNDLE_TIME_ZONE = "bundle_event_time_zone";
public static final String BUNDLE_RRULE = "bundle_event_rrule";
private static final String BUNDLE_MODEL = "bundle_model";
private static final String BUNDLE_END_COUNT_HAS_FOCUS = "bundle_end_count_has_focus";
private static final String FRAG_TAG_DATE_PICKER = "tag_date_picker_frag";
private Switch mRepeatSwitch;
private EditText mInterval;
private TextView mIntervalPreText;
private TextView mIntervalPostText;
private int mIntervalResId = -1;
private Spinner mEndSpinner;
private TextView mEndDateTextView;
private EditText mEndCount;
private TextView mPostEndCount;
private boolean mHidePostEndCount;
private ArrayList<CharSequence> mEndSpinnerArray = new ArrayList<CharSequence>(3);
private EndSpinnerAdapter mEndSpinnerAdapter;
private String mEndNeverStr;
private String mEndDateLabel;
private String mEndCountLabel;
/** Hold toggle buttons in the order per user's first day of week preference */
private LinearLayout mWeekGroup;
private LinearLayout mWeekGroup2;
@ -330,15 +136,12 @@ public class RecurrencePickerDialog extends DialogFragment implements OnItemSele
* "on every [Nth] [DAY_OF_WEEK]", e.g. "on every second Monday",
* where [Nth] can be [first, second, third, fourth, last] */
private String[][] mMonthRepeatByDayOfWeekStrs;
private LinearLayout mMonthGroup;
private RadioGroup mMonthRepeatByRadioGroup;
private RadioButton mRepeatMonthlyByNthDayOfWeek;
private RadioButton mRepeatMonthlyByNthDayOfMonth;
private String mMonthRepeatByDayOfWeekStr;
private Button mDone;
private OnRecurrenceSetListener mRecurrenceSetListener;
public RecurrencePickerDialog() {
@ -1154,12 +957,193 @@ public class RecurrencePickerDialog extends DialogFragment implements OnItemSele
}
}
public void setOnRecurrenceSetListener(OnRecurrenceSetListener l) {
mRecurrenceSetListener = l;
}
public interface OnRecurrenceSetListener {
void onRecurrenceSet(String rrule);
}
public void setOnRecurrenceSetListener(OnRecurrenceSetListener l) {
mRecurrenceSetListener = l;
private class RecurrenceModel implements Parcelable {
// Should match EventRecurrence.DAILY, etc
static final int FREQ_DAILY = 0;
static final int FREQ_WEEKLY = 1;
static final int FREQ_MONTHLY = 2;
static final int FREQ_YEARLY = 3;
static final int END_NEVER = 0;
static final int END_BY_DATE = 1;
static final int END_BY_COUNT = 2;
static final int MONTHLY_BY_DATE = 0;
static final int MONTHLY_BY_NTH_DAY_OF_WEEK = 1;
static final int STATE_NO_RECURRENCE = 0;
static final int STATE_RECURRENCE = 1;
int recurrenceState;
/**
* FREQ: Repeat pattern
*
* @see FREQ_DAILY
* @see FREQ_WEEKLY
* @see FREQ_MONTHLY
* @see FREQ_YEARLY
*/
int freq = FREQ_WEEKLY;
/**
* INTERVAL: Every n days/weeks/months/years. n >= 1
*/
int interval = INTERVAL_DEFAULT;
/**
* UNTIL and COUNT: How does the the event end?
*
* @see END_NEVER
* @see END_BY_DATE
* @see END_BY_COUNT
* @see untilDate
* @see untilCount
*/
int end;
/**
* UNTIL: Date of the last recurrence. Used when until == END_BY_DATE
*/
Time endDate;
/**
* COUNT: Times to repeat. Use when until == END_BY_COUNT
*/
int endCount = COUNT_DEFAULT;
/**
* BYDAY: Days of the week to be repeated. Sun = 0, Mon = 1, etc
*/
boolean[] weeklyByDayOfWeek = new boolean[7];
/**
* BYDAY AND BYMONTHDAY: How to repeat monthly events? Same date of the
* month or Same nth day of week.
*
* @see MONTHLY_BY_DATE
* @see MONTHLY_BY_NTH_DAY_OF_WEEK
*/
int monthlyRepeat;
/**
* Day of the month to repeat. Used when monthlyRepeat ==
* MONTHLY_BY_DATE
*/
int monthlyByMonthDay;
/**
* Day of the week to repeat. Used when monthlyRepeat ==
* MONTHLY_BY_NTH_DAY_OF_WEEK
*/
int monthlyByDayOfWeek;
/**
* Nth day of the week to repeat. Used when monthlyRepeat ==
* MONTHLY_BY_NTH_DAY_OF_WEEK 0=undefined, 1=1st, 2=2nd, etc
*/
int monthlyByNthDayOfWeek;
public RecurrenceModel() {
}
/*
* (generated method)
*/
@Override
public String toString() {
return "Model [freq=" + freq + ", interval=" + interval + ", end=" + end + ", endDate="
+ endDate + ", endCount=" + endCount + ", weeklyByDayOfWeek="
+ Arrays.toString(weeklyByDayOfWeek) + ", monthlyRepeat=" + monthlyRepeat
+ ", monthlyByMonthDay=" + monthlyByMonthDay + ", monthlyByDayOfWeek="
+ monthlyByDayOfWeek + ", monthlyByNthDayOfWeek=" + monthlyByNthDayOfWeek + "]";
}
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeInt(freq);
dest.writeInt(interval);
dest.writeInt(end);
dest.writeInt(endDate.year);
dest.writeInt(endDate.month);
dest.writeInt(endDate.monthDay);
dest.writeInt(endCount);
dest.writeBooleanArray(weeklyByDayOfWeek);
dest.writeInt(monthlyRepeat);
dest.writeInt(monthlyByMonthDay);
dest.writeInt(monthlyByDayOfWeek);
dest.writeInt(monthlyByNthDayOfWeek);
dest.writeInt(recurrenceState);
}
}
class minMaxTextWatcher implements TextWatcher {
private int mMin;
private int mMax;
private int mDefault;
public minMaxTextWatcher(int min, int defaultInt, int max) {
mMin = min;
mMax = max;
mDefault = defaultInt;
}
@Override
public void afterTextChanged(Editable s) {
boolean updated = false;
int value;
try {
value = Integer.parseInt(s.toString());
} catch (NumberFormatException e) {
value = mDefault;
}
if (value < mMin) {
value = mMin;
updated = true;
} else if (value > mMax) {
updated = true;
value = mMax;
}
// Update UI
if (updated) {
s.clear();
s.append(Integer.toString(value));
}
updateDoneButtonState();
onChange(value);
}
/**
* Override to be called after each key stroke
*/
void onChange(int value) {
}
@Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
}
@Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
}
}
private class EndSpinnerAdapter extends ArrayAdapter<CharSequence> {

View File

@ -22,9 +22,10 @@ import android.util.AttributeSet;
import android.widget.QuickContactBadge;
import com.android.calendar.CalendarColorPickerDialog;
import org.sufficientlysecure.standalonecalendar.R;
import com.android.colorpicker.ColorStateDrawable;
import ws.xsoh.etar.R;
/**
* The color square used as an entry point to launching the {@link CalendarColorPickerDialog}.
*/

View File

@ -37,37 +37,33 @@ import android.widget.ListAdapter;
import android.widget.TextView;
import com.android.calendar.CalendarColorPickerDialog;
import org.sufficientlysecure.standalonecalendar.R;
import com.android.calendar.Utils;
import com.android.calendar.selectcalendars.CalendarColorCache.OnCalendarColorsLoadedListener;
import ws.xsoh.etar.R;
public class SelectCalendarsSimpleAdapter extends BaseAdapter implements ListAdapter,
OnCalendarColorsLoadedListener {
private static final String TAG = "SelectCalendarsAdapter";
private static final String COLOR_PICKER_DIALOG_TAG = "ColorPickerDialog";
private static int BOTTOM_ITEM_HEIGHT = 64;
private static int NORMAL_ITEM_HEIGHT = 48;
private static final int IS_SELECTED = 1 << 0;
private static final int IS_TOP = 1 << 1;
private static final int IS_BOTTOM = 1 << 2;
private static final int IS_BELOW_SELECTED = 1 << 3;
private CalendarColorPickerDialog mColorPickerDialog;
private LayoutInflater mInflater;
private static int BOTTOM_ITEM_HEIGHT = 64;
private static int NORMAL_ITEM_HEIGHT = 48;
private static float mScale = 0;
Resources mRes;
private CalendarColorPickerDialog mColorPickerDialog;
private LayoutInflater mInflater;
private int mLayout;
private int mOrientation;
private CalendarRow[] mData;
private Cursor mCursor;
private int mRowCount = 0;
private FragmentManager mFragmentManager;
private boolean mIsTablet;
private int mColorViewTouchAreaIncrease;
private int mIdColumn;
private int mNameColumn;
private int mColorColumn;
@ -75,7 +71,6 @@ public class SelectCalendarsSimpleAdapter extends BaseAdapter implements ListAda
private int mOwnerAccountColumn;
private int mAccountNameColumn;
private int mAccountTypeColumn;
private static float mScale = 0;
private int mColorCalendarVisible;
private int mColorCalendarHidden;
private int mColorCalendarSecondaryVisible;
@ -83,16 +78,6 @@ public class SelectCalendarsSimpleAdapter extends BaseAdapter implements ListAda
private CalendarColorCache mCache;
private class CalendarRow {
long id;
String displayName;
String ownerAccount;
String accountName;
String accountType;
int color;
boolean selected;
}
public SelectCalendarsSimpleAdapter(Context context, int layout, Cursor c, FragmentManager fm) {
super();
mLayout = layout;
@ -121,56 +106,6 @@ public class SelectCalendarsSimpleAdapter extends BaseAdapter implements ListAda
.getDimensionPixelSize(R.dimen.color_view_touch_area_increase);
}
private static class TabletCalendarItemBackgrounds {
static private int[] mBackgrounds = null;
/**
* Sets up the background drawables for the calendars list
*
* @param res The context's resources
*/
static int[] getBackgrounds() {
// Not thread safe. Ok if called only from main thread
if (mBackgrounds != null) {
return mBackgrounds;
}
mBackgrounds = new int[16];
mBackgrounds[0] = R.drawable.calname_unselected;
mBackgrounds[IS_SELECTED] = R.drawable.calname_select_underunselected;
mBackgrounds[IS_SELECTED | IS_BOTTOM] =
R.drawable.calname_bottom_select_underunselected;
mBackgrounds[IS_SELECTED | IS_BOTTOM | IS_BELOW_SELECTED] =
R.drawable.calname_bottom_select_underselect;
mBackgrounds[IS_SELECTED | IS_TOP | IS_BOTTOM | IS_BELOW_SELECTED] = mBackgrounds[
IS_SELECTED | IS_BOTTOM | IS_BELOW_SELECTED];
mBackgrounds[IS_SELECTED | IS_TOP | IS_BOTTOM] = mBackgrounds[IS_SELECTED | IS_BOTTOM
| IS_BELOW_SELECTED];
mBackgrounds[IS_SELECTED | IS_BELOW_SELECTED] = R.drawable.calname_select_underselect;
mBackgrounds[IS_SELECTED | IS_TOP | IS_BELOW_SELECTED] = mBackgrounds[IS_SELECTED
| IS_BELOW_SELECTED];
mBackgrounds[IS_SELECTED | IS_TOP] = mBackgrounds[IS_SELECTED | IS_BELOW_SELECTED];
mBackgrounds[IS_BOTTOM] = R.drawable.calname_bottom_unselected;
mBackgrounds[IS_BOTTOM | IS_BELOW_SELECTED] =
R.drawable.calname_bottom_unselected_underselect;
mBackgrounds[IS_TOP | IS_BOTTOM | IS_BELOW_SELECTED] = mBackgrounds[IS_BOTTOM
| IS_BELOW_SELECTED];
mBackgrounds[IS_TOP | IS_BOTTOM] = mBackgrounds[IS_BOTTOM | IS_BELOW_SELECTED];
mBackgrounds[IS_BELOW_SELECTED] = R.drawable.calname_unselected_underselect;
mBackgrounds[IS_TOP | IS_BELOW_SELECTED] = mBackgrounds[IS_BELOW_SELECTED];
mBackgrounds[IS_TOP] = mBackgrounds[IS_BELOW_SELECTED];
return mBackgrounds;
}
}
private void initData(Cursor c) {
if (mCursor != null && c != mCursor) {
mCursor.close();
@ -385,4 +320,64 @@ public class SelectCalendarsSimpleAdapter extends BaseAdapter implements ListAda
public void onCalendarColorsLoaded() {
notifyDataSetChanged();
}
private static class TabletCalendarItemBackgrounds {
static private int[] mBackgrounds = null;
/**
* Sets up the background drawables for the calendars list
*
* @param res The context's resources
*/
static int[] getBackgrounds() {
// Not thread safe. Ok if called only from main thread
if (mBackgrounds != null) {
return mBackgrounds;
}
mBackgrounds = new int[16];
mBackgrounds[0] = R.drawable.calname_unselected;
mBackgrounds[IS_SELECTED] = R.drawable.calname_select_underunselected;
mBackgrounds[IS_SELECTED | IS_BOTTOM] =
R.drawable.calname_bottom_select_underunselected;
mBackgrounds[IS_SELECTED | IS_BOTTOM | IS_BELOW_SELECTED] =
R.drawable.calname_bottom_select_underselect;
mBackgrounds[IS_SELECTED | IS_TOP | IS_BOTTOM | IS_BELOW_SELECTED] = mBackgrounds[
IS_SELECTED | IS_BOTTOM | IS_BELOW_SELECTED];
mBackgrounds[IS_SELECTED | IS_TOP | IS_BOTTOM] = mBackgrounds[IS_SELECTED | IS_BOTTOM
| IS_BELOW_SELECTED];
mBackgrounds[IS_SELECTED | IS_BELOW_SELECTED] = R.drawable.calname_select_underselect;
mBackgrounds[IS_SELECTED | IS_TOP | IS_BELOW_SELECTED] = mBackgrounds[IS_SELECTED
| IS_BELOW_SELECTED];
mBackgrounds[IS_SELECTED | IS_TOP] = mBackgrounds[IS_SELECTED | IS_BELOW_SELECTED];
mBackgrounds[IS_BOTTOM] = R.drawable.calname_bottom_unselected;
mBackgrounds[IS_BOTTOM | IS_BELOW_SELECTED] =
R.drawable.calname_bottom_unselected_underselect;
mBackgrounds[IS_TOP | IS_BOTTOM | IS_BELOW_SELECTED] = mBackgrounds[IS_BOTTOM
| IS_BELOW_SELECTED];
mBackgrounds[IS_TOP | IS_BOTTOM] = mBackgrounds[IS_BOTTOM | IS_BELOW_SELECTED];
mBackgrounds[IS_BELOW_SELECTED] = R.drawable.calname_unselected_underselect;
mBackgrounds[IS_TOP | IS_BELOW_SELECTED] = mBackgrounds[IS_BELOW_SELECTED];
mBackgrounds[IS_TOP] = mBackgrounds[IS_BELOW_SELECTED];
return mBackgrounds;
}
}
private class CalendarRow {
long id;
String displayName;
String ownerAccount;
String accountName;
String accountType;
int color;
boolean selected;
}
}

View File

@ -36,54 +36,38 @@ import android.widget.ListAdapter;
import android.widget.TextView;
import com.android.calendar.CalendarColorPickerDialog;
import org.sufficientlysecure.standalonecalendar.R;
import com.android.calendar.Utils;
import com.android.calendar.selectcalendars.CalendarColorCache.OnCalendarColorsLoadedListener;
import java.util.HashMap;
import ws.xsoh.etar.R;
public class SelectCalendarsSyncAdapter extends BaseAdapter
implements ListAdapter, AdapterView.OnItemClickListener, OnCalendarColorsLoadedListener {
private static final String TAG = "SelCalsAdapter";
private static final String COLOR_PICKER_DIALOG_TAG = "ColorPickerDialog";
private static final int LAYOUT = R.layout.calendar_sync_item;
private static int COLOR_CHIP_SIZE = 30;
private final String mSyncedString;
private final String mNotSyncedString;
private RectShape r = new RectShape();
private CalendarColorPickerDialog mColorPickerDialog;
private CalendarColorCache mCache;
private LayoutInflater mInflater;
private static final int LAYOUT = R.layout.calendar_sync_item;
private CalendarRow[] mData;
private HashMap<Long, CalendarRow> mChanges = new HashMap<Long, CalendarRow>();
private int mRowCount = 0;
private int mIdColumn;
private int mNameColumn;
private int mColorColumn;
private int mSyncedColumn;
private int mAccountNameColumn;
private int mAccountTypeColumn;
private boolean mIsTablet;
private FragmentManager mFragmentManager;
private int mColorViewTouchAreaIncrease;
private final String mSyncedString;
private final String mNotSyncedString;
public class CalendarRow {
long id;
String displayName;
int color;
boolean synced;
boolean originalSynced;
String accountName;
String accountType;
}
public SelectCalendarsSyncAdapter(Context context, Cursor c, FragmentManager manager) {
super();
initData(c);
@ -102,6 +86,14 @@ public class SelectCalendarsSyncAdapter extends BaseAdapter
mNotSyncedString = res.getString(R.string.not_synced);
}
private static void setText(View view, int id, String text) {
if (TextUtils.isEmpty(text)) {
return;
}
TextView textView = (TextView) view.findViewById(id);
textView.setText(text);
}
private void initData(Cursor c) {
if (c == null) {
mRowCount = 0;
@ -217,14 +209,6 @@ public class SelectCalendarsSyncAdapter extends BaseAdapter
return mCache.hasColors(mData[position].accountName, mData[position].accountType);
}
private static void setText(View view, int id, String text) {
if (TextUtils.isEmpty(text)) {
return;
}
TextView textView = (TextView) view.findViewById(id);
textView.setText(text);
}
@Override
public int getCount() {
return mRowCount;
@ -285,4 +269,14 @@ public class SelectCalendarsSyncAdapter extends BaseAdapter
public void onCalendarColorsLoaded() {
notifyDataSetChanged();
}
public class CalendarRow {
long id;
String displayName;
int color;
boolean synced;
boolean originalSynced;
String accountName;
String accountType;
}
}

View File

@ -42,12 +42,13 @@ import android.widget.ListAdapter;
import android.widget.TextView;
import com.android.calendar.AsyncQueryService;
import org.sufficientlysecure.standalonecalendar.R;
import com.android.calendar.Utils;
import com.android.calendar.selectcalendars.SelectCalendarsSyncAdapter.CalendarRow;
import java.util.HashMap;
import ws.xsoh.etar.R;
public class SelectCalendarsSyncFragment extends ListFragment
implements View.OnClickListener, LoaderManager.LoaderCallbacks<Cursor> {
@ -69,11 +70,10 @@ public class SelectCalendarsSyncFragment extends ListFragment
Calendars.ACCOUNT_NAME,
Calendars.ACCOUNT_TYPE,
"(" + Calendars.ACCOUNT_NAME + "=" + Calendars.OWNER_ACCOUNT + ") AS " + IS_PRIMARY, };
private final String[] mArgs = new String[2];
private TextView mSyncStatus;
private Button mAccountsButton;
private Account mAccount;
private final String[] mArgs = new String[2];
private AsyncQueryService mService;
private Handler mHandler = new Handler();
private ContentObserver mCalendarsObserver = new ContentObserver(mHandler) {

View File

@ -28,18 +28,16 @@ import android.view.MenuItem;
import android.view.View;
import android.widget.ExpandableListView;
import org.sufficientlysecure.standalonecalendar.R;
import com.android.calendar.Utils;
import ws.xsoh.etar.R;
public class SelectSyncedCalendarsMultiAccountActivity extends ExpandableListActivity
implements View.OnClickListener {
private static final String TAG = "Calendar";
private static final String EXPANDED_KEY = "is_expanded";
private static final String ACCOUNT_UNIQUE_KEY = "ACCOUNT_KEY";
private MatrixCursor mAccountsCursor = null;
private ExpandableListView mList;
private SelectSyncedCalendarsMultiAccountAdapter mAdapter;
private static final String[] PROJECTION = new String[] {
Calendars._ID,
Calendars.ACCOUNT_TYPE,
@ -47,6 +45,9 @@ public class SelectSyncedCalendarsMultiAccountActivity extends ExpandableListAct
Calendars.ACCOUNT_TYPE + " || " + Calendars.ACCOUNT_NAME + " AS " +
ACCOUNT_UNIQUE_KEY,
};
private MatrixCursor mAccountsCursor = null;
private ExpandableListView mList;
private SelectSyncedCalendarsMultiAccountAdapter mAdapter;
@Override
protected void onCreate(Bundle icicle) {

View File

@ -42,7 +42,6 @@ import android.widget.CursorTreeAdapter;
import android.widget.TextView;
import com.android.calendar.CalendarColorPickerDialog;
import org.sufficientlysecure.standalonecalendar.R;
import com.android.calendar.Utils;
import com.android.calendar.selectcalendars.CalendarColorCache.OnCalendarColorsLoadedListener;
@ -50,6 +49,8 @@ import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import ws.xsoh.etar.R;
public class SelectSyncedCalendarsMultiAccountAdapter extends CursorTreeAdapter implements
View.OnClickListener, OnCalendarColorsLoadedListener {
@ -61,56 +62,13 @@ public class SelectSyncedCalendarsMultiAccountAdapter extends CursorTreeAdapter
+ Calendars.CALENDAR_DISPLAY_NAME + " COLLATE NOCASE";
private static final String ACCOUNT_SELECTION = Calendars.ACCOUNT_NAME + "=?"
+ " AND " + Calendars.ACCOUNT_TYPE + "=?";
private final LayoutInflater mInflater;
private final ContentResolver mResolver;
private final SelectSyncedCalendarsMultiAccountActivity mActivity;
private final FragmentManager mFragmentManager;
private final boolean mIsTablet;
private CalendarColorPickerDialog mColorPickerDialog;
private final View mView;
private final static Runnable mStopRefreshing = new Runnable() {
@Override
public void run() {
mRefresh = false;
}
};
private Map<String, AuthenticatorDescription> mTypeToAuthDescription
= new HashMap<String, AuthenticatorDescription>();
protected AuthenticatorDescription[] mAuthDescs;
// These track changes to the synced state of calendars
private Map<Long, Boolean> mCalendarChanges
= new HashMap<Long, Boolean>();
private Map<Long, Boolean> mCalendarInitialStates
= new HashMap<Long, Boolean>();
// Flag for when the cursors have all been closed to ensure no race condition with queries.
private boolean mClosedCursorsFlag;
// This is for keeping MatrixCursor copies so that we can requery in the background.
private Map<String, Cursor> mChildrenCursors
= new HashMap<String, Cursor>();
private AsyncCalendarsUpdater mCalendarsUpdater;
// This is to keep our update tokens separate from other tokens. Since we cancel old updates
// when a new update comes in, we'd like to leave a token space that won't be canceled.
private static final int MIN_UPDATE_TOKEN = 1000;
private static int mUpdateToken = MIN_UPDATE_TOKEN;
// How long to wait between requeries of the calendars to see if anything has changed.
private static final int REFRESH_DELAY = 5000;
// How long to keep refreshing for
private static final int REFRESH_DURATION = 60000;
private static boolean mRefresh = true;
private static String mSyncedText;
private static String mNotSyncedText;
// This is to keep track of whether or not multiple calendars have the same display name
private static HashMap<String, Boolean> mIsDuplicateName = new HashMap<String, Boolean>();
private int mColorViewTouchAreaIncrease;
private static final String[] PROJECTION = new String[] {
Calendars._ID,
Calendars.ACCOUNT_NAME,
@ -132,91 +90,46 @@ public class SelectSyncedCalendarsMultiAccountAdapter extends CursorTreeAdapter
private static final int SYNCED_COLUMN = 6;
private static final int PRIMARY_COLUMN = 7;
private static final int ACCOUNT_TYPE_COLUMN = 8;
private static final int TAG_ID_CALENDAR_ID = R.id.calendar;
private static final int TAG_ID_SYNC_CHECKBOX = R.id.sync;
private static int mUpdateToken = MIN_UPDATE_TOKEN;
private static boolean mRefresh = true;
private final static Runnable mStopRefreshing = new Runnable() {
@Override
public void run() {
mRefresh = false;
}
};
private static String mSyncedText;
private static String mNotSyncedText;
// This is to keep track of whether or not multiple calendars have the same display name
private static HashMap<String, Boolean> mIsDuplicateName = new HashMap<String, Boolean>();
private final LayoutInflater mInflater;
private final ContentResolver mResolver;
private final SelectSyncedCalendarsMultiAccountActivity mActivity;
private final FragmentManager mFragmentManager;
private final boolean mIsTablet;
private final View mView;
protected AuthenticatorDescription[] mAuthDescs;
private CalendarColorPickerDialog mColorPickerDialog;
private Map<String, AuthenticatorDescription> mTypeToAuthDescription
= new HashMap<String, AuthenticatorDescription>();
// These track changes to the synced state of calendars
private Map<Long, Boolean> mCalendarChanges
= new HashMap<Long, Boolean>();
private Map<Long, Boolean> mCalendarInitialStates
= new HashMap<Long, Boolean>();
// Flag for when the cursors have all been closed to ensure no race condition with queries.
private boolean mClosedCursorsFlag;
// This is for keeping MatrixCursor copies so that we can requery in the background.
private Map<String, Cursor> mChildrenCursors
= new HashMap<String, Cursor>();
private AsyncCalendarsUpdater mCalendarsUpdater;
private int mColorViewTouchAreaIncrease;
private CalendarColorCache mCache;
private class AsyncCalendarsUpdater extends AsyncQueryHandler {
public AsyncCalendarsUpdater(ContentResolver cr) {
super(cr);
}
@Override
protected void onQueryComplete(int token, Object cookie, Cursor cursor) {
if(cursor == null) {
return;
}
synchronized(mChildrenCursors) {
if (mClosedCursorsFlag || (mActivity != null && mActivity.isFinishing())) {
cursor.close();
return;
}
}
Cursor currentCursor = mChildrenCursors.get(cookie);
// Check if the new cursor has the same content as our old cursor
if (currentCursor != null) {
if (Utils.compareCursors(currentCursor, cursor)) {
cursor.close();
return;
}
}
// If not then make a new matrix cursor for our Map
MatrixCursor newCursor = Utils.matrixCursorFromCursor(cursor);
cursor.close();
// And update our list of duplicated names
Utils.checkForDuplicateNames(mIsDuplicateName, newCursor, NAME_COLUMN);
mChildrenCursors.put((String)cookie, newCursor);
try {
setChildrenCursor(token, newCursor);
} catch (NullPointerException e) {
Log.w(TAG, "Adapter expired, try again on the next query: " + e);
}
// Clean up our old cursor if we had one. We have to do this after setting the new
// cursor so that our view doesn't throw on an invalid cursor.
if (currentCursor != null) {
currentCursor.close();
}
}
}
/**
* Method for changing the sync state when a calendar's button is pressed.
*
* This gets called when the CheckBox for a calendar is clicked. It toggles
* the sync state for the associated calendar and saves a change of state to
* a hashmap. It also compares against the original value and removes any
* changes from the hashmap if this is back at its initial state.
*/
@Override
public void onClick(View v) {
long id = (Long) v.getTag(TAG_ID_CALENDAR_ID);
boolean newState;
boolean initialState = mCalendarInitialStates.get(id);
if (mCalendarChanges.containsKey(id)) {
// Negate to reflect the click
newState = !mCalendarChanges.get(id);
} else {
// Negate to reflect the click
newState = !initialState;
}
if (newState == initialState) {
mCalendarChanges.remove(id);
} else {
mCalendarChanges.put(id, newState);
}
((CheckBox) v.getTag(TAG_ID_SYNC_CHECKBOX)).setChecked(newState);
setText(v, R.id.status, newState ? mSyncedText : mNotSyncedText);
}
public SelectSyncedCalendarsMultiAccountAdapter(Context context, Cursor acctsCursor,
SelectSyncedCalendarsMultiAccountActivity act) {
SelectSyncedCalendarsMultiAccountActivity act) {
super(acctsCursor, context);
mSyncedText = context.getString(R.string.synced);
mNotSyncedText = context.getString(R.string.not_synced);
@ -251,6 +164,45 @@ public class SelectSyncedCalendarsMultiAccountAdapter extends CursorTreeAdapter
.getDimensionPixelSize(R.dimen.color_view_touch_area_increase);
}
private static void setText(View view, int id, String text) {
if (TextUtils.isEmpty(text)) {
return;
}
TextView textView = (TextView) view.findViewById(id);
textView.setText(text);
}
/**
* Method for changing the sync state when a calendar's button is pressed.
*
* This gets called when the CheckBox for a calendar is clicked. It toggles
* the sync state for the associated calendar and saves a change of state to
* a hashmap. It also compares against the original value and removes any
* changes from the hashmap if this is back at its initial state.
*/
@Override
public void onClick(View v) {
long id = (Long) v.getTag(TAG_ID_CALENDAR_ID);
boolean newState;
boolean initialState = mCalendarInitialStates.get(id);
if (mCalendarChanges.containsKey(id)) {
// Negate to reflect the click
newState = !mCalendarChanges.get(id);
} else {
// Negate to reflect the click
newState = !initialState;
}
if (newState == initialState) {
mCalendarChanges.remove(id);
} else {
mCalendarChanges.put(id, newState);
}
((CheckBox) v.getTag(TAG_ID_SYNC_CHECKBOX)).setChecked(newState);
setText(v, R.id.status, newState ? mSyncedText : mNotSyncedText);
}
public void startRefreshStopDelay() {
mRefresh = true;
mView.postDelayed(mStopRefreshing, REFRESH_DURATION);
@ -287,14 +239,6 @@ public class SelectSyncedCalendarsMultiAccountAdapter extends CursorTreeAdapter
}
}
private static void setText(View view, int id, String text) {
if (TextUtils.isEmpty(text)) {
return;
}
TextView textView = (TextView) view.findViewById(id);
textView.setText(text);
}
/**
* Gets the label associated with a particular account type. If none found, return null.
* @param accountType the type of account
@ -434,6 +378,57 @@ public class SelectSyncedCalendarsMultiAccountAdapter extends CursorTreeAdapter
}
}
@Override
public void onCalendarColorsLoaded() {
notifyDataSetChanged();
}
private class AsyncCalendarsUpdater extends AsyncQueryHandler {
public AsyncCalendarsUpdater(ContentResolver cr) {
super(cr);
}
@Override
protected void onQueryComplete(int token, Object cookie, Cursor cursor) {
if (cursor == null) {
return;
}
synchronized (mChildrenCursors) {
if (mClosedCursorsFlag || (mActivity != null && mActivity.isFinishing())) {
cursor.close();
return;
}
}
Cursor currentCursor = mChildrenCursors.get(cookie);
// Check if the new cursor has the same content as our old cursor
if (currentCursor != null) {
if (Utils.compareCursors(currentCursor, cursor)) {
cursor.close();
return;
}
}
// If not then make a new matrix cursor for our Map
MatrixCursor newCursor = Utils.matrixCursorFromCursor(cursor);
cursor.close();
// And update our list of duplicated names
Utils.checkForDuplicateNames(mIsDuplicateName, newCursor, NAME_COLUMN);
mChildrenCursors.put((String) cookie, newCursor);
try {
setChildrenCursor(token, newCursor);
} catch (NullPointerException e) {
Log.w(TAG, "Adapter expired, try again on the next query: " + e);
}
// Clean up our old cursor if we had one. We have to do this after setting the new
// cursor so that our view doesn't throw on an invalid cursor.
if (currentCursor != null) {
currentCursor.close();
}
}
}
private class RefreshCalendars implements Runnable {
int mToken;
@ -462,9 +457,4 @@ public class SelectSyncedCalendarsMultiAccountAdapter extends CursorTreeAdapter
CALENDARS_ORDERBY);
}
}
@Override
public void onCalendarColorsLoaded() {
notifyDataSetChanged();
}
}

View File

@ -33,7 +33,7 @@ import com.android.calendar.CalendarController.EventType;
import com.android.calendar.CalendarController.ViewType;
import com.android.calendar.Utils;
import org.sufficientlysecure.standalonecalendar.R;
import ws.xsoh.etar.R;
public class SelectVisibleCalendarsActivity extends AbstractCalendarActivity {
private SelectVisibleCalendarsFragment mFragment;

View File

@ -34,10 +34,11 @@ import com.android.calendar.AsyncQueryService;
import com.android.calendar.CalendarController;
import com.android.calendar.CalendarController.EventInfo;
import com.android.calendar.CalendarController.EventType;
import org.sufficientlysecure.standalonecalendar.R;
import com.android.calendar.Utils;
import com.android.calendar.selectcalendars.CalendarColorCache.OnCalendarColorsLoadedListener;
import ws.xsoh.etar.R;
public class SelectVisibleCalendarsFragment extends Fragment
implements AdapterView.OnItemClickListener, CalendarController.EventHandler,
OnCalendarColorsLoadedListener {

View File

@ -16,9 +16,6 @@
package com.android.calendar.widget;
import org.sufficientlysecure.standalonecalendar.R;
import com.android.calendar.Utils;
import android.content.Context;
import android.database.Cursor;
import android.text.TextUtils;
@ -28,17 +25,208 @@ import android.text.format.Time;
import android.util.Log;
import android.view.View;
import com.android.calendar.Utils;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.TimeZone;
import ws.xsoh.etar.R;
class CalendarAppWidgetModel {
private static final String TAG = CalendarAppWidgetModel.class.getSimpleName();
private static final boolean LOGD = false;
final List<RowInfo> mRowInfos;
final List<EventInfo> mEventInfos;
final List<DayInfo> mDayInfos;
final Context mContext;
final long mNow;
final int mTodayJulianDay;
final int mMaxJulianDay;
private String mHomeTZName;
private boolean mShowTZ;
public CalendarAppWidgetModel(Context context, String timeZone) {
mNow = System.currentTimeMillis();
Time time = new Time(timeZone);
time.setToNow(); // This is needed for gmtoff to be set
mTodayJulianDay = Time.getJulianDay(mNow, time.gmtoff);
mMaxJulianDay = mTodayJulianDay + CalendarAppWidgetService.MAX_DAYS - 1;
mEventInfos = new ArrayList<EventInfo>(50);
mRowInfos = new ArrayList<RowInfo>(50);
mDayInfos = new ArrayList<DayInfo>(8);
mContext = context;
}
public void buildFromCursor(Cursor cursor, String timeZone) {
final Time recycle = new Time(timeZone);
final ArrayList<LinkedList<RowInfo>> mBuckets =
new ArrayList<LinkedList<RowInfo>>(CalendarAppWidgetService.MAX_DAYS);
for (int i = 0; i < CalendarAppWidgetService.MAX_DAYS; i++) {
mBuckets.add(new LinkedList<RowInfo>());
}
recycle.setToNow();
mShowTZ = !TextUtils.equals(timeZone, Time.getCurrentTimezone());
if (mShowTZ) {
mHomeTZName = TimeZone.getTimeZone(timeZone).getDisplayName(recycle.isDst != 0,
TimeZone.SHORT);
}
cursor.moveToPosition(-1);
String tz = Utils.getTimeZone(mContext, null);
while (cursor.moveToNext()) {
final int rowId = cursor.getPosition();
final long eventId = cursor.getLong(CalendarAppWidgetService.INDEX_EVENT_ID);
final boolean allDay = cursor.getInt(CalendarAppWidgetService.INDEX_ALL_DAY) != 0;
long start = cursor.getLong(CalendarAppWidgetService.INDEX_BEGIN);
long end = cursor.getLong(CalendarAppWidgetService.INDEX_END);
final String title = cursor.getString(CalendarAppWidgetService.INDEX_TITLE);
final String location =
cursor.getString(CalendarAppWidgetService.INDEX_EVENT_LOCATION);
// we don't compute these ourselves because it seems to produce the
// wrong endDay for all day events
final int startDay = cursor.getInt(CalendarAppWidgetService.INDEX_START_DAY);
final int endDay = cursor.getInt(CalendarAppWidgetService.INDEX_END_DAY);
final int color = cursor.getInt(CalendarAppWidgetService.INDEX_COLOR);
final int selfStatus = cursor
.getInt(CalendarAppWidgetService.INDEX_SELF_ATTENDEE_STATUS);
// Adjust all-day times into local timezone
if (allDay) {
start = Utils.convertAlldayUtcToLocal(recycle, start, tz);
end = Utils.convertAlldayUtcToLocal(recycle, end, tz);
}
if (LOGD) {
Log.d(TAG, "Row #" + rowId + " allDay:" + allDay + " start:" + start
+ " end:" + end + " eventId:" + eventId);
}
// we might get some extra events when querying, in order to
// deal with all-day events
if (end < mNow) {
continue;
}
int i = mEventInfos.size();
mEventInfos.add(populateEventInfo(eventId, allDay, start, end, startDay, endDay, title,
location, color, selfStatus));
// populate the day buckets that this event falls into
int from = Math.max(startDay, mTodayJulianDay);
int to = Math.min(endDay, mMaxJulianDay);
for (int day = from; day <= to; day++) {
LinkedList<RowInfo> bucket = mBuckets.get(day - mTodayJulianDay);
RowInfo rowInfo = new RowInfo(RowInfo.TYPE_MEETING, i);
if (allDay) {
bucket.addFirst(rowInfo);
} else {
bucket.add(rowInfo);
}
}
}
int day = mTodayJulianDay;
int count = 0;
for (LinkedList<RowInfo> bucket : mBuckets) {
if (!bucket.isEmpty()) {
// We don't show day header in today
if (day != mTodayJulianDay) {
final DayInfo dayInfo = populateDayInfo(day, recycle);
// Add the day header
final int dayIndex = mDayInfos.size();
mDayInfos.add(dayInfo);
mRowInfos.add(new RowInfo(RowInfo.TYPE_DAY, dayIndex));
}
// Add the event row infos
mRowInfos.addAll(bucket);
count += bucket.size();
}
day++;
if (count >= CalendarAppWidgetService.EVENT_MIN_COUNT) {
break;
}
}
}
private EventInfo populateEventInfo(long eventId, boolean allDay, long start, long end,
int startDay, int endDay, String title, String location, int color, int selfStatus) {
EventInfo eventInfo = new EventInfo();
// Compute a human-readable string for the start time of the event
StringBuilder whenString = new StringBuilder();
int visibWhen;
int flags = DateUtils.FORMAT_ABBREV_ALL;
visibWhen = View.VISIBLE;
if (allDay) {
flags |= DateUtils.FORMAT_SHOW_DATE;
whenString.append(Utils.formatDateRange(mContext, start, end, flags));
} else {
flags |= DateUtils.FORMAT_SHOW_TIME;
if (DateFormat.is24HourFormat(mContext)) {
flags |= DateUtils.FORMAT_24HOUR;
}
if (endDay > startDay) {
flags |= DateUtils.FORMAT_SHOW_DATE;
}
whenString.append(Utils.formatDateRange(mContext, start, end, flags));
if (mShowTZ) {
whenString.append(" ").append(mHomeTZName);
}
}
eventInfo.id = eventId;
eventInfo.start = start;
eventInfo.end = end;
eventInfo.allDay = allDay;
eventInfo.when = whenString.toString();
eventInfo.visibWhen = visibWhen;
eventInfo.color = color;
eventInfo.selfAttendeeStatus = selfStatus;
// What
if (TextUtils.isEmpty(title)) {
eventInfo.title = mContext.getString(R.string.no_title_label);
} else {
eventInfo.title = title;
}
eventInfo.visibTitle = View.VISIBLE;
// Where
if (!TextUtils.isEmpty(location)) {
eventInfo.visibWhere = View.VISIBLE;
eventInfo.where = location;
} else {
eventInfo.visibWhere = View.GONE;
}
return eventInfo;
}
private DayInfo populateDayInfo(int julianDay, Time recycle) {
long millis = recycle.setJulianDay(julianDay);
int flags = DateUtils.FORMAT_ABBREV_ALL | DateUtils.FORMAT_SHOW_DATE;
String label;
if (julianDay == mTodayJulianDay + 1) {
label = mContext.getString(R.string.agenda_tomorrow,
Utils.formatDateRange(mContext, millis, millis, flags).toString());
} else {
flags |= DateUtils.FORMAT_SHOW_WEEKDAY;
label = Utils.formatDateRange(mContext, millis, millis, flags);
}
return new DayInfo(julianDay, label);
}
@Override
public String toString() {
StringBuilder builder = new StringBuilder();
builder.append("\nCalendarAppWidgetModel [eventInfos=");
builder.append(mEventInfos);
builder.append("]");
return builder.toString();
}
/**
* {@link RowInfo} is a class that represents a single row in the widget. It
* is actually only a pointer to either a {@link DayInfo} or an
@ -50,7 +238,7 @@ class CalendarAppWidgetModel {
static final int TYPE_MEETING = 1;
/**
* mType is either a day header (TYPE_DAY) or an event (TYPE_MEETING)
* mType is either a day header (TYPE_DAY) or an event (TYPE_MEETING)
*/
final int mType;
@ -194,10 +382,14 @@ class CalendarAppWidgetModel {
*/
static class DayInfo {
/** The Julian day */
/**
* The Julian day
*/
final int mJulianDay;
/** The string representation of this day header, to be displayed */
/**
* The string representation of this day header, to be displayed
*/
final String mDayLabel;
DayInfo(int julianDay, String label) {
@ -239,192 +431,4 @@ class CalendarAppWidgetModel {
}
}
final List<RowInfo> mRowInfos;
final List<EventInfo> mEventInfos;
final List<DayInfo> mDayInfos;
final Context mContext;
final long mNow;
final int mTodayJulianDay;
final int mMaxJulianDay;
public CalendarAppWidgetModel(Context context, String timeZone) {
mNow = System.currentTimeMillis();
Time time = new Time(timeZone);
time.setToNow(); // This is needed for gmtoff to be set
mTodayJulianDay = Time.getJulianDay(mNow, time.gmtoff);
mMaxJulianDay = mTodayJulianDay + CalendarAppWidgetService.MAX_DAYS - 1;
mEventInfos = new ArrayList<EventInfo>(50);
mRowInfos = new ArrayList<RowInfo>(50);
mDayInfos = new ArrayList<DayInfo>(8);
mContext = context;
}
public void buildFromCursor(Cursor cursor, String timeZone) {
final Time recycle = new Time(timeZone);
final ArrayList<LinkedList<RowInfo>> mBuckets =
new ArrayList<LinkedList<RowInfo>>(CalendarAppWidgetService.MAX_DAYS);
for (int i = 0; i < CalendarAppWidgetService.MAX_DAYS; i++) {
mBuckets.add(new LinkedList<RowInfo>());
}
recycle.setToNow();
mShowTZ = !TextUtils.equals(timeZone, Time.getCurrentTimezone());
if (mShowTZ) {
mHomeTZName = TimeZone.getTimeZone(timeZone).getDisplayName(recycle.isDst != 0,
TimeZone.SHORT);
}
cursor.moveToPosition(-1);
String tz = Utils.getTimeZone(mContext, null);
while (cursor.moveToNext()) {
final int rowId = cursor.getPosition();
final long eventId = cursor.getLong(CalendarAppWidgetService.INDEX_EVENT_ID);
final boolean allDay = cursor.getInt(CalendarAppWidgetService.INDEX_ALL_DAY) != 0;
long start = cursor.getLong(CalendarAppWidgetService.INDEX_BEGIN);
long end = cursor.getLong(CalendarAppWidgetService.INDEX_END);
final String title = cursor.getString(CalendarAppWidgetService.INDEX_TITLE);
final String location =
cursor.getString(CalendarAppWidgetService.INDEX_EVENT_LOCATION);
// we don't compute these ourselves because it seems to produce the
// wrong endDay for all day events
final int startDay = cursor.getInt(CalendarAppWidgetService.INDEX_START_DAY);
final int endDay = cursor.getInt(CalendarAppWidgetService.INDEX_END_DAY);
final int color = cursor.getInt(CalendarAppWidgetService.INDEX_COLOR);
final int selfStatus = cursor
.getInt(CalendarAppWidgetService.INDEX_SELF_ATTENDEE_STATUS);
// Adjust all-day times into local timezone
if (allDay) {
start = Utils.convertAlldayUtcToLocal(recycle, start, tz);
end = Utils.convertAlldayUtcToLocal(recycle, end, tz);
}
if (LOGD) {
Log.d(TAG, "Row #" + rowId + " allDay:" + allDay + " start:" + start
+ " end:" + end + " eventId:" + eventId);
}
// we might get some extra events when querying, in order to
// deal with all-day events
if (end < mNow) {
continue;
}
int i = mEventInfos.size();
mEventInfos.add(populateEventInfo(eventId, allDay, start, end, startDay, endDay, title,
location, color, selfStatus));
// populate the day buckets that this event falls into
int from = Math.max(startDay, mTodayJulianDay);
int to = Math.min(endDay, mMaxJulianDay);
for (int day = from; day <= to; day++) {
LinkedList<RowInfo> bucket = mBuckets.get(day - mTodayJulianDay);
RowInfo rowInfo = new RowInfo(RowInfo.TYPE_MEETING, i);
if (allDay) {
bucket.addFirst(rowInfo);
} else {
bucket.add(rowInfo);
}
}
}
int day = mTodayJulianDay;
int count = 0;
for (LinkedList<RowInfo> bucket : mBuckets) {
if (!bucket.isEmpty()) {
// We don't show day header in today
if (day != mTodayJulianDay) {
final DayInfo dayInfo = populateDayInfo(day, recycle);
// Add the day header
final int dayIndex = mDayInfos.size();
mDayInfos.add(dayInfo);
mRowInfos.add(new RowInfo(RowInfo.TYPE_DAY, dayIndex));
}
// Add the event row infos
mRowInfos.addAll(bucket);
count += bucket.size();
}
day++;
if (count >= CalendarAppWidgetService.EVENT_MIN_COUNT) {
break;
}
}
}
private EventInfo populateEventInfo(long eventId, boolean allDay, long start, long end,
int startDay, int endDay, String title, String location, int color, int selfStatus) {
EventInfo eventInfo = new EventInfo();
// Compute a human-readable string for the start time of the event
StringBuilder whenString = new StringBuilder();
int visibWhen;
int flags = DateUtils.FORMAT_ABBREV_ALL;
visibWhen = View.VISIBLE;
if (allDay) {
flags |= DateUtils.FORMAT_SHOW_DATE;
whenString.append(Utils.formatDateRange(mContext, start, end, flags));
} else {
flags |= DateUtils.FORMAT_SHOW_TIME;
if (DateFormat.is24HourFormat(mContext)) {
flags |= DateUtils.FORMAT_24HOUR;
}
if (endDay > startDay) {
flags |= DateUtils.FORMAT_SHOW_DATE;
}
whenString.append(Utils.formatDateRange(mContext, start, end, flags));
if (mShowTZ) {
whenString.append(" ").append(mHomeTZName);
}
}
eventInfo.id = eventId;
eventInfo.start = start;
eventInfo.end = end;
eventInfo.allDay = allDay;
eventInfo.when = whenString.toString();
eventInfo.visibWhen = visibWhen;
eventInfo.color = color;
eventInfo.selfAttendeeStatus = selfStatus;
// What
if (TextUtils.isEmpty(title)) {
eventInfo.title = mContext.getString(R.string.no_title_label);
} else {
eventInfo.title = title;
}
eventInfo.visibTitle = View.VISIBLE;
// Where
if (!TextUtils.isEmpty(location)) {
eventInfo.visibWhere = View.VISIBLE;
eventInfo.where = location;
} else {
eventInfo.visibWhere = View.GONE;
}
return eventInfo;
}
private DayInfo populateDayInfo(int julianDay, Time recycle) {
long millis = recycle.setJulianDay(julianDay);
int flags = DateUtils.FORMAT_ABBREV_ALL | DateUtils.FORMAT_SHOW_DATE;
String label;
if (julianDay == mTodayJulianDay + 1) {
label = mContext.getString(R.string.agenda_tomorrow,
Utils.formatDateRange(mContext, millis, millis, flags).toString());
} else {
flags |= DateUtils.FORMAT_SHOW_WEEKDAY;
label = Utils.formatDateRange(mContext, millis, millis, flags);
}
return new DayInfo(julianDay, label);
}
@Override
public String toString() {
StringBuilder builder = new StringBuilder();
builder.append("\nCalendarAppWidgetModel [eventInfos=");
builder.append(mEventInfos);
builder.append("]");
return builder.toString();
}
}

View File

@ -16,10 +16,6 @@
package com.android.calendar.widget;
import static android.provider.CalendarContract.EXTRA_EVENT_ALL_DAY;
import static android.provider.CalendarContract.EXTRA_EVENT_BEGIN_TIME;
import static android.provider.CalendarContract.EXTRA_EVENT_END_TIME;
import android.app.AlarmManager;
import android.app.PendingIntent;
import android.appwidget.AppWidgetManager;
@ -36,9 +32,14 @@ import android.widget.RemoteViews;
import com.android.calendar.AllInOneActivity;
import com.android.calendar.EventInfoActivity;
import org.sufficientlysecure.standalonecalendar.R;
import com.android.calendar.Utils;
import ws.xsoh.etar.R;
import static android.provider.CalendarContract.EXTRA_EVENT_ALL_DAY;
import static android.provider.CalendarContract.EXTRA_EVENT_BEGIN_TIME;
import static android.provider.CalendarContract.EXTRA_EVENT_END_TIME;
/**
* Simple widget to show next upcoming calendar event.
*/
@ -49,6 +50,77 @@ public class CalendarAppWidgetProvider extends AppWidgetProvider {
// TODO Move these to Calendar.java
static final String EXTRA_EVENT_IDS = "com.android.calendar.EXTRA_EVENT_IDS";
/**
* Build {@link ComponentName} describing this specific
* {@link AppWidgetProvider}
*/
static ComponentName getComponentName(Context context) {
return new ComponentName(context, CalendarAppWidgetProvider.class);
}
/**
* Build the {@link PendingIntent} used to trigger an update of all calendar
* widgets. Uses {@link Utils#getWidgetScheduledUpdateAction(Context)} to
* directly target all widgets instead of using
* {@link AppWidgetManager#EXTRA_APPWIDGET_IDS}.
*
* @param context Context to use when building broadcast.
*/
static PendingIntent getUpdateIntent(Context context) {
Intent intent = new Intent(Utils.getWidgetScheduledUpdateAction(context));
intent.setDataAndType(CalendarContract.CONTENT_URI, Utils.APPWIDGET_DATA_TYPE);
return PendingIntent.getBroadcast(context, 0 /* no requestCode */, intent,
0 /* no flags */);
}
/**
* Build a {@link PendingIntent} to launch the Calendar app. This should be used
* in combination with {@link RemoteViews#setPendingIntentTemplate(int, PendingIntent)}.
*/
static PendingIntent getLaunchPendingIntentTemplate(Context context) {
Intent launchIntent = new Intent();
launchIntent.setAction(Intent.ACTION_VIEW);
launchIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK |
Intent.FLAG_ACTIVITY_TASK_ON_HOME);
launchIntent.setClass(context, AllInOneActivity.class);
return PendingIntent.getActivity(context, 0 /* no requestCode */, launchIntent,
PendingIntent.FLAG_UPDATE_CURRENT);
}
/**
* Build an {@link Intent} available as FillInIntent to launch the Calendar app.
* This should be used in combination with
* {@link RemoteViews#setOnClickFillInIntent(int, Intent)}.
* If the go to time is 0, then calendar will be launched without a starting time.
*
* @param goToTime time that calendar should take the user to, or 0 to
* indicate no specific start time.
*/
static Intent getLaunchFillInIntent(Context context, long id, long start, long end,
boolean allDay) {
final Intent fillInIntent = new Intent();
String dataString = "content://com.android.calendar/events";
if (id != 0) {
fillInIntent.putExtra(Utils.INTENT_KEY_DETAIL_VIEW, true);
fillInIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK |
Intent.FLAG_ACTIVITY_TASK_ON_HOME);
dataString += "/" + id;
// If we have an event id - start the event info activity
fillInIntent.setClass(context, EventInfoActivity.class);
} else {
// If we do not have an event id - start AllInOne
fillInIntent.setClass(context, AllInOneActivity.class);
}
Uri data = Uri.parse(dataString);
fillInIntent.setData(data);
fillInIntent.putExtra(EXTRA_EVENT_BEGIN_TIME, start);
fillInIntent.putExtra(EXTRA_EVENT_END_TIME, end);
fillInIntent.putExtra(EXTRA_EVENT_ALL_DAY, allDay);
return fillInIntent;
}
/**
* {@inheritDoc}
*/
@ -95,15 +167,6 @@ public class CalendarAppWidgetProvider extends AppWidgetProvider {
performUpdate(context, appWidgetManager, appWidgetIds, null /* no eventIds */);
}
/**
* Build {@link ComponentName} describing this specific
* {@link AppWidgetProvider}
*/
static ComponentName getComponentName(Context context) {
return new ComponentName(context, CalendarAppWidgetProvider.class);
}
/**
* Process and push out an update for the given appWidgetIds. This call
* actually fires an intent to start {@link CalendarAppWidgetService} as a
@ -165,69 +228,6 @@ public class CalendarAppWidgetProvider extends AppWidgetProvider {
}
}
/**
* Build the {@link PendingIntent} used to trigger an update of all calendar
* widgets. Uses {@link Utils#getWidgetScheduledUpdateAction(Context)} to
* directly target all widgets instead of using
* {@link AppWidgetManager#EXTRA_APPWIDGET_IDS}.
*
* @param context Context to use when building broadcast.
*/
static PendingIntent getUpdateIntent(Context context) {
Intent intent = new Intent(Utils.getWidgetScheduledUpdateAction(context));
intent.setDataAndType(CalendarContract.CONTENT_URI, Utils.APPWIDGET_DATA_TYPE);
return PendingIntent.getBroadcast(context, 0 /* no requestCode */, intent,
0 /* no flags */);
}
/**
* Build a {@link PendingIntent} to launch the Calendar app. This should be used
* in combination with {@link RemoteViews#setPendingIntentTemplate(int, PendingIntent)}.
*/
static PendingIntent getLaunchPendingIntentTemplate(Context context) {
Intent launchIntent = new Intent();
launchIntent.setAction(Intent.ACTION_VIEW);
launchIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK |
Intent.FLAG_ACTIVITY_TASK_ON_HOME);
launchIntent.setClass(context, AllInOneActivity.class);
return PendingIntent.getActivity(context, 0 /* no requestCode */, launchIntent,
PendingIntent.FLAG_UPDATE_CURRENT);
}
/**
* Build an {@link Intent} available as FillInIntent to launch the Calendar app.
* This should be used in combination with
* {@link RemoteViews#setOnClickFillInIntent(int, Intent)}.
* If the go to time is 0, then calendar will be launched without a starting time.
*
* @param goToTime time that calendar should take the user to, or 0 to
* indicate no specific start time.
*/
static Intent getLaunchFillInIntent(Context context, long id, long start, long end,
boolean allDay) {
final Intent fillInIntent = new Intent();
String dataString = "content://com.android.calendar/events";
if (id != 0) {
fillInIntent.putExtra(Utils.INTENT_KEY_DETAIL_VIEW, true);
fillInIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK |
Intent.FLAG_ACTIVITY_TASK_ON_HOME);
dataString += "/" + id;
// If we have an event id - start the event info activity
fillInIntent.setClass(context, EventInfoActivity.class);
} else {
// If we do not have an event id - start AllInOne
fillInIntent.setClass(context, AllInOneActivity.class);
}
Uri data = Uri.parse(dataString);
fillInIntent.setData(data);
fillInIntent.putExtra(EXTRA_EVENT_BEGIN_TIME, start);
fillInIntent.putExtra(EXTRA_EVENT_END_TIME, end);
fillInIntent.putExtra(EXTRA_EVENT_ALL_DAY, allDay);
return fillInIntent;
}
// private static PendingIntent getNewEventPendingIntent(Context context) {
// Intent newEventIntent = new Intent(Intent.ACTION_EDIT);
// newEventIntent.setClass(context, EditEventActivity.class);

View File

@ -39,7 +39,6 @@ import android.view.View;
import android.widget.RemoteViews;
import android.widget.RemoteViewsService;
import org.sufficientlysecure.standalonecalendar.R;
import com.android.calendar.Utils;
import com.android.calendar.widget.CalendarAppWidgetModel.DayInfo;
import com.android.calendar.widget.CalendarAppWidgetModel.EventInfo;
@ -49,23 +48,14 @@ import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.atomic.AtomicInteger;
import ws.xsoh.etar.R;
public class CalendarAppWidgetService extends RemoteViewsService {
private static final String TAG = "CalendarWidget";
static final int EVENT_MIN_COUNT = 20;
static final int EVENT_MAX_COUNT = 100;
// Minimum delay between queries on the database for widget updates in ms
static final int WIDGET_UPDATE_THROTTLE = 500;
private static final String EVENT_SORT_ORDER = Instances.START_DAY + " ASC, "
+ Instances.START_MINUTE + " ASC, " + Instances.END_DAY + " ASC, "
+ Instances.END_MINUTE + " ASC LIMIT " + EVENT_MAX_COUNT;
private static final String EVENT_SELECTION = Calendars.VISIBLE + "=1";
private static final String EVENT_SELECTION_HIDE_DECLINED = Calendars.VISIBLE + "=1 AND "
+ Instances.SELF_ATTENDEE_STATUS + "!=" + Attendees.ATTENDEE_STATUS_DECLINED;
static final String[] EVENT_PROJECTION = new String[] {
Instances.ALL_DAY,
Instances.BEGIN,
@ -78,7 +68,6 @@ public class CalendarAppWidgetService extends RemoteViewsService {
Instances.DISPLAY_COLOR, // If SDK < 16, set to Instances.CALENDAR_COLOR.
Instances.SELF_ATTENDEE_STATUS,
};
static final int INDEX_ALL_DAY = 0;
static final int INDEX_BEGIN = 1;
static final int INDEX_END = 2;
@ -89,21 +78,49 @@ public class CalendarAppWidgetService extends RemoteViewsService {
static final int INDEX_END_DAY = 7;
static final int INDEX_COLOR = 8;
static final int INDEX_SELF_ATTENDEE_STATUS = 9;
static final int MAX_DAYS = 7;
private static final String TAG = "CalendarWidget";
private static final String EVENT_SORT_ORDER = Instances.START_DAY + " ASC, "
+ Instances.START_MINUTE + " ASC, " + Instances.END_DAY + " ASC, "
+ Instances.END_MINUTE + " ASC LIMIT " + EVENT_MAX_COUNT;
private static final String EVENT_SELECTION = Calendars.VISIBLE + "=1";
private static final String EVENT_SELECTION_HIDE_DECLINED = Calendars.VISIBLE + "=1 AND "
+ Instances.SELF_ATTENDEE_STATUS + "!=" + Attendees.ATTENDEE_STATUS_DECLINED;
private static final long SEARCH_DURATION = MAX_DAYS * DateUtils.DAY_IN_MILLIS;
/**
* Update interval used when no next-update calculated, or bad trigger time in past.
* Unit: milliseconds.
*/
private static final long UPDATE_TIME_NO_EVENTS = DateUtils.HOUR_IN_MILLIS * 6;
static {
if (!Utils.isJellybeanOrLater()) {
EVENT_PROJECTION[INDEX_COLOR] = Instances.CALENDAR_COLOR;
}
}
static final int MAX_DAYS = 7;
private static final long SEARCH_DURATION = MAX_DAYS * DateUtils.DAY_IN_MILLIS;
/**
* Update interval used when no next-update calculated, or bad trigger time in past.
* Unit: milliseconds.
* Format given time for debugging output.
*
* @param unixTime Target time to report.
* @param now Current system time from {@link System#currentTimeMillis()}
* for calculating time difference.
*/
private static final long UPDATE_TIME_NO_EVENTS = DateUtils.HOUR_IN_MILLIS * 6;
static String formatDebugTime(long unixTime, long now) {
Time time = new Time();
time.set(unixTime);
long delta = unixTime - now;
if (delta > DateUtils.MINUTE_IN_MILLIS) {
delta /= DateUtils.MINUTE_IN_MILLIS;
return String.format("[%d] %s (%+d mins)", unixTime,
time.format("%H:%M:%S"), delta);
} else {
delta /= DateUtils.SECOND_IN_MILLIS;
return String.format("[%d] %s (%+d secs)", unixTime,
time.format("%H:%M:%S"), delta);
}
}
@Override
public RemoteViewsFactory onGetViewFactory(Intent intent) {
@ -113,28 +130,21 @@ public class CalendarAppWidgetService extends RemoteViewsService {
public static class CalendarFactory extends BroadcastReceiver implements
RemoteViewsService.RemoteViewsFactory, Loader.OnLoadCompleteListener<Cursor> {
private static final boolean LOGD = false;
private static final AtomicInteger currentVersion = new AtomicInteger(0);
// Suppress unnecessary logging about update time. Need to be static as this object is
// re-instanciated frequently.
// TODO: It seems loadData() is called via onCreate() four times, which should mean
// unnecessary CalendarFactory object is created and dropped. It is not efficient.
private static long sLastUpdateTime = UPDATE_TIME_NO_EVENTS;
private Context mContext;
private Resources mResources;
private static CalendarAppWidgetModel mModel;
private static Object mLock = new Object();
private static volatile int mSerialNum = 0;
private final Handler mHandler = new Handler();
private final ExecutorService executor = Executors.newSingleThreadExecutor();
private Context mContext;
private Resources mResources;
private int mLastSerialNum = -1;
private CursorLoader mLoader;
private final Handler mHandler = new Handler();
private static final AtomicInteger currentVersion = new AtomicInteger(0);
private final ExecutorService executor = Executors.newSingleThreadExecutor();
private int mAppWidgetId;
private int mDeclinedColor;
private int mStandardColor;
private int mAllDayColor;
private final Runnable mTimezoneChanged = new Runnable() {
@Override
public void run() {
@ -143,6 +153,61 @@ public class CalendarAppWidgetService extends RemoteViewsService {
}
}
};
private int mAppWidgetId;
private int mDeclinedColor;
private int mStandardColor;
private int mAllDayColor;
protected CalendarFactory(Context context, Intent intent) {
mContext = context;
mResources = context.getResources();
mAppWidgetId = intent.getIntExtra(
AppWidgetManager.EXTRA_APPWIDGET_ID, AppWidgetManager.INVALID_APPWIDGET_ID);
mDeclinedColor = mResources.getColor(R.color.appwidget_item_declined_color);
mStandardColor = mResources.getColor(R.color.appwidget_item_standard_color);
mAllDayColor = mResources.getColor(R.color.appwidget_item_allday_color);
}
public CalendarFactory() {
// This is being created as part of onReceive
}
/* @VisibleForTesting */
protected static CalendarAppWidgetModel buildAppWidgetModel(
Context context, Cursor cursor, String timeZone) {
CalendarAppWidgetModel model = new CalendarAppWidgetModel(context, timeZone);
model.buildFromCursor(cursor, timeZone);
return model;
}
private static long getNextMidnightTimeMillis(String timezone) {
Time time = new Time();
time.setToNow();
time.monthDay++;
time.hour = 0;
time.minute = 0;
time.second = 0;
long midnightDeviceTz = time.normalize(true);
time.timezone = timezone;
time.setToNow();
time.monthDay++;
time.hour = 0;
time.minute = 0;
time.second = 0;
long midnightHomeTz = time.normalize(true);
return Math.min(midnightDeviceTz, midnightHomeTz);
}
static void updateTextView(RemoteViews views, int id, int visibility, String string) {
views.setViewVisibility(id, visibility);
if (visibility == View.VISIBLE) {
views.setTextViewText(id, string);
}
}
private Runnable createUpdateLoaderRunnable(final String selection,
final PendingResult result, final int version) {
@ -164,22 +229,6 @@ public class CalendarAppWidgetService extends RemoteViewsService {
};
}
protected CalendarFactory(Context context, Intent intent) {
mContext = context;
mResources = context.getResources();
mAppWidgetId = intent.getIntExtra(
AppWidgetManager.EXTRA_APPWIDGET_ID, AppWidgetManager.INVALID_APPWIDGET_ID);
mDeclinedColor = mResources.getColor(R.color.appwidget_item_declined_color);
mStandardColor = mResources.getColor(R.color.appwidget_item_standard_color);
mAllDayColor = mResources.getColor(R.color.appwidget_item_allday_color);
}
public CalendarFactory() {
// This is being created as part of onReceive
}
@Override
public void onCreate() {
String selection = queryForSelection();
@ -409,14 +458,6 @@ public class CalendarAppWidgetService extends RemoteViewsService {
return uri;
}
/* @VisibleForTesting */
protected static CalendarAppWidgetModel buildAppWidgetModel(
Context context, Cursor cursor, String timeZone) {
CalendarAppWidgetModel model = new CalendarAppWidgetModel(context, timeZone);
model.buildFromCursor(cursor, timeZone);
return model;
}
/**
* Calculates and returns the next time we should push widget updates.
*/
@ -439,33 +480,6 @@ public class CalendarAppWidgetService extends RemoteViewsService {
return minUpdateTime;
}
private static long getNextMidnightTimeMillis(String timezone) {
Time time = new Time();
time.setToNow();
time.monthDay++;
time.hour = 0;
time.minute = 0;
time.second = 0;
long midnightDeviceTz = time.normalize(true);
time.timezone = timezone;
time.setToNow();
time.monthDay++;
time.hour = 0;
time.minute = 0;
time.second = 0;
long midnightHomeTz = time.normalize(true);
return Math.min(midnightDeviceTz, midnightHomeTz);
}
static void updateTextView(RemoteViews views, int id, int visibility, String string) {
views.setViewVisibility(id, visibility);
if (visibility == View.VISIBLE) {
views.setTextViewText(id, string);
}
}
/*
* (non-Javadoc)
* @see
@ -599,27 +613,4 @@ public class CalendarAppWidgetService extends RemoteViewsService {
});
}
}
/**
* Format given time for debugging output.
*
* @param unixTime Target time to report.
* @param now Current system time from {@link System#currentTimeMillis()}
* for calculating time difference.
*/
static String formatDebugTime(long unixTime, long now) {
Time time = new Time();
time.set(unixTime);
long delta = unixTime - now;
if (delta > DateUtils.MINUTE_IN_MILLIS) {
delta /= DateUtils.MINUTE_IN_MILLIS;
return String.format("[%d] %s (%+d mins)", unixTime,
time.format("%H:%M:%S"), delta);
} else {
delta /= DateUtils.SECOND_IN_MILLIS;
return String.format("[%d] %s (%+d secs)", unixTime,
time.format("%H:%M:%S"), delta);
}
}
}