package com.battlelancer.seriesguide.ui.dialogs;
import android.content.Context;
import android.database.Cursor;
import android.net.Uri;
import android.os.Bundle;
import android.support.v4.app.DialogFragment;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentTransaction;
import android.support.v4.app.LoaderManager;
import android.support.v4.content.CursorLoader;
import android.support.v4.content.Loader;
import android.support.v4.widget.CursorAdapter;
import android.support.v7.app.AppCompatDialogFragment;
import android.text.TextUtils;
import android.util.SparseBooleanArray;
import android.view.LayoutInflater;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.Button;
import android.widget.Checkable;
import android.widget.CheckedTextView;
import android.widget.ListView;
import android.widget.TextView;
import com.battlelancer.seriesguide.R;
import com.battlelancer.seriesguide.SgApp;
import com.battlelancer.seriesguide.provider.SeriesGuideContract;
import com.battlelancer.seriesguide.provider.SeriesGuideContract.Episodes;
import com.battlelancer.seriesguide.provider.SeriesGuideContract.ListItems;
import com.battlelancer.seriesguide.provider.SeriesGuideContract.Lists;
import com.battlelancer.seriesguide.provider.SeriesGuideContract.Seasons;
import com.battlelancer.seriesguide.provider.SeriesGuideContract.Shows;
import com.battlelancer.seriesguide.provider.SeriesGuideDatabase.Tables;
import com.battlelancer.seriesguide.util.ListsTools;
import com.battlelancer.seriesguide.util.SeasonTools;
import java.util.ArrayList;
import java.util.List;
/**
* Displays a dialog displaying all lists, allowing to add the given show, season or episode to any
* number of them.
*/
public class ManageListsDialogFragment extends AppCompatDialogFragment implements
LoaderManager.LoaderCallbacks<Cursor>, OnItemClickListener {
public static ManageListsDialogFragment newInstance(int itemTvdbId,
@SeriesGuideContract.ListItemTypes int itemType) {
ManageListsDialogFragment f = new ManageListsDialogFragment();
Bundle args = new Bundle();
args.putInt(InitBundle.INT_ITEM_TVDB_ID, itemTvdbId);
args.putInt(InitBundle.INT_ITEM_TYPE, itemType);
f.setArguments(args);
return f;
}
public interface InitBundle {
String INT_ITEM_TVDB_ID = "item-tvdbid";
String INT_ITEM_TYPE = "item-type";
}
private ListView mListView;
private ListsAdapter mAdapter;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// hide title, use custom theme
setStyle(STYLE_NO_TITLE, 0);
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
final View layout = inflater.inflate(R.layout.dialog_manage_lists, container, false);
// buttons
Button dontAddButton = (Button) layout.findViewById(R.id.buttonNegative);
dontAddButton.setText(android.R.string.cancel);
dontAddButton.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
dismiss();
}
});
Button addButton = (Button) layout.findViewById(R.id.buttonPositive);
addButton.setText(android.R.string.ok);
addButton.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
// add item to selected lists, remove from previously selected lists
SparseBooleanArray checkedLists = mAdapter.getCheckedPositions();
List<String> addToTheseLists = new ArrayList<>();
List<String> removeFromTheseLists = new ArrayList<>();
for (int position = 0; position < mAdapter.getCount(); position++) {
final Cursor listEntry = (Cursor) mAdapter.getItem(position);
boolean wasListChecked = !TextUtils.isEmpty(listEntry
.getString(ListsQuery.LIST_ITEM_ID));
boolean isListChecked = checkedLists.get(position);
String listId = listEntry.getString(ListsQuery.LIST_ID);
if (wasListChecked && !isListChecked) {
// remove from list
removeFromTheseLists.add(listId);
} else if (!wasListChecked && isListChecked) {
// add to list
addToTheseLists.add(listId);
}
}
int itemTvdbId = getArguments().getInt(InitBundle.INT_ITEM_TVDB_ID);
int itemType = getArguments().getInt(InitBundle.INT_ITEM_TYPE);
ListsTools.changeListsOfItem(SgApp.from(getActivity()), itemTvdbId, itemType,
addToTheseLists, removeFromTheseLists);
dismiss();
}
});
// lists list
mListView = (ListView) layout.findViewById(R.id.list);
/*
* As using CHOICE_MODE_MULTIPLE does not seem to work before Jelly
* Bean, do everything ourselves.
*/
mListView.setOnItemClickListener(this);
return layout;
}
@Override
public void onActivityCreated(Bundle args) {
super.onActivityCreated(args);
// display item title
final int itemTvdbId = getArguments().getInt(InitBundle.INT_ITEM_TVDB_ID);
final int itemType = getArguments().getInt(InitBundle.INT_ITEM_TYPE);
final TextView itemTitle = (TextView) getView().findViewById(R.id.item);
Uri uri = null;
String[] projection = null;
switch (itemType) {
case 1:
// show
uri = Shows.buildShowUri(itemTvdbId);
projection = new String[] {
Shows._ID, Shows.TITLE
};
break;
case 2:
// season
uri = Seasons.buildSeasonUri(itemTvdbId);
projection = new String[] {
Seasons._ID, Seasons.COMBINED
};
break;
case 3:
// episode
uri = Episodes.buildEpisodeUri(itemTvdbId);
projection = new String[] {
Episodes._ID, Episodes.TITLE
};
break;
}
if (uri != null && projection != null) {
Cursor item = getActivity().getContentResolver().query(uri, projection, null, null,
null);
if (item != null && item.moveToFirst()) {
if (itemType == 2) {
// season just has a number, build string
itemTitle.setText(SeasonTools.getSeasonString(getActivity(), item.getInt(1)));
} else {
// shows and episodes
itemTitle.setText(item.getString(1));
}
}
if (item != null) {
item.close();
}
}
mAdapter = new ListsAdapter(getActivity());
mListView.setAdapter(mAdapter);
getLoaderManager().initLoader(0, getArguments(), this);
}
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
Checkable checkable = (Checkable) view;
checkable.toggle();
mAdapter.setItemChecked(position, checkable.isChecked());
}
@Override
public Loader<Cursor> onCreateLoader(int id, Bundle args) {
// filter for this item, but keep other lists
int itemTvdbId = args.getInt(InitBundle.INT_ITEM_TVDB_ID);
int itemType = args.getInt(InitBundle.INT_ITEM_TYPE);
return new CursorLoader(getActivity(), Lists.buildListsWithListItemUri(ListItems
.generateListItemIdWildcard(itemTvdbId, itemType)),
ListsQuery.PROJECTION, null, null, Lists.SORT_ORDER_THEN_NAME);
}
@Override
public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
mAdapter.swapCursor(data);
}
@Override
public void onLoaderReset(Loader<Cursor> loader) {
mAdapter.swapCursor(null);
}
private class ListsAdapter extends CursorAdapter {
private LayoutInflater mInflater;
private SparseBooleanArray mCheckedItems;
public ListsAdapter(Context context) {
super(context, null, 0);
mInflater = LayoutInflater.from(context);
mCheckedItems = new SparseBooleanArray();
}
@Override
public void bindView(View view, Context context, Cursor cursor) {
CheckedTextView checkedView = (CheckedTextView) view.findViewById(android.R.id.text1);
checkedView.setText(cursor.getString(ListsQuery.NAME));
int position = cursor.getPosition();
// prefer state set by user over database
boolean isChecked;
if (mCheckedItems.indexOfKey(position) >= 0) {
// user has changed checked state, prefer it
isChecked = mCheckedItems.get(position);
} else {
// otherwise prefer database state, check if item is in this list
String itemId = cursor.getString(ListsQuery.LIST_ITEM_ID);
isChecked = !TextUtils.isEmpty(itemId);
mCheckedItems.put(position, isChecked);
}
checkedView.setChecked(isChecked);
}
@Override
public View newView(Context context, Cursor cursor, ViewGroup parent) {
return mInflater.inflate(R.layout.item_list_checked, parent, false);
}
public void setItemChecked(int position, boolean value) {
mCheckedItems.put(position, value);
}
public SparseBooleanArray getCheckedPositions() {
return mCheckedItems;
}
}
interface ListsQuery {
String[] PROJECTION = new String[] {
Tables.LISTS + "." + Lists._ID, Tables.LISTS + "." + Lists.LIST_ID, Lists.NAME,
ListItems.LIST_ITEM_ID
};
int LIST_ID = 1;
int NAME = 2;
int LIST_ITEM_ID = 3;
}
/**
* Display a dialog which asks if the user wants to add the given show to one or more lists.
*
* @param itemTvdbId TVDb id of the item to add
* @param itemType type of the item to add (show, season or episode)
*/
public static void showListsDialog(int itemTvdbId,
@SeriesGuideContract.ListItemTypes int itemType, FragmentManager fm) {
if (fm == null) {
return;
}
// DialogFragment.show() will take care of adding the fragment
// in a transaction. We also want to remove any currently showing
// dialog, so make our own transaction and take care of that here.
FragmentTransaction ft = fm.beginTransaction();
Fragment prev = fm.findFragmentByTag("listsdialog");
if (prev != null) {
ft.remove(prev);
}
ft.addToBackStack(null);
// Create and show the dialog.
DialogFragment newFragment = ManageListsDialogFragment.newInstance(itemTvdbId, itemType);
newFragment.show(ft, "listsdialog");
}
}