/*
* Overchan Android (Meta Imageboard Client)
* Copyright (C) 2014-2016 miku-nyan <https://github.com/miku-nyan>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package nya.miku.wishmaster.ui.tabs;
import nya.miku.wishmaster.R;
import nya.miku.wishmaster.api.ChanModule;
import nya.miku.wishmaster.common.MainApplication;
import nya.miku.wishmaster.ui.HistoryFragment;
import nya.miku.wishmaster.ui.theme.ThemeUtils;
import android.content.Context;
import android.graphics.Color;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.LayerDrawable;
import android.support.v4.content.res.ResourcesCompat;
import android.util.TypedValue;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.ImageView;
import android.widget.TextView;
public class TabsAdapter extends ArrayAdapter<TabModel> {
private final LayoutInflater inflater;
private final Context context;
private final TabsState tabsState;
private final TabsIdStack tabsIdStack;
private final TabSelectListener selectListener;
private int selectedItem;
private int draggingItem = -1;
private final View.OnClickListener onCloseClick = new View.OnClickListener() {
@Override
public void onClick(View v) {
closeTab((Integer) v.getTag());
}
};
/**
* Конструктор адаптера.
* После создания и привязки к объекту списка необходимо дополнительно установить позицию текущей вкладки ({@link #setSelectedItem(int)})
* @param context контекст активности для получения темы (стиля) и инфлатера
* @param tabsState объект состояния вкладок
* @param selectListener интерфейс {@link TabSelectListener}, слушающий событие выбора (переключения) вкладки
*/
public TabsAdapter(Context context, TabsState tabsState, TabSelectListener selectListener) {
super(context, 0, tabsState.tabsArray);
this.inflater = LayoutInflater.from(context);
this.context = context;
this.tabsState = tabsState;
this.tabsIdStack = tabsState.tabsIdStack;
this.selectListener = selectListener;
}
/**
* Выбрать текущую вкладку (и переключиться на неё). Объект состояния вкладок будет сериализован
* @param position позиция вкладки в списке
*/
public void setSelectedItem(int position) {
setSelectedItem(position, true);
}
/**
* Выбрать текущую вкладку (и переключиться на неё)
* @param position позиция вкладки в списке
* @param serialize если true, сериализовать объект состояния вкладок
*/
public void setSelectedItem(int position, boolean serialize) {
selectedItem = position;
tabsState.position = position;
if (position >= 0) {
tabsIdStack.addTab(getItem(position).id);
}
notifyDataSetChanged(serialize);
selectListener.onTabSelected(position);
}
/**
* Выбрать текущую вкладку (и переключиться на неё) с поиском по ID вкладки
* @param id ID вкладки
*/
public void setSelectedItemId(long id) {
for (int i=0; i<getCount(); ++i) {
if (getItem(i).id == id) {
setSelectedItem(i);
break;
}
}
}
/**
* Установить или убрать маркер перемещения вкладки
* @param position позиция вкладки в списке или -1, если необходимо убрать маркер перемещения
*/
public void setDraggingItem(int position) {
draggingItem = position;
notifyDataSetChanged(false);
}
/**
* @return Возвращает позицию текущей выбранной вкладки
*/
public int getSelectedItem() {
return selectedItem;
}
/**
* @return Возвращает позицию текущей перемещаемой вкладки (с маркером перемещения)
* или -1, если перемещение не активно в данный момент
*/
public int getDraggingItem() {
return draggingItem;
}
/**
* Закрыть вкладку
* @param position позиция вкладки в списке
*/
public void closeTab(int position) {
setDraggingItem(-1);
if (position >= getCount()) return;
HistoryFragment.setLastClosed(tabsState.tabsArray.get(position));
tabsIdStack.removeTab(getItem(position).id);
remove(getItem(position), false);
if (position == selectedItem) {
if (!tabsIdStack.isEmpty()) {
setSelectedItemId(tabsIdStack.getCurrentTab());
} else {
if (getCount() == 0) {
setSelectedItem(TabModel.POSITION_NEWTAB);
} else {
if (getCount() <= position) --position;
setSelectedItem(position); //serialize
}
}
} else {
if (position < selectedItem) --selectedItem;
setSelectedItem(selectedItem); //serialize
}
}
/**
* Метод для обработки нажатия клавиши "Назад"
* @return
*/
public boolean back() {
if (selectedItem < 0) {
if (!tabsIdStack.isEmpty()) {
setSelectedItemId(tabsIdStack.getCurrentTab());
return true;
}
} else {
if (MainApplication.getInstance().settings.doNotCloseTabs()) {
tabsIdStack.removeTab(getItem(selectedItem).id);
if (tabsIdStack.isEmpty()) {
setSelectedItem(TabModel.POSITION_NEWTAB);
} else {
setSelectedItemId(tabsIdStack.getCurrentTab());
}
} else {
closeTab(selectedItem);
}
return true;
}
return false;
}
@Override
public View getView(final int position, View convertView, ViewGroup parent) {
View view = convertView == null ? inflater.inflate(R.layout.sidebar_tabitem, parent, false) : convertView;
View dragHandler = view.findViewById(R.id.tab_drag_handle);
ImageView favIcon = (ImageView)view.findViewById(R.id.tab_favicon);
TextView title = (TextView)view.findViewById(R.id.tab_text_view);
ImageView closeBtn = (ImageView)view.findViewById(R.id.tab_close_button);
dragHandler.getLayoutParams().width = position == draggingItem ? ViewGroup.LayoutParams.WRAP_CONTENT : 0;
dragHandler.setLayoutParams(dragHandler.getLayoutParams());
if (position == selectedItem) {
TypedValue typedValue = ThemeUtils.resolveAttribute(context.getTheme(), R.attr.sidebarSelectedItem, true);
if (typedValue.type >= TypedValue.TYPE_FIRST_COLOR_INT && typedValue.type <= TypedValue.TYPE_LAST_COLOR_INT) {
view.setBackgroundColor(typedValue.data);
} else {
view.setBackgroundResource(typedValue.resourceId);
}
} else {
view.setBackgroundColor(Color.TRANSPARENT);
}
TabModel model = this.getItem(position);
switch (model.type) {
case TabModel.TYPE_NORMAL:
case TabModel.TYPE_LOCAL:
closeBtn.setVisibility(View.VISIBLE);
String titleText = model.title;
if (model.unreadPostsCount > 0 || model.autoupdateError) {
StringBuilder titleStringBuilder = new StringBuilder();
if (model.unreadSubscriptions) titleStringBuilder.append("[*] ");
if (model.autoupdateError) titleStringBuilder.append("[X] ");
if (model.unreadPostsCount > 0) titleStringBuilder.append('[').append(model.unreadPostsCount).append("] ");
titleText = titleStringBuilder.append(titleText).toString();
}
title.setText(titleText);
ChanModule chan = MainApplication.getInstance().getChanModule(model.pageModel.chanName);
Drawable icon = chan != null ? chan.getChanFavicon() :
ResourcesCompat.getDrawable(context.getResources(), android.R.drawable.ic_delete, null);
if (icon != null) {
if (model.type == TabModel.TYPE_LOCAL) {
Drawable[] layers = new Drawable[] {
icon, ResourcesCompat.getDrawable(context.getResources(), R.drawable.favicon_overlay_local, null) };
icon = new LayerDrawable(layers);
}
/* XXX
Была идея помечать вкладки на автообновлении дополнительным значком (небольшой overlay поверх favicon в левом верхнем углу),
чтобы сразу видеть в списке вкладок, какие будут обновлены, а где автообновление отключено (по умолчанию включено везде).
Но в таком виде это выглядит плохо, возможно, стоит запилить-поискать какую-нибудь лёгкую иконку, чтобы не загромождать интерфейс.
Или придумать другой способ отображать это, или вообще это всё не нужно.
else if (model.type == TabModel.TYPE_NORMAL && model.pageModel != null &&
model.pageModel.type == UrlPageModel.TYPE_THREADPAGE && model.autoupdateBackground &&
MainApplication.getInstance().settings.isAutoupdateEnabled() &&
MainApplication.getInstance().settings.isAutoupdateBackground()) {
Drawable[] layers = new Drawable[] {
icon, ResourcesCompat.getDrawable(context.getResources(), R.drawable.favicon_overlay_autoupdate, null) };
icon = new LayerDrawable(layers);
}
*/
favIcon.setImageDrawable(icon);
favIcon.setVisibility(View.VISIBLE);
} else {
favIcon.setVisibility(View.GONE);
}
break;
default:
closeBtn.setVisibility(View.GONE);
title.setText(R.string.error_deserialization);
favIcon.setVisibility(View.GONE);
}
closeBtn.setTag(position);
closeBtn.setOnClickListener(onCloseClick);
return view;
}
@Override
public void notifyDataSetChanged() {
notifyDataSetChanged(true);
}
/**
* @param serialize если true, сериализовать объект состояния вкладок
*/
public void notifyDataSetChanged(boolean serialize) {
super.notifyDataSetChanged();
if (serialize) MainApplication.getInstance().serializer.serializeTabsState(tabsState);
}
@Override
public void add(TabModel object) {
add(object, true);
}
/**
* Добавить объект в конец массива
* @param object объект
* @param serialize если true, сериализовать объект состояния вкладок
*/
public void add(TabModel object, boolean serialize) {
setNotifyOnChange(false);
super.add(object);
notifyDataSetChanged(serialize);
}
@Override
public void remove(TabModel object) {
remove(object, true);
}
/**
* Удалить объект из массива
* @param object объект
* @param serialize если true, сериализовать объект состояния вкладок
*/
public void remove(TabModel object, boolean serialize) {
setNotifyOnChange(false);
super.remove(object);
notifyDataSetChanged(serialize);
}
@Override
public void insert(TabModel object, int index) {
insert(object, index, true);
}
/**
* Вставить объект в массив на заданную позицию (индекс)
* @param object объект
* @param index индекс, на который объект должен быть вставлен
* @param serialize если true, сериализовать объект состояния вкладок
*/
public void insert(TabModel object, int index, boolean serialize) {
setNotifyOnChange(false);
super.insert(object, index);
notifyDataSetChanged(serialize);
}
/**
* Интерфейс, слушающий событие выбора (переключения) вкладки.
* @author miku-nyan
*
*/
public static interface TabSelectListener {
/** Вызывается при переключении вкладки.
* Переключение может быть на ту же самую (открытую в данный момент) вкладку, например, при изменении её позиции в списке. */
public void onTabSelected(int position);
}
}