public class Config {

	private String url;
	private boolean trustAllCertificates;
	private boolean useHttps;
	private String username;
	private String password;
	private long syncPeriod;
	private boolean syncOverWifiOnly;


	public String getUrl() {
		return url;
	}

	public void setUrl(String url) {
		this.url = url;
	}

	public boolean trustAllCertificates() {
		return trustAllCertificates;
	}

	public void setTrustAllCertificates(boolean trustAllCertificates) {
		this.trustAllCertificates = trustAllCertificates;
	}

	public boolean useHttps() {
		return useHttps;
	}

	public void setUseHttps(boolean useHttps) {
		this.useHttps = useHttps;
	}

	public String getUsername() {
		return username;
	}

	public void setUsername(String username) {
		this.username = username;
	}

	public String getPassword() {
		return password;
	}

	public void setPassword(String password) {
		this.password = password;
	}

	public long getSyncPeriod() {
		return syncPeriod;
	}

	public void setSyncPeriod(long syncPeriod) {
		this.syncPeriod = syncPeriod;
	}

	public boolean requireAuth() {
		return username != null;
	}

	public boolean syncOverWifiOnly() {
		return syncOverWifiOnly;
	}

	public void setSyncOverWifiOnly(boolean syncOverWifiOnly) {
		this.syncOverWifiOnly = syncOverWifiOnly;
	}
} @EBean
public class ConfigManager {

	public static final String ACCOUNT_TYPE = BuildConfig.ACCOUNT_TYPE;

	private static final String KEY_USERNAME = "username";
	private static final String KEY_SYNC_PERIOD = "syncPeriod";
	private static final String KEY_TRUST_ALL_CERTIFICATES = "trustAllCertificates";
	private static final String KEY_USE_HTTPS = "useHttps";
	private static final String KEY_SYNC_OVER_WIFI_ONLY = "syncOverWifiOnly";

	@SystemService protected AccountManager accountManager;
	@RootContext protected Context context;
	private DatabaseHelper databaseHelper;

	@AfterInject
	protected void init() {
		databaseHelper = new DatabaseHelper(context);
	}

	public void save(Config config) {
		Account account = getAccount();
		if (account != null && !account.name.equals(config.getUrl())) {
			remove(account);
			account = null;
		}
		if (account == null) {
			account = new Account(config.getUrl(), ACCOUNT_TYPE);
			accountManager.addAccountExplicitly(account, config.getPassword(), null);
		}
		accountManager.setUserData(account, KEY_USERNAME, config.getUsername());
		accountManager.setUserData(account, KEY_USE_HTTPS, String.valueOf(config.useHttps()));
		accountManager.setUserData(account, KEY_TRUST_ALL_CERTIFICATES, String.valueOf(config.trustAllCertificates()));
		accountManager.setUserData(account, KEY_SYNC_PERIOD, String.valueOf(config.getSyncPeriod()));
		accountManager.setUserData(account, KEY_SYNC_OVER_WIFI_ONLY, String.valueOf(config.syncOverWifiOnly()));

		SyncManager.setPeriodicSync(this, config);
	}

	public Config get() {
		Account account = getAccount();
		Config config = null;
		if (account != null) {
			config = new Config();
			config.setUrl(account.name);
			config.setTrustAllCertificates(Boolean.valueOf(accountManager.getUserData(account, KEY_TRUST_ALL_CERTIFICATES)));
			config.setUsername(accountManager.getUserData(account, KEY_USERNAME));
			config.setPassword(accountManager.getPassword(account));
			config.setUseHttps(Boolean.valueOf(accountManager.getUserData(account, KEY_USE_HTTPS)));
			config.setSyncOverWifiOnly(Boolean.valueOf(accountManager.getUserData(account, KEY_SYNC_OVER_WIFI_ONLY)));
			String syncPeriod = accountManager.getUserData(account, KEY_SYNC_PERIOD);
			if (syncPeriod != null) {
				config.setSyncPeriod(Long.valueOf(syncPeriod));
			}
		}
		return config;
	}

	public Account getAccountForConfig(Config config) {
		Account[] accounts = accountManager.getAccountsByType(ACCOUNT_TYPE);
		for (Account account : accounts) {
			if (account.name.equals(config.getUrl())) {
				return account;
			}
		}
		return null;
	}

