/ * *
* Copyright ( c ) 2012 Todoroo Inc
*
* See the file "LICENSE" for the full license governing this code .
* /
package com.todoroo.astrid.adapter ;
import java.io.IOException ;
import java.text.ParseException ;
import java.util.Arrays ;
import java.util.Comparator ;
import java.util.Date ;
import java.util.HashMap ;
import java.util.regex.Matcher ;
import java.util.regex.Pattern ;
import org.json.JSONArray ;
import org.json.JSONException ;
import org.json.JSONObject ;
import android.app.AlertDialog ;
import android.content.Context ;
import android.content.DialogInterface ;
import android.database.Cursor ;
import android.graphics.Bitmap ;
import android.support.v4.app.Fragment ;
import android.text.Html ;
import android.text.SpannableString ;
import android.text.SpannableStringBuilder ;
import android.text.Spanned ;
import android.text.TextPaint ;
import android.text.TextUtils ;
import android.text.format.DateUtils ;
import android.text.method.LinkMovementMethod ;
import android.text.style.ClickableSpan ;
import android.util.TypedValue ;
import android.view.LayoutInflater ;
import android.view.View ;
import android.view.View.OnClickListener ;
import android.view.ViewGroup ;
import android.view.ViewGroup.LayoutParams ;
import android.widget.CursorAdapter ;
import android.widget.TextView ;
import org.tasks.R ;
import com.todoroo.andlib.data.Property ;
import com.todoroo.andlib.data.Property.StringProperty ;
import com.todoroo.andlib.data.TodorooCursor ;
import com.todoroo.andlib.service.DependencyInjectionService ;
import com.todoroo.andlib.utility.AndroidUtilities ;
import com.todoroo.andlib.utility.DateUtilities ;
import com.todoroo.andlib.utility.Preferences ;
import com.todoroo.astrid.actfm.sync.ActFmPreferenceService ;
import com.todoroo.astrid.actfm.sync.messages.NameMaps ;
import com.todoroo.astrid.activity.AstridActivity ;
import com.todoroo.astrid.data.History ;
import com.todoroo.astrid.data.RemoteModel ;
import com.todoroo.astrid.data.Task ;
import com.todoroo.astrid.data.User ;
import com.todoroo.astrid.data.UserActivity ;
import com.todoroo.astrid.helper.AsyncImageView ;
import edu.mit.mobile.android.imagecache.ImageCache ;
/ * *
* Adapter for displaying a user ' s activity
*
* @author Tim Su < tim @todoroo.com >
*
* /
public class UpdateAdapter extends CursorAdapter {
// --- instance variables
protected final Fragment fragment ;
private final int resource ;
private final LayoutInflater inflater ;
private final ImageCache imageCache ;
private final String linkColor ;
private final String fromView ;
public static final String USER_TABLE_ALIAS = "users_join" ; //$NON-NLS-1$
public static final StringProperty USER_PICTURE = User . PICTURE . cloneAs ( USER_TABLE_ALIAS , "userPicture" ) ; //$NON-NLS-1$
private static final StringProperty USER_FIRST_NAME = User . FIRST_NAME . cloneAs ( USER_TABLE_ALIAS , "userFirstName" ) ; //$NON-NLS-1$
private static final StringProperty USER_LAST_NAME = User . LAST_NAME . cloneAs ( USER_TABLE_ALIAS , "userLastName" ) ; //$NON-NLS-1$
private static final StringProperty USER_NAME = User . NAME . cloneAs ( USER_TABLE_ALIAS , "userName" ) ; //$NON-NLS-1$
public static final StringProperty ACTIVITY_TYPE_PROPERTY = new StringProperty ( null , "'" + NameMaps . TABLE_ID_USER_ACTIVITY + "' as type" ) ; //$NON-NLS-1$//$NON-NLS-2$
public static final StringProperty HISTORY_TYPE_PROPERTY = new StringProperty ( null , "'" + NameMaps . TABLE_ID_HISTORY + "'" ) ; //$NON-NLS-1$ //$NON-NLS-2$
public static final StringProperty PADDING_PROPERTY = new StringProperty ( null , "'0'" ) ; //$NON-NLS-1$
public static final Property < ? > [ ] USER_PROPERTIES = {
USER_PICTURE ,
USER_FIRST_NAME ,
USER_LAST_NAME ,
USER_NAME
} ;
public static final Property < ? > [ ] USER_ACTIVITY_PROPERTIES = {
UserActivity . CREATED_AT ,
UserActivity . UUID ,
UserActivity . ACTION ,
UserActivity . MESSAGE ,
UserActivity . TARGET_ID ,
UserActivity . TARGET_NAME ,
UserActivity . PICTURE ,
UserActivity . USER_UUID ,
UserActivity . ID ,
ACTIVITY_TYPE_PROPERTY ,
} ;
public static final Property < ? > [ ] HISTORY_PROPERTIES = {
History . CREATED_AT ,
History . USER_UUID ,
History . COLUMN ,
History . TABLE_ID ,
History . OLD_VALUE ,
History . NEW_VALUE ,
History . TASK ,
History . USER_UUID ,
History . ID ,
HISTORY_TYPE_PROPERTY ,
} ;
public static final int TYPE_PROPERTY_INDEX = USER_ACTIVITY_PROPERTIES . length - 1 ;
private static final String TARGET_LINK_PREFIX = "$link_" ; //$NON-NLS-1$
private static final Pattern TARGET_LINK_PATTERN = Pattern . compile ( "\\" + TARGET_LINK_PREFIX + "(\\w*)" ) ; //$NON-NLS-1$//$NON-NLS-2$
private static final String TASK_LINK_TYPE = "task" ; //$NON-NLS-1$
public static final String FROM_TAG_VIEW = "from_tag" ; //$NON-NLS-1$
public static final String FROM_TASK_VIEW = "from_task" ; //$NON-NLS-1$
public static final String FROM_RECENT_ACTIVITY_VIEW = "from_recent_activity" ; //$NON-NLS-1$
private final User self ;
private final int color ;
private final int grayColor ;
/ * *
* Constructor
*
* @param activity
* @param resource
* layout resource to inflate
* @param c
* database cursor
* @param autoRequery
* whether cursor is automatically re - queried on changes
* @param onCompletedTaskListener
* goal listener . can be null
* /
public UpdateAdapter ( Fragment fragment , int resource ,
Cursor c , boolean autoRequery ,
String fromView ) {
super ( fragment . getActivity ( ) , c , autoRequery ) ;
DependencyInjectionService . getInstance ( ) . inject ( this ) ;
linkColor = getLinkColor ( fragment ) ;
inflater = ( LayoutInflater ) fragment . getActivity ( ) . getSystemService (
Context . LAYOUT_INFLATER_SERVICE ) ;
imageCache = AsyncImageView . getImageCache ( ) ;
this . fromView = fromView ;
this . resource = resource ;
this . fragment = fragment ;
this . self = getSelfUser ( ) ;
TypedValue tv = new TypedValue ( ) ;
fragment . getActivity ( ) . getTheme ( ) . resolveAttribute ( R . attr . asTextColor , tv , false ) ;
color = tv . data ;
fragment . getActivity ( ) . getTheme ( ) . resolveAttribute ( R . attr . asDueDateColor , tv , false ) ;
grayColor = tv . data ;
}
public static User getSelfUser ( ) {
User self = new User ( ) ;
readPreferenceToUser ( self , USER_FIRST_NAME , ActFmPreferenceService . PREF_FIRST_NAME ) ;
readPreferenceToUser ( self , USER_LAST_NAME , ActFmPreferenceService . PREF_LAST_NAME ) ;
readPreferenceToUser ( self , USER_NAME , ActFmPreferenceService . PREF_NAME ) ;
readPreferenceToUser ( self , USER_PICTURE , ActFmPreferenceService . PREF_PICTURE ) ;
return self ;
}
private static void readPreferenceToUser ( User u , StringProperty prop , String prefKey ) {
String val = Preferences . getStringValue ( prefKey ) ;
if ( val = = null ) {
val = "" ; //$NON-NLS-1$
}
u . setValue ( prop , val ) ;
}
public static String getLinkColor ( Fragment f ) {
TypedValue colorType = new TypedValue ( ) ;
f . getActivity ( ) . getTheme ( ) . resolveAttribute ( R . attr . asDetailsColor , colorType , false ) ;
return "#" + Integer . toHexString ( colorType . data ) . substring ( 2 ) ; //$NON-NLS-1$
}
/ * = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = view setup
* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = * /
private class ModelHolder {
public UserActivity activity = new UserActivity ( ) ;
public User user = new User ( ) ;
public History history = new History ( ) ;
}
/** Creates a new view for use in the list view */
@Override
public View newView ( Context context , Cursor cursor , ViewGroup parent ) {
ViewGroup view = ( ViewGroup ) inflater . inflate ( resource , parent , false ) ;
view . setTag ( new ModelHolder ( ) ) ;
// populate view content
bindView ( view , context , cursor ) ;
return view ;
}
/** Populates a view with content */
@Override
public void bindView ( View view , Context context , Cursor c ) {
TodorooCursor < UserActivity > cursor = ( TodorooCursor < UserActivity > ) c ;
ModelHolder mh = ( ( ModelHolder ) view . getTag ( ) ) ;
String type = cursor . getString ( TYPE_PROPERTY_INDEX ) ;
UserActivity update = mh . activity ;
update . clear ( ) ;
User user = mh . user ;
user . clear ( ) ;
History history = mh . history ;
boolean isSelf ;
if ( NameMaps . TABLE_ID_USER_ACTIVITY . equals ( type ) ) {
readUserActivityProperties ( cursor , update ) ;
isSelf = Task . USER_ID_SELF . equals ( update . getValue ( UserActivity . USER_UUID ) ) ;
} else {
readHistoryProperties ( cursor , history ) ;
isSelf = Task . USER_ID_SELF . equals ( history . getValue ( History . USER_UUID ) ) ;
}
readUserProperties ( cursor , user , self , isSelf ) ;
setFieldContentsAndVisibility ( view , update , user , history , type ) ;
}
public static void readUserActivityProperties ( TodorooCursor < UserActivity > unionCursor , UserActivity activity ) {
activity . setValue ( UserActivity . CREATED_AT , unionCursor . getLong ( 0 ) ) ;
activity . setValue ( UserActivity . UUID , unionCursor . getString ( 1 ) ) ;
activity . setValue ( UserActivity . ACTION , unionCursor . getString ( 2 ) ) ;
activity . setValue ( UserActivity . MESSAGE , unionCursor . getString ( 3 ) ) ;
activity . setValue ( UserActivity . TARGET_ID , unionCursor . getString ( 4 ) ) ;
activity . setValue ( UserActivity . TARGET_NAME , unionCursor . getString ( 5 ) ) ;
activity . setValue ( UserActivity . PICTURE , unionCursor . getString ( 6 ) ) ;
activity . setValue ( UserActivity . USER_UUID , unionCursor . getString ( 7 ) ) ;
}
public static void readHistoryProperties ( TodorooCursor < UserActivity > unionCursor , History history ) {
history . setValue ( History . CREATED_AT , unionCursor . getLong ( 0 ) ) ;
history . setValue ( History . USER_UUID , unionCursor . getString ( 1 ) ) ;
history . setValue ( History . COLUMN , unionCursor . getString ( 2 ) ) ;
history . setValue ( History . TABLE_ID , unionCursor . getString ( 3 ) ) ;
history . setValue ( History . OLD_VALUE , unionCursor . getString ( 4 ) ) ;
history . setValue ( History . NEW_VALUE , unionCursor . getString ( 5 ) ) ;
history . setValue ( History . TASK , unionCursor . getString ( 6 ) ) ;
history . setValue ( History . USER_UUID , unionCursor . getString ( 7 ) ) ;
}
public static void readUserProperties ( TodorooCursor < UserActivity > joinCursor , User user , User self , boolean isSelf ) {
if ( isSelf ) {
user . mergeWith ( self . getSetValues ( ) ) ;
} else {
user . setValue ( USER_FIRST_NAME , joinCursor . get ( USER_FIRST_NAME ) ) ;
user . setValue ( USER_LAST_NAME , joinCursor . get ( USER_LAST_NAME ) ) ;
user . setValue ( USER_NAME , joinCursor . get ( USER_NAME ) ) ;
user . setValue ( USER_PICTURE , joinCursor . get ( USER_PICTURE ) ) ;
}
}
/** Helper method to set the contents and visibility of each field */
public synchronized void setFieldContentsAndVisibility ( View view , UserActivity activity , User user , History history , String type ) {
// picture
if ( NameMaps . TABLE_ID_USER_ACTIVITY . equals ( type ) ) {
setupUserActivityRow ( view , activity , user ) ;
} else if ( NameMaps . TABLE_ID_HISTORY . equals ( type ) ) {
setupHistoryRow ( view , history , user ) ;
}
}
private void setupUserActivityRow ( View view , UserActivity activity , User user ) {
final AsyncImageView pictureView = ( AsyncImageView ) view . findViewById ( R . id . picture ) ; {
if ( user . containsNonNullValue ( USER_PICTURE ) ) {
String pictureUrl = user . getPictureUrl ( USER_PICTURE , RemoteModel . PICTURE_THUMB ) ;
pictureView . setUrl ( pictureUrl ) ;
} else {
pictureView . setUrl ( null ) ;
}
pictureView . setVisibility ( View . VISIBLE ) ;
}
final AsyncImageView commentPictureView = ( AsyncImageView ) view . findViewById ( R . id . comment_picture ) ; {
String pictureThumb = activity . getPictureUrl ( UserActivity . PICTURE , RemoteModel . PICTURE_MEDIUM ) ;
String pictureFull = activity . getPictureUrl ( UserActivity . PICTURE , RemoteModel . PICTURE_LARGE ) ;
Bitmap updateBitmap = null ;
if ( TextUtils . isEmpty ( pictureThumb ) ) {
updateBitmap = activity . getPictureBitmap ( UserActivity . PICTURE ) ;
}
setupImagePopupForCommentView ( view , commentPictureView , pictureThumb , pictureFull , updateBitmap ,
activity . getValue ( UserActivity . MESSAGE ) , fragment , imageCache ) ;
}
// name
final TextView nameView = ( TextView ) view . findViewById ( R . id . title ) ; {
nameView . setText ( getUpdateComment ( ( AstridActivity ) fragment . getActivity ( ) , activity , user , linkColor , fromView ) ) ;
nameView . setMovementMethod ( new LinkMovementMethod ( ) ) ;
nameView . setTextColor ( color ) ;
}
// date
final TextView date = ( TextView ) view . findViewById ( R . id . date ) ; {
CharSequence dateString = DateUtils . getRelativeTimeSpanString ( activity . getValue ( UserActivity . CREATED_AT ) ,
DateUtilities . now ( ) , DateUtils . MINUTE_IN_MILLIS ,
DateUtils . FORMAT_ABBREV_RELATIVE ) ;
date . setText ( dateString ) ;
}
}
private void setupHistoryRow ( View view , History history , User user ) {
final AsyncImageView pictureView = ( AsyncImageView ) view . findViewById ( R . id . picture ) ; {
if ( user . containsNonNullValue ( USER_PICTURE ) ) {
String pictureUrl = user . getPictureUrl ( USER_PICTURE , RemoteModel . PICTURE_THUMB ) ;
pictureView . setUrl ( pictureUrl ) ;
} else {
pictureView . setUrl ( null ) ;
}
pictureView . setVisibility ( View . VISIBLE ) ;
}
final AsyncImageView commentPictureView = ( AsyncImageView ) view . findViewById ( R . id . comment_picture ) ;
commentPictureView . setVisibility ( View . GONE ) ;
final TextView nameView = ( TextView ) view . findViewById ( R . id . title ) ; {
nameView . setText ( getHistoryComment ( ( AstridActivity ) fragment . getActivity ( ) , history , user , linkColor , fromView ) ) ;
nameView . setTextColor ( grayColor ) ;
}
final TextView date = ( TextView ) view . findViewById ( R . id . date ) ; {
CharSequence dateString = DateUtils . getRelativeTimeSpanString ( history . getValue ( History . CREATED_AT ) ,
DateUtilities . now ( ) , DateUtils . MINUTE_IN_MILLIS ,
DateUtils . FORMAT_ABBREV_RELATIVE ) ;
date . setText ( dateString ) ;
}
}
@Override
public boolean isEnabled ( int position ) {
return false ;
}
public static void setupImagePopupForCommentView ( View view , AsyncImageView commentPictureView , final String pictureThumb , final String pictureFull , final Bitmap updateBitmap ,
final String message , final Fragment fragment , ImageCache imageCache ) {
if ( ( ! TextUtils . isEmpty ( pictureThumb ) & & ! "null" . equals ( pictureThumb ) ) | | updateBitmap ! = null ) { //$NON-NLS-1$
commentPictureView . setVisibility ( View . VISIBLE ) ;
if ( updateBitmap ! = null ) {
commentPictureView . setImageBitmap ( updateBitmap ) ;
} else {
commentPictureView . setUrl ( pictureThumb ) ;
}
if ( pictureThumb ! = null & & imageCache . contains ( pictureThumb ) & & updateBitmap = = null ) {
try {
commentPictureView . setDefaultImageBitmap ( imageCache . get ( pictureThumb ) ) ;
} catch ( IOException e ) {
e . printStackTrace ( ) ;
}
} else if ( updateBitmap = = null ) {
commentPictureView . setUrl ( pictureThumb ) ;
}
view . setOnClickListener ( new OnClickListener ( ) {
@Override
public void onClick ( View v ) {
AlertDialog image = new AlertDialog . Builder ( fragment . getActivity ( ) ) . create ( ) ;
AsyncImageView imageView = new AsyncImageView ( fragment . getActivity ( ) ) ;
imageView . setLayoutParams ( new LayoutParams ( LayoutParams . FILL_PARENT , LayoutParams . WRAP_CONTENT ) ) ;
imageView . setDefaultImageResource ( android . R . drawable . ic_menu_gallery ) ;
if ( updateBitmap ! = null ) {
imageView . setImageBitmap ( updateBitmap ) ;
} else {
imageView . setUrl ( pictureFull ) ;
}
image . setView ( imageView ) ;
image . setMessage ( message ) ;
image . setButton ( fragment . getString ( R . string . DLG_close ) , new DialogInterface . OnClickListener ( ) {
@Override
public void onClick ( DialogInterface dialog , int which ) {
return ;
}
} ) ;
image . show ( ) ;
}
} ) ;
} else {
commentPictureView . setVisibility ( View . GONE ) ;
}
}
public static String linkify ( String string , String linkColor ) {
return String . format ( "<font color=%s>%s</font>" , linkColor , string ) ; //$NON-NLS-1$
}
public static Spanned getUpdateComment ( final AstridActivity context , UserActivity activity , User user , String linkColor , String fromView ) {
String userDisplay ;
if ( activity . getValue ( UserActivity . USER_UUID ) . equals ( Task . USER_ID_SELF ) ) {
userDisplay = context . getString ( R . string . update_string_user_self ) ;
} else if ( user = = null ) {
userDisplay = context . getString ( R . string . ENA_no_user ) ;
} else {
userDisplay = user . getDisplayName ( USER_NAME , USER_FIRST_NAME , USER_LAST_NAME ) ;
}
if ( TextUtils . isEmpty ( userDisplay ) ) {
userDisplay = context . getString ( R . string . ENA_no_user ) ;
}
String targetName = activity . getValue ( UserActivity . TARGET_NAME ) ;
String action = activity . getValue ( UserActivity . ACTION ) ;
String message = activity . getValue ( UserActivity . MESSAGE ) ;
int commentResource = 0 ;
if ( UserActivity . ACTION_TASK_COMMENT . equals ( action ) ) {
if ( fromView . equals ( FROM_TASK_VIEW ) | | TextUtils . isEmpty ( targetName ) ) {
commentResource = R . string . update_string_default_comment ;
} else {
commentResource = R . string . update_string_task_comment ;
}
} else if ( UserActivity . ACTION_TAG_COMMENT . equals ( action ) ) {
if ( fromView . equals ( FROM_TAG_VIEW ) | | TextUtils . isEmpty ( targetName ) ) {
commentResource = R . string . update_string_default_comment ;
} else {
commentResource = R . string . update_string_tag_comment ;
}
}
if ( commentResource = = 0 ) {
return Html . fromHtml ( String . format ( "%s %s" , userDisplay , message ) ) ; //$NON-NLS-1$
}
String original = context . getString ( commentResource , userDisplay , targetName , message ) ;
int taskLinkIndex = original . indexOf ( TARGET_LINK_PREFIX ) ;
if ( taskLinkIndex < 0 ) {
return Html . fromHtml ( original ) ;
}
String [ ] components = original . split ( " " ) ; //$NON-NLS-1$
SpannableStringBuilder builder = new SpannableStringBuilder ( ) ;
StringBuilder htmlStringBuilder = new StringBuilder ( ) ;
for ( String comp : components ) {
Matcher m = TARGET_LINK_PATTERN . matcher ( comp ) ;
if ( m . find ( ) ) {
builder . append ( Html . fromHtml ( htmlStringBuilder . toString ( ) ) ) ;
htmlStringBuilder . setLength ( 0 ) ;
String linkType = m . group ( 1 ) ;
CharSequence link = getLinkSpan ( context , activity , targetName , linkColor , linkType ) ;
if ( link ! = null ) {
builder . append ( link ) ;
if ( ! m . hitEnd ( ) ) {
builder . append ( comp . substring ( m . end ( ) ) ) ;
}
builder . append ( ' ' ) ;
}
} else {
htmlStringBuilder . append ( comp ) ;
htmlStringBuilder . append ( ' ' ) ;
}
}
if ( htmlStringBuilder . length ( ) > 0 ) {
builder . append ( Html . fromHtml ( htmlStringBuilder . toString ( ) ) ) ;
}
return builder ;
}
public static String getHistoryComment ( final AstridActivity context , History history , User user , String linkColor , String fromView ) {
boolean hasTask = false ;
JSONArray taskAttrs = null ;
if ( ! TextUtils . isEmpty ( history . getValue ( History . TASK ) ) ) {
try {
taskAttrs = new JSONArray ( history . getValue ( History . TASK ) ) ;
hasTask = true ;
} catch ( JSONException e ) {
//
}
}
String item ;
String itemPosessive ;
if ( FROM_TASK_VIEW . equals ( fromView ) ) {
item = context . getString ( R . string . history_this_task ) ;
} else if ( hasTask & & taskAttrs ! = null ) {
item = taskAttrs . optString ( 1 ) ;
} else {
item = context . getString ( R . string . history_this_list ) ;
}
itemPosessive = item + "'s" ;
String oldValue = history . getValue ( History . OLD_VALUE ) ;
String newValue = history . getValue ( History . NEW_VALUE ) ;
String result = "" ;
String column = history . getValue ( History . COLUMN ) ;
try {
if ( History . COL_TAG_ADDED . equals ( column ) | | History . COL_TAG_REMOVED . equals ( column ) ) {
JSONArray tagObj = new JSONArray ( newValue ) ;
String tagName = tagObj . getString ( 1 ) ;
if ( History . COL_TAG_ADDED . equals ( column ) ) {
result = context . getString ( R . string . history_tag_added , item , tagName ) ;
} else {
result = context . getString ( R . string . history_tag_removed , item , tagName ) ;
}
} else if ( History . COL_ATTACHMENT_ADDED . equals ( column ) | | History . COL_ATTACHMENT_REMOVED . equals ( column ) ) {
JSONArray attachmentArray = new JSONArray ( newValue ) ;
String attachmentName = attachmentArray . getString ( 0 ) ;
if ( History . COL_ATTACHMENT_ADDED . equals ( column ) ) {
result = context . getString ( R . string . history_attach_added , attachmentName , item ) ;
} else {
result = context . getString ( R . string . history_attach_removed , attachmentName , item ) ;
}
} else if ( History . COL_ACKNOWLEDGED . equals ( column ) ) {
result = context . getString ( R . string . history_acknowledged , item ) ;
} else if ( History . COL_SHARED_WITH . equals ( column ) | | History . COL_UNSHARED_WITH . equals ( column ) ) {
JSONArray members = new JSONArray ( newValue ) ;
String userId = history . getValue ( History . USER_UUID ) ;
StringBuilder memberList = new StringBuilder ( ) ;
for ( int i = 0 ; i < members . length ( ) ; i + + ) {
JSONObject m = members . getJSONObject ( i ) ;
memberList . append ( userDisplay ( context , userId , m ) ) ;
if ( i ! = members . length ( ) - 1 ) {
memberList . append ( ", " ) ;
}
}
if ( History . COL_SHARED_WITH . equals ( column ) ) {
result = context . getString ( R . string . history_shared_with , item , memberList ) ;
} else {
result = context . getString ( R . string . history_unshared_with , item , memberList ) ;
}
} else if ( History . COL_MEMBER_ADDED . equals ( column ) | | History . COL_MEMBER_REMOVED . equals ( column ) ) {
JSONObject userValue = new JSONObject ( newValue ) ;
if ( history . getValue ( History . USER_UUID ) . equals ( userValue . optString ( "id" ) ) & & History . COL_MEMBER_REMOVED . equals ( column ) ) {
result = context . getString ( R . string . history_left_list , item ) ;
} else {
String userDisplay = userDisplay ( context , history . getValue ( History . USER_UUID ) , userValue ) ;
if ( History . COL_MEMBER_ADDED . equals ( column ) ) {
result = context . getString ( R . string . history_added_user , userDisplay , item ) ;
} else {
result = context . getString ( R . string . history_removed_user , userDisplay , item ) ;
}
}
} else if ( History . COL_COMPLETED_AT . equals ( column ) ) {
if ( ! TextUtils . isEmpty ( newValue ) & & ! "null" . equals ( newValue ) ) {
result = context . getString ( R . string . history_completed , item ) ;
} else {
result = context . getString ( R . string . history_uncompleted , item ) ;
}
} else if ( History . COL_DELETED_AT . equals ( column ) ) {
if ( ! TextUtils . isEmpty ( newValue ) & & ! "null" . equals ( newValue ) ) {
result = context . getString ( R . string . history_deleted , item ) ;
} else {
result = context . getString ( R . string . history_undeleted , item ) ;
}
} else if ( History . COL_IMPORTANCE . equals ( column ) ) {
int oldPriority = AndroidUtilities . tryParseInt ( oldValue , 0 ) ;
int newPriority = AndroidUtilities . tryParseInt ( newValue , 0 ) ;
result = context . getString ( R . string . history_importance_changed , itemPosessive , priorityString ( oldPriority ) , priorityString ( newPriority ) ) ;
} else if ( History . COL_NOTES_LENGTH . equals ( column ) ) {
int oldLength = AndroidUtilities . tryParseInt ( oldValue , 0 ) ;
int newLength = AndroidUtilities . tryParseInt ( newValue , 0 ) ;
if ( oldLength > 0 & & newLength > oldLength ) {
result = context . getString ( R . string . history_added_description_characters , ( newLength - oldLength ) , itemPosessive ) ;
} else if ( newLength = = 0 ) {
result = context . getString ( R . string . history_removed_description , itemPosessive ) ;
} else if ( oldLength > 0 & & newLength < oldLength ) {
result = context . getString ( R . string . history_removed_description_characters , ( oldLength - newLength ) , itemPosessive ) ;
} else if ( oldLength > 0 & & oldLength = = newLength ) {
result = context . getString ( R . string . history_updated_description , itemPosessive ) ;
}
} else if ( History . COL_PUBLIC . equals ( column ) ) {
int value = AndroidUtilities . tryParseInt ( newValue , 0 ) ;
if ( value > 0 ) {
result = context . getString ( R . string . history_made_public , item ) ;
} else {
result = context . getString ( R . string . history_made_private , item ) ;
}
} else if ( History . COL_DUE . equals ( column ) ) {
if ( ! TextUtils . isEmpty ( oldValue ) & & ! TextUtils . isEmpty ( newValue )
& & ! "null" . equals ( oldValue ) & & ! "null" . equals ( newValue ) ) {
result = context . getString ( R . string . history_changed_due_date , itemPosessive , dateString ( context , oldValue , newValue ) , dateString ( context , newValue , oldValue ) ) ;
} else if ( ! TextUtils . isEmpty ( newValue ) & & ! "null" . equals ( newValue ) ) {
result = context . getString ( R . string . history_set_due_date , itemPosessive , dateString ( context , newValue , DateUtilities . timeToIso8601 ( DateUtilities . now ( ) , true ) ) ) ;
} else {
result = context . getString ( R . string . history_removed_due_date , itemPosessive ) ;
}
} else if ( History . COL_REPEAT . equals ( column ) ) {
String repeatString = getRepeatString ( context , newValue ) ;
if ( ! TextUtils . isEmpty ( repeatString ) ) {
result = context . getString ( R . string . history_changed_repeat , itemPosessive , repeatString ) ;
} else {
result = context . getString ( R . string . history_removed_repeat , itemPosessive ) ;
}
} else if ( History . COL_TASK_REPEATED . equals ( column ) ) {
result = context . getString ( R . string . history_completed_repeating_task , item , dateString ( context , newValue , oldValue ) ) ;
} else if ( History . COL_TITLE . equals ( column ) ) {
if ( ! TextUtils . isEmpty ( oldValue ) & & ! "null" . equals ( oldValue ) ) {
result = context . getString ( R . string . history_title_changed , itemPosessive , oldValue , newValue ) ;
} else {
result = context . getString ( R . string . history_title_set , itemPosessive , newValue ) ;
}
} else if ( History . COL_NAME . equals ( column ) ) {
if ( ! TextUtils . isEmpty ( oldValue ) & & ! "null" . equals ( oldValue ) ) {
result = context . getString ( R . string . history_name_changed , oldValue , newValue ) ;
} else {
result = context . getString ( R . string . history_name_set , newValue ) ;
}
} else if ( History . COL_DESCRIPTION . equals ( column ) ) {
if ( ! TextUtils . isEmpty ( oldValue ) & & ! "null" . equals ( oldValue ) ) {
result = context . getString ( R . string . history_description_changed , oldValue , newValue ) ;
} else {
result = context . getString ( R . string . history_description_set , newValue ) ;
}
} else if ( History . COL_PICTURE_ID . equals ( column ) | | History . COL_DEFAULT_LIST_IMAGE_ID . equals ( column ) ) {
result = context . getString ( R . string . history_changed_list_picture ) ;
} else if ( History . COL_IS_SILENT . equals ( column ) ) {
int value = AndroidUtilities . tryParseInt ( newValue , 0 ) ;
if ( value > 0 ) {
result = context . getString ( R . string . history_silenced , item ) ;
} else {
result = context . getString ( R . string . history_unsilenced , item ) ;
}
} else if ( History . COL_IS_FAVORITE . equals ( column ) ) {
int value = AndroidUtilities . tryParseInt ( newValue , 0 ) ;
if ( value > 0 ) {
result = context . getString ( R . string . history_favorited , item ) ;
} else {
result = context . getString ( R . string . history_unfavorited , item ) ;
}
} else if ( History . COL_USER_ID . equals ( column ) ) {
String userId = history . getValue ( History . USER_UUID ) ;
JSONObject userValue = new JSONObject ( newValue ) ;
if ( FROM_TAG_VIEW . equals ( fromView ) & & ! hasTask ) {
if ( ! TextUtils . isEmpty ( oldValue ) & & ! "null" . equals ( oldValue ) ) {
result = context . getString ( R . string . history_changed_list_owner , userDisplay ( context , userId , userValue ) ) ;
} else {
result = context . getString ( R . string . history_created_this_list ) ;
}
} else if ( ! TextUtils . isEmpty ( oldValue ) & & ! "null" . equals ( oldValue ) & & Task . USER_ID_UNASSIGNED . equals ( userValue ) ) {
result = context . getString ( R . string . history_unassigned , item ) ;
} else if ( Task . USER_ID_UNASSIGNED . equals ( oldValue ) & & userValue . optString ( "id" ) . equals ( ActFmPreferenceService . userId ( ) ) ) {
result = context . getString ( R . string . history_claimed , item ) ;
} else if ( ! TextUtils . isEmpty ( oldValue ) & & ! "null" . equals ( oldValue ) ) {
result = context . getString ( R . string . history_assigned_to , item , userDisplay ( context , userId , userValue ) ) ;
} else if ( ! userValue . optString ( "id" ) . equals ( ActFmPreferenceService . userId ( ) ) & & ! Task . USER_ID_UNASSIGNED . equals ( userValue . optString ( "id" ) ) ) {
result = context . getString ( R . string . history_created_for , item , userDisplay ( context , userId , userValue ) ) ;
} else {
result = context . getString ( R . string . history_created , item ) ;
}
} else {
result = context . getString ( R . string . history_default , column , newValue ) ;
}
} catch ( Exception e ) {
e . printStackTrace ( ) ;
result = context . getString ( R . string . history_default , column , newValue ) ;
}
if ( TextUtils . isEmpty ( result ) ) {
result = context . getString ( R . string . history_default , column , newValue ) ;
}
String userDisplay ;
if ( history . getValue ( History . USER_UUID ) . equals ( Task . USER_ID_SELF ) | | history . getValue ( History . USER_UUID ) . equals ( ActFmPreferenceService . userId ( ) ) ) {
userDisplay = context . getString ( R . string . update_string_user_self ) ;
} else if ( user = = null ) {
userDisplay = context . getString ( R . string . ENA_no_user ) ;
} else {
userDisplay = user . getDisplayName ( USER_NAME , USER_FIRST_NAME , USER_LAST_NAME ) ;
}
return userDisplay + " " + result ;
}
private static String dateString ( Context context , String value , String other ) {
boolean includeYear = ( ! TextUtils . isEmpty ( other ) & & ! value . substring ( 0 , 4 ) . equals ( other . substring ( 0 , 4 ) ) ) ;
boolean hasTime = DateUtilities . isoStringHasTime ( value ) ;
long time = 0 ;
try {
time = DateUtilities . parseIso8601 ( value ) ;
Date date = new Date ( time ) ;
String result = DateUtilities . getDateString ( context , date , includeYear ) ;
if ( hasTime ) {
result + = ", " + DateUtilities . getTimeString ( context , date , false ) ; //$NON-NLS-1$
}
return result ;
} catch ( ParseException e ) {
return value ;
}
}
private static final HashMap < String , Integer > INTERVAL_LABELS = new HashMap < String , Integer > ( ) ;
static {
INTERVAL_LABELS . put ( "DAILY" , R . string . repeat_days ) ; //$NON-NLS-1$
INTERVAL_LABELS . put ( "WEEKDAYS" , R . string . repeat_weekdays ) ; //$NON-NLS-1$
INTERVAL_LABELS . put ( "WEEKLY" , R . string . repeat_weeks ) ; //$NON-NLS-1$
INTERVAL_LABELS . put ( "MONTHLY" , R . string . repeat_months ) ; //$NON-NLS-1$
INTERVAL_LABELS . put ( "YEARLY" , R . string . repeat_years ) ; //$NON-NLS-1$
INTERVAL_LABELS . put ( "HOURLY" , R . string . repeat_hours ) ; //$NON-NLS-1$
INTERVAL_LABELS . put ( "MINUTELY" , R . string . repeat_minutes ) ; //$NON-NLS-1$
}
private static final String [ ] SORTED_WEEKDAYS = { "SU" , "MO" , "TU" , "WE" , "TH" , "FR" , "SA" } ;
private static String getRepeatString ( Context context , String value ) {
if ( TextUtils . isEmpty ( value ) | | "null" . equals ( value ) ) {
return null ;
}
try {
JSONObject repeat = new JSONObject ( value ) ;
boolean weekdays = false ;
if ( repeat . has ( "freq" ) ) {
String freq = repeat . getString ( "freq" ) ;
int interval = repeat . getInt ( "interval" ) ;
JSONArray byDay = repeat . optJSONArray ( "byday" ) ;
String [ ] byDayStrings = null ;
if ( byDay ! = null ) {
byDayStrings = new String [ byDay . length ( ) ] ;
for ( int i = 0 ; i < byDay . length ( ) ; i + + ) {
byDayStrings [ i ] = byDay . getString ( i ) ;
}
}
String result = "" ;
if ( "WEEKLY" . equals ( freq ) & & byDay ! = null & & byDayStrings ! = null ) {
Arrays . sort ( byDayStrings ) ;
StringBuilder daysString = new StringBuilder ( ) ;
daysString . append ( "[" ) ;
for ( String s : byDayStrings ) {
daysString . append ( "\"" ) . append ( s ) . append ( "\"" ) . append ( "," ) ;
}
daysString . deleteCharAt ( daysString . length ( ) - 1 ) ;
daysString . append ( "]" ) ;
if ( daysString . toString ( ) . equals ( "[\"FR\",\"MO\",\"TH\",\"TU\",\"WE\"]" ) ) {
result = context . getString ( R . string . repeat_weekdays ) ;
weekdays = true ;
}
}
if ( ! weekdays ) {
if ( interval = = 1 ) {
result = context . getString ( INTERVAL_LABELS . get ( freq ) ) ;
result = result . substring ( 0 , result . length ( ) - 1 ) ;
} else {
result = interval + " " + context . getString ( INTERVAL_LABELS . get ( freq ) ) ;
}
}
result = context . getString ( R . string . history_repeat_every , result ) ;
if ( "WEEKLY" . equals ( freq ) & & ! weekdays & & byDay ! = null & & byDay . length ( ) > 0 & & byDayStrings ! = null ) {
Arrays . sort ( byDayStrings , new Comparator < String > ( ) {
@Override
public int compare ( String lhs , String rhs ) {
int lhIndex = AndroidUtilities . indexOf ( SORTED_WEEKDAYS , lhs ) ;
int rhIndex = AndroidUtilities . indexOf ( SORTED_WEEKDAYS , rhs ) ;
if ( lhIndex < rhIndex ) {
return - 1 ;
} else if ( lhIndex > rhIndex ) {
return 1 ;
} else {
return 0 ;
}
}
} ) ;
StringBuilder byDayDisplay = new StringBuilder ( ) ;
for ( String s : byDayStrings ) {
byDayDisplay . append ( s ) . append ( ", " ) ;
}
byDayDisplay . delete ( byDayDisplay . length ( ) - 2 , byDayDisplay . length ( ) ) ;
result + = ( " " + context . getString ( R . string . history_repeat_on , byDayDisplay . toString ( ) ) ) ;
}
if ( "COMPLETION" . equals ( repeat . optString ( "from" ) ) ) {
result + = ( " " + context . getString ( R . string . history_repeat_from_completion ) ) ;
}
return result ;
} else {
return null ;
}
} catch ( JSONException e ) {
return null ;
}
}
private static String userDisplay ( Context context , String historyUserId , JSONObject userJson ) {
try {
String id = userJson . getString ( "id" ) ;
String name = userJson . getString ( "name" ) ;
if ( historyUserId . equals ( id ) & & ActFmPreferenceService . userId ( ) . equals ( id ) ) {
return context . getString ( R . string . history_yourself ) ;
} else if ( ActFmPreferenceService . userId ( ) . equals ( id ) ) {
return context . getString ( R . string . history_you ) ;
} else if ( RemoteModel . isValidUuid ( id ) ) {
return name ;
} else {
return context . getString ( R . string . history_a_deleted_user ) ;
}
} catch ( JSONException e ) {
return context . getString ( R . string . ENA_no_user ) . toLowerCase ( ) ;
}
}
private static final String [ ] PRIORITY_STRINGS = { "!!!" , "!!" , "!" , "o" } ;
private static String priorityString ( int priority ) {
return PRIORITY_STRINGS [ priority ] ;
}
private static CharSequence getLinkSpan ( final AstridActivity activity , UserActivity update , String targetName , String linkColor , String linkType ) {
if ( TASK_LINK_TYPE . equals ( linkType ) ) {
final String taskId = update . getValue ( UserActivity . TARGET_ID ) ;
if ( RemoteModel . isValidUuid ( taskId ) ) {
SpannableString taskSpan = new SpannableString ( targetName ) ;
taskSpan . setSpan ( new ClickableSpan ( ) {
@Override
public void onClick ( View widget ) {
if ( activity ! = null ) // TODO: This shouldn't happen, but sometimes does
{
activity . onTaskListItemClicked ( taskId ) ;
}
}
@Override
public void updateDrawState ( TextPaint ds ) {
super . updateDrawState ( ds ) ;
ds . setUnderlineText ( false ) ;
}
} , 0 , targetName . length ( ) , Spanned . SPAN_INCLUSIVE_EXCLUSIVE ) ;
return taskSpan ;
} else {
return Html . fromHtml ( linkify ( targetName , linkColor ) ) ;
}
}
return null ;
}
}