/*
* Copyright (C) 2012 The CyanogenMod Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.cyanogenmod.filemanager.adapters;
import android.content.Context;
import android.content.res.Resources;
import android.graphics.drawable.Drawable;
import android.view.LayoutInflater;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.ImageButton;
import android.widget.ImageView;
import android.widget.TextView;
import com.cyanogenmod.filemanager.R;
import com.cyanogenmod.filemanager.model.FileSystemObject;
import com.cyanogenmod.filemanager.model.ParentDirectory;
import com.cyanogenmod.filemanager.ui.IconHolder;
import com.cyanogenmod.filemanager.ui.ThemeManager;
import com.cyanogenmod.filemanager.ui.ThemeManager.Theme;
import com.cyanogenmod.filemanager.util.FileHelper;
import com.cyanogenmod.filemanager.util.MimeTypeHelper;
import java.text.DateFormat;
import java.util.ArrayList;
import java.util.List;
/**
* An implementation of {@link ArrayAdapter} for display file system objects.
*/
public class FileSystemObjectAdapter
extends ArrayAdapter<FileSystemObject> implements OnClickListener {
/**
* An interface to communicate selection changes events.
*/
public interface OnSelectionChangedListener {
/**
* Method invoked when the selection changed.
*
* @param selectedItems The new selected items
*/
void onSelectionChanged(List<FileSystemObject> selectedItems);
}
/**
* A class that conforms with the ViewHolder pattern to performance
* the list view rendering.
*/
private static class ViewHolder {
/**
* @hide
*/
public ViewHolder() {
super();
}
ImageButton mBtCheck;
ImageView mIvIcon;
TextView mTvName;
TextView mTvSummary;
TextView mTvSize;
}
/**
* A class that holds the full data information.
*/
private static class DataHolder {
/**
* @hide
*/
public DataHolder() {
super();
}
boolean mSelected;
Drawable mDwCheck;
Drawable mDwIcon;
String mName;
String mSummary;
String mSize;
}
private DataHolder[] mData;
private IconHolder mIconHolder;
private final int mItemViewResourceId;
private List<FileSystemObject> mSelectedItems;
private final boolean mPickable;
private OnSelectionChangedListener mOnSelectionChangedListener;
//The resource of the item check
private static final int RESOURCE_ITEM_CHECK = R.id.navigation_view_item_check;
//The resource of the item icon
private static final int RESOURCE_ITEM_ICON = R.id.navigation_view_item_icon;
//The resource of the item name
private static final int RESOURCE_ITEM_NAME = R.id.navigation_view_item_name;
//The resource of the item summary information
private static final int RESOURCE_ITEM_SUMMARY = R.id.navigation_view_item_summary;
//The resource of the item size information
private static final int RESOURCE_ITEM_SIZE = R.id.navigation_view_item_size;
/**
* Constructor of <code>FileSystemObjectAdapter</code>.
*
* @param context The current context
* @param files The list of file system objects
* @param itemViewResourceId The identifier of the layout that represents an item
* of the list adapter
* @param pickable If the adapter should act as a pickable browser.
*/
public FileSystemObjectAdapter(
Context context, List<FileSystemObject> files,
int itemViewResourceId, boolean pickable) {
super(context, RESOURCE_ITEM_NAME, files);
this.mIconHolder = new IconHolder();
this.mItemViewResourceId = itemViewResourceId;
this.mSelectedItems = new ArrayList<FileSystemObject>();
this.mPickable = pickable;
//Do cache of the data for better performance
loadDefaultIcons();
processData(files);
}
/**
* Method that sets the listener which communicates selection changes.
*
* @param onSelectionChangedListener The listener reference
*/
public void setOnSelectionChangedListener(
OnSelectionChangedListener onSelectionChangedListener) {
this.mOnSelectionChangedListener = onSelectionChangedListener;
}
/**
* Method that loads the default icons (known icons and more common icons).
*/
private void loadDefaultIcons() {
this.mIconHolder.getDrawable(getContext(), "ic_fso_folder_drawable"); //$NON-NLS-1$
this.mIconHolder.getDrawable(getContext(), "ic_fso_default_drawable"); //$NON-NLS-1$
}
/**
* {@inheritDoc}
*/
@Override
public void notifyDataSetChanged() {
processData(null);
super.notifyDataSetChanged();
}
/**
* Method that dispose the elements of the adapter.
*/
public void dispose() {
clear();
this.mData = null;
this.mIconHolder = null;
this.mSelectedItems.clear();
}
/**
* Method that returns the {@link FileSystemObject} reference from his path.
*
* @param path The path of the file system object
* @return FileSystemObject The file system object reference
*/
public FileSystemObject getItem(String path) {
int cc = getCount();
for (int i = 0; i < cc; i++) {
//File system object info
FileSystemObject fso = getItem(i);
if (fso.getFullPath().compareTo(path) == 0) {
return fso;
}
}
return null;
}
/**
* Method that process the data before use {@link #getView} method.
*
* @param files The list of files (to better performance) or null.
*/
private void processData(List<FileSystemObject> files) {
Theme theme = ThemeManager.getCurrentTheme(getContext());
Resources res = getContext().getResources();
DateFormat df = DateFormat.getDateTimeInstance(DateFormat.SHORT, DateFormat.SHORT);
this.mData = new DataHolder[getCount()];
int cc = (files == null) ? getCount() : files.size();
for (int i = 0; i < cc; i++) {
//File system object info
FileSystemObject fso = (files == null) ? getItem(i) : files.get(i);
//Parse the last modification time and permissions
StringBuilder sbSummary = new StringBuilder();
if (fso instanceof ParentDirectory) {
sbSummary.append(res.getString(R.string.parent_dir));
} else {
sbSummary.append(df.format(fso.getLastModifiedTime()));
sbSummary.append(" "); //$NON-NLS-1$
sbSummary.append(fso.toRawPermissionString());
}
//Build the data holder
this.mData[i] = new FileSystemObjectAdapter.DataHolder();
this.mData[i].mSelected = this.mSelectedItems.contains(fso);
if (this.mData[i].mSelected) {
this.mData[i].mDwCheck =
theme.getDrawable(
getContext(), "checkbox_selected_drawable"); //$NON-NLS-1$
} else {
this.mData[i].mDwCheck =
theme.getDrawable(
getContext(), "checkbox_deselected_drawable"); //$NON-NLS-1$
}
this.mData[i].mDwIcon = this.mIconHolder.getDrawable(
getContext(),
MimeTypeHelper.getIcon(getContext(), fso));
this.mData[i].mName = fso.getName();
this.mData[i].mSummary = sbSummary.toString();
this.mData[i].mSize = FileHelper.getHumanReadableSize(fso);
}
}
/**
* {@inheritDoc}
*/
@Override
public View getView(int position, View convertView, ViewGroup parent) {
//Check to reuse view
View v = convertView;
if (v == null) {
//Create the view holder
LayoutInflater li =
(LayoutInflater)getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
v = li.inflate(this.mItemViewResourceId, parent, false);
ViewHolder viewHolder = new FileSystemObjectAdapter.ViewHolder();
viewHolder.mIvIcon = (ImageView)v.findViewById(RESOURCE_ITEM_ICON);
viewHolder.mTvName = (TextView)v.findViewById(RESOURCE_ITEM_NAME);
viewHolder.mTvSummary = (TextView)v.findViewById(RESOURCE_ITEM_SUMMARY);
viewHolder.mTvSize = (TextView)v.findViewById(RESOURCE_ITEM_SIZE);
if (!this.mPickable) {
viewHolder.mBtCheck = (ImageButton)v.findViewById(RESOURCE_ITEM_CHECK);
viewHolder.mBtCheck.setOnClickListener(this);
} else {
viewHolder.mBtCheck = (ImageButton)v.findViewById(RESOURCE_ITEM_CHECK);
viewHolder.mBtCheck.setVisibility(View.GONE);
}
v.setTag(viewHolder);
}
//Retrieve data holder
final DataHolder dataHolder = this.mData[position];
//Retrieve the view holder
ViewHolder viewHolder = (ViewHolder)v.getTag();
// Apply the current theme
Theme theme = ThemeManager.getCurrentTheme(getContext());
theme.setBackgroundDrawable(
getContext(), v, "background_drawable"); //$NON-NLS-1$
theme.setTextColor(
getContext(), viewHolder.mTvName, "text_color"); //$NON-NLS-1$
if (viewHolder.mTvSummary != null) {
theme.setTextColor(
getContext(), viewHolder.mTvSummary, "text_color"); //$NON-NLS-1$
}
if (viewHolder.mTvSize != null) {
theme.setTextColor(
getContext(), viewHolder.mTvSize, "text_color"); //$NON-NLS-1$
}
//Set the data
viewHolder.mIvIcon.setImageDrawable(dataHolder.mDwIcon);
viewHolder.mTvName.setText(dataHolder.mName);
if (viewHolder.mTvSummary != null) {
viewHolder.mTvSummary.setText(dataHolder.mSummary);
}
if (viewHolder.mTvSize != null) {
viewHolder.mTvSize.setText(dataHolder.mSize);
}
if (!this.mPickable) {
viewHolder.mBtCheck.setVisibility(
dataHolder.mName.compareTo(
FileHelper.PARENT_DIRECTORY) == 0 ? View.INVISIBLE : View.VISIBLE);
viewHolder.mBtCheck.setImageDrawable(dataHolder.mDwCheck);
viewHolder.mBtCheck.setTag(Integer.valueOf(position));
// Apply theme
if (dataHolder.mSelected) {
theme.setBackgroundDrawable(
getContext(), v, "selectors_selected_drawable"); //$NON-NLS-1$
} else {
theme.setBackgroundDrawable(
getContext(), v, "selectors_deselected_drawable"); //$NON-NLS-1$
}
}
//Return the view
return v;
}
/**
* Method that returns if the item of the passed position is selected.
*
* @param position The position of the item
* @return boolean If the item of the passed position is selected
*/
public boolean isSelected(int position) {
return this.mData[position].mSelected;
}
/**
* Method that selects in the {@link ArrayAdapter} the passed item.
*
* @param item The view to select
*/
public void toggleSelection(View item) {
ImageButton view = (ImageButton)item.findViewById(RESOURCE_ITEM_CHECK);
onClick(view);
}
/**
* Method that selects in the {@link ArrayAdapter} the passed item.
*
* @param fso The file system object to select
*/
public void toggleSelection(FileSystemObject fso) {
toggleSelection(null, fso);
}
/**
* Method that selects in the {@link ArrayAdapter} the passed item.
*
* @param v The check view object (can be null)
* @param fso The file system object to select
*/
private void toggleSelection(View v, FileSystemObject fso) {
if (this.mData != null) {
Theme theme = ThemeManager.getCurrentTheme(getContext());
int cc = this.mData.length;
for (int i = 0; i < cc; i++) {
DataHolder data = this.mData[i];
if (data.mName.compareTo(fso.getName()) == 0) {
//Select/Deselect the item
data.mSelected = !data.mSelected;
if (v != null) {
((View)v.getParent()).setSelected(data.mSelected);
}
if (data.mSelected) {
data.mDwCheck =
theme.getDrawable(
getContext(), "checkbox_selected_drawable"); //$NON-NLS-1$
} else {
data.mDwCheck =
theme.getDrawable(
getContext(),
"checkbox_deselected_drawable"); //$NON-NLS-1$
}
if (v != null) {
((ImageView)v).setImageDrawable(data.mDwCheck);
if (data.mSelected) {
theme.setBackgroundDrawable(
getContext(),
(View)v.getParent(),
"selectors_selected_drawable"); //$NON-NLS-1$
} else {
theme.setBackgroundDrawable(
getContext(),
(View)v.getParent(),
"selectors_deselected_drawable"); //$NON-NLS-1$
}
}
//Add or remove from the global selected items
if (data.mSelected) {
FileSystemObjectAdapter.this.mSelectedItems.add(fso);
} else {
FileSystemObjectAdapter.this.mSelectedItems.remove(fso);
}
//Communicate event
if (this.mOnSelectionChangedListener != null) {
List<FileSystemObject> selection =
new ArrayList<FileSystemObject>(
FileSystemObjectAdapter.this.mSelectedItems);
this.mOnSelectionChangedListener.onSelectionChanged(selection);
}
//Found
return;
}
}
}
}
/**
* Method that deselect all items.
*/
public void deselectedAll() {
this.mSelectedItems.clear();
doSelectDeselectAllVisibleItems(false);
}
/**
* Method that select all visible items.
*/
public void selectedAllVisibleItems() {
doSelectDeselectAllVisibleItems(true);
}
/**
* Method that deselect all visible items.
*/
public void deselectedAllVisibleItems() {
doSelectDeselectAllVisibleItems(false);
}
/**
* Method that select/deselect all items.
*
* @param select Indicates if select (true) or deselect (false) all items.
*/
private void doSelectDeselectAllVisibleItems(boolean select) {
if (this.mData != null && this.mData.length > 0) {
Theme theme = ThemeManager.getCurrentTheme(getContext());
int cc = this.mData.length;
for (int i = 0; i < cc; i++) {
DataHolder data = this.mData[i];
if (data.mName.compareTo(FileHelper.PARENT_DIRECTORY) == 0) {
// No select the parent directory
continue;
}
data.mSelected = select;
if (data.mSelected) {
data.mDwCheck =
theme.getDrawable(
getContext(), "checkbox_selected_drawable"); //$NON-NLS-1$
} else {
data.mDwCheck =
theme.getDrawable(
getContext(), "checkbox_deselected_drawable"); //$NON-NLS-1$
}
//Add or remove from the global selected items
FileSystemObject fso = getItem(i);
if (data.mSelected) {
FileSystemObjectAdapter.this.mSelectedItems.add(fso);
} else {
if (FileSystemObjectAdapter.this.mSelectedItems.contains(fso)) {
FileSystemObjectAdapter.this.mSelectedItems.remove(fso);
}
}
}
//Communicate event
if (this.mOnSelectionChangedListener != null) {
List<FileSystemObject> selection =
new ArrayList<FileSystemObject>(
FileSystemObjectAdapter.this.mSelectedItems);
this.mOnSelectionChangedListener.onSelectionChanged(selection);
}
// The internal structure was update, only super adapter need to be notified
super.notifyDataSetChanged();
}
}
/**
* Method that returns the selected items.
*
* @return List<FileSystemObject> The selected items
*/
public List<FileSystemObject> getSelectedItems() {
return new ArrayList<FileSystemObject>(this.mSelectedItems);
}
/**
* Method that sets the selected items.
*
* @param selectedItems The selected items
*/
public void setSelectedItems(List<FileSystemObject> selectedItems) {
this.mSelectedItems = selectedItems;
}
/**
* {@inheritDoc}
*/
@Override
public void onClick(View v) {
//Select or deselect the item
int pos = ((Integer)v.getTag()).intValue();
//Retrieve data holder
final FileSystemObject fso = getItem(pos);
//What button was pressed?
switch (v.getId()) {
case RESOURCE_ITEM_CHECK:
//Get the row item view
toggleSelection(v, fso);
break;
default:
break;
}
}
/**
* Method that should be invoked when the theme of the app was changed
*/
public void notifyThemeChanged() {
// Empty icon holder
this.mIconHolder = new IconHolder();
}
}