	public Account getAccount() {
		Account[] accounts = accountManager.getAccountsByType(ACCOUNT_TYPE);
		if (accounts.length > 0) {
			return accounts[0];
		}
		return null;
	}

	private void remove(Account account) {
		accountManager.removeAccount(account, null, null);
		databaseHelper.clearTables();
	}
} public enum ArticleType {

	Newest(R.id.newest, R.string.newest, ""),
	Unread(R.id.unread, R.string.unread, "unread"),
	Starred(R.id.starred, R.string.starred, "starred");

	private int id;
	private int nameResId;
	private String apiName;

	private ArticleType(int id, int nameResId, String apiName) {
		this.id = id;
		this.nameResId = nameResId;
		this.apiName = apiName;
	}

	public int getId() {
		return id;
	}

	public String getName(Context context) {
		return context.getString(nameResId);
	}

	@Override
	public String toString() {
		return apiName;
	}

	public static ArticleType fromId(int id) {
		for (ArticleType type : ArticleType.values()) {
			if (type.getId() == id) {
				return type;
			}
		}
		throw new IllegalArgumentException("There is no ArticleType for this id : " + id);
	}
} public class Filter implements Parcelable {

	private ArticleType type = ArticleType.Newest;
	private Tag tag = Tag.ALL;
	private Source source = null;

	public Filter() {
	}

	public ArticleType getType() {
		return type;
	}

	public void setType(ArticleType type) {
		this.type = type;
	}

	public Tag getTag() {
		return tag;
	}

	public void setTag(Tag tag) {
		this.tag = tag;
		this.source = null;
	}

	public Source getSource() {
		return source;
	}

	public void setSource(Source source) {
		this.source = source;
		if (source == null) {
			this.tag = Tag.ALL;
		} else {
			this.tag = null;
		}
	}


	@Override
	public int describeContents() {
		return 0;
	}

	@Override
	public void writeToParcel(Parcel dest, int flags) {
		dest.writeInt(this.type == null ? -1 : this.type.ordinal());
		dest.writeParcelable(this.tag, 0);
		dest.writeParcelable(this.source, 0);
	}

	private Filter(Parcel in) {
		int tmpType = in.readInt();
		this.type = tmpType == -1 ? null : ArticleType.values()[tmpType];
		this.tag = in.readParcelable(Tag.class.getClassLoader());
		this.source = in.readParcelable(Source.class.getClassLoader());
	}

	public static Creator<Filter> CREATOR = new Creator<Filter>() {
		public Filter createFromParcel(Parcel source) {
			return new Filter(source);
		}

		public Filter[] newArray(int size) {
			return new Filter[size];
		}
	};
} @DatabaseTable
@JsonIgnoreProperties(ignoreUnknown = true)
public class Source implements Parcelable {

	public static final Comparator<Source> COMPARATOR_UNREAD_INVERSE = new Comparator<Source>() {
		@Override
		public int compare(Source lhs, Source rhs) {
			int unreadCompare = - Integer.valueOf(lhs.getUnread()).compareTo(rhs.getUnread());
			if (unreadCompare == 0) {
				return lhs.title.compareTo(rhs.getTitle());
			}
			return unreadCompare;
		}
	};

	@DatabaseField(id = true)
	private int id;
	@DatabaseField
	private String title;
	@DatabaseField
	private String tags;
	@DatabaseField
	private String icon;
	private int unread;

	public Source() {

	}

	public int getId() {
		return id;
	}

	public void setId(int id) {
		this.id = id;
	}

	public String getTitle() {
		return title;
	}

	public void setTitle(String title) {
		this.title = title;
	}

	public List<String> getTags() {
		return Arrays.asList(tags.split(","));
	}

	public void setTags(List<String> tags) {
		this.tags = TextUtils.join(",",tags);
	}

	@JsonSetter
	public void setTags(String tags) {
		this.tags = tags;
	}

	public String getIcon() {
		return icon;
	}

	public void setIcon(String icon) {
        if (!"false".equals(icon)) {
            this.icon = icon;
        }
	}

	public int getUnread() {
		return unread;
	}

	public void setUnread(int unread) {
		this.unread = unread;
	}

	@Override
	public boolean equals(Object o) {
		if (!(o instanceof Source))
			return false;
		Source oSource = (Source) o;
		return id == oSource.getId();
	}

	@Override
	public int describeContents() {
		return 0;
	}

	@Override
	public void writeToParcel(Parcel dest, int flags) {
		dest.writeInt(this.id);
		dest.writeString(this.title);
		dest.writeString(this.tags);
		dest.writeString(this.icon);
		dest.writeInt(this.unread);
	}

	private Source(Parcel in) {
		this.id = in.readInt();
		this.title = in.readString();
		this.tags = in.readString();
		this.icon = in.readString();
		this.unread = in.readInt();
	}

	public static Parcelable.Creator<Source> CREATOR = new Parcelable.Creator<Source>() {
		public Source createFromParcel(Parcel source) {
			return new Source(source);
		}

		public Source[] newArray(int size) {
			return new Source[size];
		}
	};
} @DatabaseTable(daoClass = TagDao.class)
@JsonIgnoreProperties(ignoreUnknown = true)
public class Tag implements Parcelable {

	public static final Tag ALL = new Tag(R.string.allTags);

	public static final Comparator<Tag> COMPARATOR_UNREAD_INVERSE = new Comparator<Tag>() {
		@Override
		public int compare(Tag lhs, Tag rhs) {
			return - Integer.valueOf(lhs.getUnread()).compareTo(rhs.getUnread());
		}
	};

    public static List<Integer> colorsOfTags(List<Tag> tags) {
        List<Integer> colors = new ArrayList<Integer>();
        for (Tag tag : tags) {
            colors.add(tag.getColor());
        }
        return colors;
    }

	@DatabaseField(id = true, columnName = TagDao.COLUMN_NAME)
	@JsonProperty("tag")
	private String name;
	private int nameId;
	@DatabaseField
	private int color;
	@DatabaseField
	private int unread;

	public Tag() {

	}

	private Tag(int nameId) {
		this.nameId = nameId;
	}

	public String getName(Context context) {
		if (nameId != 0)
			return context.getString(nameId);
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public int getColor() {
		return color;
	}

	@JsonProperty("color")
	public void setColor(String color) {
		this.color = ColorUtil.parseColor(color);
	}

	public int getUnread() {
		return unread;
	}

	public void setUnread(int unread) {
		this.unread = unread;
	}

	@Override
	public boolean equals(Object o) {
		if (!(o instanceof Tag))
			return false;
		Tag oTag = (Tag) o;
		if (name == null)
			return nameId == oTag.nameId;
		return name.equals(oTag.name);
	}

	@Override
	public String toString() {
		if (name == null)
			return "All";
		return name;
	}

	@Override
	public int describeContents() {
		return 0;
	}

	@Override
	public void writeToParcel(Parcel dest, int flags) {
		dest.writeString(this.name);
		dest.writeInt(this.nameId);
		dest.writeInt(this.color);
		dest.writeInt(this.unread);
	}

	private Tag(Parcel in) {
		this.name = in.readString();
		this.nameId = in.readInt();
		this.color = in.readInt();
		this.unread = in.readInt();
	}

	public static Parcelable.Creator<Tag> CREATOR = new Parcelable.Creator<Tag>() {
		public Tag createFromParcel(Parcel source) {
			return new Tag(source);
		}

		public Tag[] newArray(int size) {
			return new Tag[size];
		}
	};
} public class ArticleDao extends BaseDaoImpl<Article, Integer> {

	public static final String COLUMN_ID = "id";
	public static final String COLUMN_DATETIME = "dateTime";
	public static final String COLUMN_UNREAD = "unread";
	public static final String COLUMN_STARRED = "starred";
	public static final String COLUMN_TAGS = "tags";
	public static final String COLUMN_SOURCE_ID = "sourceId";
	public static final String COLUMN_UPDATE_TIME = "updateTime";

	public static final String ACTION_CREATION = "fr.ydelouis.selfoss.article.ACTION_CREATION";
	public static final String ACTION_UPDATE = "fr.ydelouis.selfoss.article.ACTION_UPDATE";
	public static final String EXTRA_ARTICLE = "article";

	private Context context;

	public ArticleDao(ConnectionSource connectionSource) throws SQLException {
		super(connectionSource, Article.class);
	}

	public void setContext(Context context) {
		this.context = context.getApplicationContext();
	}

	@Override
	public CreateOrUpdateStatus createOrUpdate(Article article) {
		try {
			CreateOrUpdateStatus status = super.createOrUpdate(article);
			if (status.isCreated()) {
				notifyCreation(article);
			} else if (status.isUpdated()) {
				notifyUpdate(article);
			}
			return status;
		} catch (SQLException e) {
			throw new RuntimeException(e);
		}
	}

	@Override
	public int update(Article article) {
		try {
			int updated = super.update(article);
			if (updated > 0) {
				notifyUpdate(article);
			}
			return updated;
		} catch (SQLException e) {
			throw new RuntimeException(e);
		}
	}

	public void updateAlsoCached(Article article) {
		int updated = update(article);
		if (updated != 0) {
			notifyUpdate(article);
			article.setCached(!article.isCached());
			createOrUpdate(article);
			article.setCached(!article.isCached());
		}
	}

	public List<Article> queryForUnread(Filter filter) {
		try {
			QueryBuilder<Article, Integer> queryBuilder = queryBuilder();
			Where<Article, Integer> where = queryBuilder.where();

			whereFilter(where, filter);
			where.and().eq(COLUMN_UNREAD, true);

			queryBuilder.orderBy(COLUMN_DATETIME, false);

			return queryBuilder.query();
		} catch (SQLException e) {
			throw new RuntimeException(e);
		}
	}

	public List<Article> queryForPrevious(Filter filter, Article firstArticle) {
		try {
			QueryBuilder<Article, Integer> queryBuilder = queryBuilder();
			Where<Article, Integer> where = queryBuilder.where();

			whereFilter(where, filter);
			if (firstArticle != null) {
				where.and().gt(COLUMN_DATETIME, firstArticle.getDateTime());
			}

			queryBuilder.orderBy(COLUMN_DATETIME, false);

			return queryBuilder.query();
		} catch (SQLException e) {
			throw new RuntimeException(e);
		}
	}

	public List<Article> queryForNext(Filter filter, Article article, long pageSize) {
		try {
			QueryBuilder<Article, Integer> queryBuilder = queryBuilder();
			Where<Article, Integer> where = queryBuilder.where();

			whereFilter(where, filter);

			if (article != null) {
				where.and().lt(COLUMN_DATETIME, article.getDateTime());
			}

			queryBuilder.orderBy(COLUMN_DATETIME, false);
			queryBuilder.offset(0l).limit(pageSize);

			return queryBuilder.query();
		} catch (SQLException e) {
			throw new RuntimeException(e);
		}
	}

	public int queryForCount() {
		try {
			return (int) queryBuilder().countOf();
		} catch (SQLException e) {
			throw new RuntimeException(e);
		}
	}

	public int queryForCount(ArticleType type) {
		return queryForCount(type, Tag.ALL);
	}

	public int queryForCount(ArticleType type, Tag tag) {
		try {
			QueryBuilder<Article, Integer> queryBuilder = queryBuilder();
			Where<Article, Integer> where = queryBuilder.where();

			whereTypeAndTag(where, type, tag);
			where.and().gt(COLUMN_ID, 0);

			return (int) queryBuilder.countOf();
		} catch (SQLException e) {
			throw new RuntimeException(e);
		}
	}

	public int queryForCount(ArticleType type, Source source) {
		try {
			QueryBuilder<Article, Integer> queryBuilder = queryBuilder();
			Where<Article, Integer> where = queryBuilder.where();

			whereTypeAndSource(where, type, source);
			where.and().gt(COLUMN_ID, 0);

			return (int) queryBuilder.countOf();
		} catch (SQLException e) {
			throw new RuntimeException(e);
		}
	}

	private void whereFilter(Where<Article, Integer> where, Filter filter) throws SQLException {
		if (filter.getTag() != null) {
			whereTypeAndTag(where, filter.getType(), filter.getTag());
		} else {
			whereTypeAndSource(where, filter.getType(), filter.getSource());
		}
	}

	private void whereType(Where<Article, Integer> where, ArticleType type) throws SQLException {
		if (type == ArticleType.Unread) {
			where.eq(COLUMN_UNREAD, true);
		} else if (type == ArticleType.Starred) {
			where.eq(COLUMN_STARRED, true);
		} else {
			where.lt(COLUMN_ID, 0);
		}
	}

	private void whereTypeAndTag(Where<Article, Integer> where, ArticleType type, Tag tag) throws SQLException {
		whereType(where, type);
		if (!Tag.ALL.equals(tag)) {
			where.and().like(COLUMN_TAGS, new SelectArg("%" + tag.getName(null) + "%"));
		}
	}

	private void whereTypeAndSource(Where<Article, Integer> where, ArticleType type, Source source) throws SQLException {
		whereType(where, type);
		where.and().like(COLUMN_SOURCE_ID, source.getId());
	}

	public String queryForLatestUpdateTime() {
		try {
			QueryBuilder<Article, Integer> queryBuilder = queryBuilder();
			queryBuilder.where().isNotNull(COLUMN_UPDATE_TIME);
			queryBuilder.orderBy(COLUMN_UPDATE_TIME, false);
			queryBuilder.limit(1l);
			Article latestUpdated = queryBuilder.queryForFirst();

			if (latestUpdated != null) {
				return latestUpdated.getUpdateTime();
			} else {
				return null;
			}
		} catch (SQLException e) {
			throw new RuntimeException(e);
		}
	}

	public void deleteCachedOlderThan(long dateTime) {
		try {
			DeleteBuilder<Article, Integer> deleteBuilder = deleteBuilder();
			deleteBuilder.where().lt(COLUMN_ID, 0).and().lt(COLUMN_DATETIME, dateTime);
			deleteBuilder.delete();
		} catch (SQLException e) {
			throw new RuntimeException(e);
		}
	}

	public void deleteUnread() {
		try {
			DeleteBuilder<Article, Integer> deleteBuilder = deleteBuilder();
			deleteBuilder.where().eq(COLUMN_UNREAD, true).and().gt(COLUMN_ID, 0);
			deleteBuilder.delete();
		} catch (SQLException e) {
			throw new RuntimeException(e);
		}
	}

	public void deleteFavorite() {
		try {
			DeleteBuilder<Article, Integer> deleteBuilder = deleteBuilder();
			deleteBuilder.where().eq(COLUMN_STARRED, true).and().gt(COLUMN_ID, 0);
			deleteBuilder.delete();
		} catch (SQLException e) {
			throw new RuntimeException(e);
		}
	}

	public void deleteReadNotFavoriteAndNotCached() {
		try {
			DeleteBuilder<Article, Integer> deleteBuilder = deleteBuilder();
			deleteBuilder.where()
					.eq(COLUMN_UNREAD, false)
					.and().eq(COLUMN_STARRED, false)
					.and().gt(COLUMN_ID, 0);
			deleteBuilder.delete();
		} catch (SQLException e) {
			throw new RuntimeException(e);
		}
	}

	public void deleteNotFavoriteOlderThan(long dateTime) {
		try {
			DeleteBuilder<Article, Integer> deleteBuilder = deleteBuilder();
			deleteBuilder.where()
					.eq(COLUMN_STARRED, false)
					.and().lt(COLUMN_DATETIME, dateTime);
			deleteBuilder.delete();
		} catch (SQLException e) {
			throw new RuntimeException(e);
		}
	}


	private void notifyCreation(Article article) {
		broadcast(ACTION_CREATION, article);
	}

	private void notifyUpdate(Article article) {
		broadcast(ACTION_UPDATE, article);
	}

	private void broadcast(String action, Article article) {
		if (context != null) {
			Intent intent = new Intent(action);
			intent.putExtra(EXTRA_ARTICLE, article);
			context.sendBroadcast(intent);
		} else {
			Log.w(ArticleDao.class.getSimpleName(), "Context not set, so changes are not broadcast");
		}
	}
} public class DatabaseHelper extends OrmLiteSqliteOpenHelper {

	public static final String ACTION_TABLES_CLEARED = "fr.ydelouis.selfoss.ACTION_TABLES_CLEARED";

	private static final String DATABASE_NAME = "selfoss.db";
	private static final int DATABASE_VERSION = 5;

	private Context context;

	public DatabaseHelper(Context context) {
		super(context, DATABASE_NAME, null, DATABASE_VERSION);
		this.context = context;
	}

	@Override
	public void onCreate(SQLiteDatabase sqLiteDatabase, ConnectionSource connectionSource) {
		try {
			TableUtils.createTable(connectionSource, Tag.class);
			TableUtils.createTable(connectionSource, Source.class);
			TableUtils.createTable(connectionSource, Article.class);
			TableUtils.createTable(connectionSource, ArticleSyncAction.class);
		} catch (SQLException e) {
			e.printStackTrace();
		}
	}

	@Override
	public void onUpgrade(SQLiteDatabase sqLiteDatabase, ConnectionSource connectionSource, int oldVersion, int newVersion) {
		try {
			if (oldVersion < 2) {
				TableUtils.dropTable(connectionSource, Article.class, true);
				TableUtils.createTable(connectionSource, Article.class);
			}
			if (oldVersion < 3) {
				TableUtils.createTable(connectionSource, Source.class);
			}
            if (oldVersion < 4) {
                TableUtils.dropTable(connectionSource, Article.class, true);
                TableUtils.createTable(connectionSource, Article.class);
            }
			if (oldVersion < 5) {
				TableUtils.dropTable(connectionSource, Article.class, true);
				TableUtils.createTable(connectionSource, Article.class);
			}
		} catch (SQLException e) {
			e.printStackTrace();
		}
	}

	public void clearTables() {
		try {
			TableUtils.clearTable(getConnectionSource(), Tag.class);
			TableUtils.clearTable(getConnectionSource(), Source.class);
			TableUtils.clearTable(getConnectionSource(), Article.class);
			TableUtils.clearTable(getConnectionSource(), ArticleSyncAction.class);
			context.sendBroadcast(new Intent(ACTION_TABLES_CLEARED));
		} catch (SQLException e) {
			e.printStackTrace();
		}
	}
} @EBean
public class SourceSync {

	public static final String ACTION_SYNC_SOURCES = "fr.ydelouis.selfoss.ACTION_SYNC_SOURCES";
	public static final String EXTRA_SOURCES = "sources";

	@RootContext protected Context context;
	@RestService protected SelfossRest selfossRest;
	@OrmLiteDao(helper = DatabaseHelper.class)
	protected RuntimeExceptionDao<Source, String> sourceDao;

	public void performSync() {
		List<Source> serverSources = selfossRest.listSources();
		List<Source> databaseSources = sourceDao.queryForAll();
		for (Source source : serverSources) {
			sourceDao.createOrUpdate(source);
			databaseSources.remove(source);
		}
		sourceDao.delete(databaseSources);
		sendSourceBroadcast(serverSources);
	}

	private void sendSourceBroadcast(List<Source> sources) {
		Intent intent = new Intent(ACTION_SYNC_SOURCES);
		intent.putExtra(EXTRA_SOURCES, new ArrayList<Source>(sources));
		context.sendBroadcast(intent);
	}

} @EBean
public class TagSync {

	public static final String ACTION_SYNC_TAGS = "fr.ydelouis.selfoss.ACTION_SYNC_TAGS";
	public static final String EXTRA_TAGS = "tags";

	@RootContext protected Context context;
	@RestService protected SelfossRest selfossRest;
	@OrmLiteDao(helper = DatabaseHelper.class)
	protected RuntimeExceptionDao<Tag, String> tagDao;

	public void performSync() {
		List<Tag> serverTags = selfossRest.listTags();
		List<Tag> databaseTags = tagDao.queryForAll();
		for (Tag tag : serverTags) {
			tagDao.createOrUpdate(tag);
			databaseTags.remove(tag);
		}
		tagDao.delete(databaseTags);
		sendTagBroadcast(serverTags);
	}

	private void sendTagBroadcast(List<Tag> tags) {
		Intent intent = new Intent(ACTION_SYNC_TAGS);
		intent.putExtra(EXTRA_TAGS, new ArrayList<Tag>(tags));
		context.sendBroadcast(intent);
	}

} @EViewGroup(R.layout.view_source)
public class SourceView extends RelativeLayout {

	private Source source;

	@Bean protected SelfossImageLoader imageLoader;
    @ViewById protected ImageView icon;
    @ViewById protected TextView letter;
	@ViewById protected TextView title;
	@ViewById protected TextView count;

	public SourceView(Context context) {
		super(context);
	}

	public SourceView(Context context, AttributeSet attrs) {
		super(context, attrs);
	}

	public SourceView(Context context, AttributeSet attrs, int defStyle) {
		super(context, attrs, defStyle);
	}

	public void setSource(Source source, List<Tag> tags) {
		this.source = source;
        setSourceImageOrLetter(tags);
		title.setText(source.getTitle());
		count.setText(String.valueOf(source.getUnread()));
		count.setVisibility(source.getUnread() == 0 ? View.GONE : View.VISIBLE);
	}

    public void setSourceImageOrLetter(List<Tag> tags) {
        icon.setVisibility(GONE);
        letter.setVisibility(GONE);
        if (source.getIcon() != null && !source.getIcon().isEmpty()) {
			imageLoader.displayFavicon(source, icon);
            icon.setVisibility(VISIBLE);
        } else if (source.getTitle() != null && !source.getTitle().isEmpty()) {
            letter.setText(source.getTitle().substring(0, 1).toUpperCase());
            letter.setBackgroundDrawable(new ColorsOvalDrawable(Tag.colorsOfTags(tags)));
            letter.setVisibility(VISIBLE);
        }
    }

    @Override
    public void setSelected(boolean selected) {
        title.setTextColor(getResources().getColor(selected ? R.color.main_color : R.color.text));
        title.setTypeface(null, selected ? Typeface.BOLD : Typeface.NORMAL);
    }

	public Source getSource() {
		return source;
	}
} @EViewGroup(R.layout.view_tag)
public class TagView extends RelativeLayout {

	private Tag tag;

	@ViewById protected View color;
	@ViewById protected TextView name;
	@ViewById protected TextView count;

	public TagView(Context context) {
		super(context);
	}

	public TagView(Context context, AttributeSet attrs) {
		super(context, attrs);
	}

	public TagView(Context context, AttributeSet attrs, int defStyle) {
		super(context, attrs, defStyle);
	}

	public void setTag(Tag tag) {
		this.tag = tag;
        ShapeDrawable colorDrawable = new ShapeDrawable(new OvalShape());
        colorDrawable.getPaint().setColor(tag.getColor());
		color.setBackgroundDrawable(colorDrawable);
		name.setText(tag.getName(getContext()));
		count.setText(String.valueOf(tag.getUnread()));
		count.setVisibility(tag.getUnread() == 0 ? View.GONE : View.VISIBLE);
	}

    public void setAllTags(List<Tag> tags) {
        color.setBackgroundDrawable(new ColorsOvalDrawable(Tag.colorsOfTags(tags)));
    }

    @Override
    public void setSelected(boolean selected) {
        name.setTextColor(getResources().getColor(selected ? R.color.main_color : R.color.text));
        name.setTypeface(null, selected ? Typeface.BOLD : Typeface.NORMAL);
    }

	public Tag getTag() {
		return tag;
	}
} @EViewGroup(R.layout.view_type)
public class TypeView extends RelativeLayout {

	private ArticleType type;

	@ViewById protected TextView name;
	@ViewById protected TextView count;

	public TypeView(Context context) {
		super(context);
	}

	public TypeView(Context context, AttributeSet attrs) {
		super(context, attrs);
	}

	public TypeView(Context context, AttributeSet attrs, int defStyle) {
		super(context, attrs, defStyle);
	}

	@AfterViews
	protected void initViews() {
		type = ArticleType.fromId(getId());
		name.setText(type.getName(getContext()));
	}

	public ArticleType getType() {
		return type;
	}

	public void setSelected(ArticleType type) {
		setSelected(this.type.equals(type));
	}

    @Override
    public void setSelected(boolean selected) {
        name.setTextColor(getResources().getColor(selected ? R.color.main_color : R.color.text));
        name.setTypeface(null, selected ? Typeface.BOLD : Typeface.NORMAL);
    }

    @UiThread
	public void setCount(int count) {
		this.count.setText(String.valueOf(count));
		this.count.setVisibility(count == 0 ? View.GONE : View.VISIBLE);
	}
}
import android.app.Fragment;
import android.content.Intent;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import com.j256.ormlite.dao.RuntimeExceptionDao;
import org.androidannotations.annotations.AfterViews;
import org.androidannotations.annotations.Background;
import org.androidannotations.annotations.Bean;
import org.androidannotations.annotations.Click;
import org.androidannotations.annotations.EFragment;
import org.androidannotations.annotations.FragmentArg;
import org.androidannotations.annotations.IgnoredWhenDetached;
import org.androidannotations.annotations.InstanceState;
import org.androidannotations.annotations.OrmLiteDao;
import org.androidannotations.annotations.Receiver;
import org.androidannotations.annotations.UiThread;
import org.androidannotations.annotations.ViewById;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import fr.ydelouis.selfoss.R;
import fr.ydelouis.selfoss.config.model.Config;
import fr.ydelouis.selfoss.config.ui.ConfigActivity_;
import fr.ydelouis.selfoss.config.model.ConfigManager;
import fr.ydelouis.selfoss.entity.ArticleType;
import fr.ydelouis.selfoss.entity.Filter;
import fr.ydelouis.selfoss.entity.Source;
import fr.ydelouis.selfoss.entity.Tag;
import fr.ydelouis.selfoss.model.ArticleDao;
import fr.ydelouis.selfoss.model.DatabaseHelper;
import fr.ydelouis.selfoss.sync.SourceSync;
import fr.ydelouis.selfoss.sync.TagSync;
import fr.ydelouis.selfoss.view.SourceView;
import fr.ydelouis.selfoss.view.SourceView_;
import fr.ydelouis.selfoss.view.TagView;
import fr.ydelouis.selfoss.view.TagView_;
import fr.ydelouis.selfoss.view.TypeView;
package fr.ydelouis.selfoss.fragment;






@EFragment(R.layout.fragment_menu)
public class MenuFragment extends Fragment implements View.OnClickListener {

	@Bean protected ConfigManager configManager;
	@FragmentArg @InstanceState
	protected Filter filter = new Filter();
	@OrmLiteDao(helper = DatabaseHelper.class)
	protected RuntimeExceptionDao<Tag, String> tagDao;
	@OrmLiteDao(helper = DatabaseHelper.class)
	protected RuntimeExceptionDao<Source, Integer> sourceDao;
	@OrmLiteDao(helper = DatabaseHelper.class)
	protected ArticleDao articleDao;
	private Listener listener = new DummyListener();

	@ViewById protected TextView url;
	@ViewById protected TypeView newest;
	@ViewById protected TypeView unread;
	@ViewById protected TypeView starred;
	@ViewById protected ViewGroup tagContainer;
	@ViewById protected ViewGroup sourceContainer;

	public void onOpened() {
		updateViews();
	}

	@AfterViews
	protected void updateViews() {
		articleDao.setContext(getActivity());
		Config config = configManager.get();
		if (config != null) {
			url.setText(config.getUrl());
		}
		updateTypes();
		loadAndUpdateTags();
		loadAndUpdateSources();
	}

	private void updateTypes() {
		for (TypeView typeView : new TypeView[] {newest, unread, starred}) {
			typeView.setSelected(filter.getType());
		}
		for (TypeView typeView : new TypeView[] {unread, starred}) {
			loadTypeCount(typeView);
		}
	}

	@Background
	protected void loadTypeCount(TypeView typeView) {
		typeView.setCount(articleDao.queryForCount(typeView.getType()));
	}

	@Background
	protected void loadAndUpdateTags() {
		List<Tag> tags = tagDao.queryForAll();
		for (Tag tag : tags) {
			tag.setUnread(articleDao.queryForCount(ArticleType.Unread, tag));
		}
		sortAndUpdateTags(tags);
	}

	@Background
	protected void loadAndUpdateSources() {
		List<Source> sources = sourceDao.queryForAll();
		for (Source source : sources) {
			source.setUnread(articleDao.queryForCount(ArticleType.Unread, source));
		}
		sortAndUpdateSources(sources);
	}

	@Receiver(actions = {TagSync.ACTION_SYNC_TAGS}, registerAt = Receiver.RegisterAt.OnResumeOnPause)