/* Copyright (C) 2023-2024 akasaka / Genjitsu Labs
This file is part of Gadgetbridge.
Gadgetbridge is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published
by the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Gadgetbridge 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 Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see . */
package nodomain.freeyourgadget.gadgetbridge.activities.files;
import android.content.Context;
import android.content.Intent;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Filter;
import android.widget.ImageView;
import android.widget.PopupMenu;
import android.widget.TextView;
import android.widget.Toast;
import androidx.annotation.NonNull;
import androidx.appcompat.content.res.AppCompatResources;
import androidx.recyclerview.widget.RecyclerView;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.File;
import java.io.IOException;
import java.text.DecimalFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import nodomain.freeyourgadget.gadgetbridge.R;
import nodomain.freeyourgadget.gadgetbridge.util.AndroidUtils;
import nodomain.freeyourgadget.gadgetbridge.util.GB;
public class FileManagerAdapter extends RecyclerView.Adapter {
protected static final Logger LOG = LoggerFactory.getLogger(FileManagerAdapter.class);
private final List fileList;
private final Context mContext;
private final FileFilter fileFilter;
public FileManagerAdapter(final Context context, final File directory) {
mContext = context;
// FIXME: This can be slow, make it async
fileList = new ArrayList<>(Arrays.asList(directory.listFiles()));
fileList.sort((f1, f2) -> {
if (f1.isDirectory() && f2.isFile())
return -1;
if (f1.isFile() && f2.isDirectory())
return 1;
return String.CASE_INSENSITIVE_ORDER.compare(f1.getName(), f2.getName());
});
fileFilter = new FileFilter(this, fileList);
}
@NonNull
@Override
public FileManagerViewHolder onCreateViewHolder(@NonNull final ViewGroup parent, final int viewType) {
final View view = LayoutInflater.from(mContext).inflate(R.layout.item_file_manager, parent, false);
return new FileManagerViewHolder(view);
}
@Override
public void onBindViewHolder(final FileManagerViewHolder holder, int position) {
final File file = fileList.get(position);
holder.name.setText(file.getName());
if (file.isDirectory()) {
holder.icon.setImageDrawable(AppCompatResources.getDrawable(mContext, R.drawable.ic_folder));
holder.description.setVisibility(View.GONE);
holder.menu.setVisibility(View.GONE);
} else {
holder.icon.setImageDrawable(AppCompatResources.getDrawable(mContext, R.drawable.ic_file_open));
holder.description.setText(formatFileSize(file.length()));
holder.description.setVisibility(View.VISIBLE);
holder.menu.setVisibility(View.VISIBLE);
holder.menu.setOnClickListener(view -> {
final PopupMenu menu = new PopupMenu(mContext, holder.menu);
menu.inflate(R.menu.file_manager_file);
menu.setOnMenuItemClickListener(item -> {
final int itemId = item.getItemId();
if (itemId == R.id.file_manager_file_menu_share) {
try {
AndroidUtils.shareFile(mContext, file, "*/*");
} catch (final IOException e) {
GB.toast("Failed to share file", Toast.LENGTH_LONG, GB.ERROR, e);
}
return true;
}
return false;
});
menu.show();
});
}
holder.itemView.setOnClickListener(v -> {
if (file.isDirectory()) {
final Intent fileManagerIntent = new Intent(mContext, FileManagerActivity.class);
fileManagerIntent.putExtra(FileManagerActivity.EXTRA_PATH, file.getPath());
mContext.startActivity(fileManagerIntent);
} else {
try {
AndroidUtils.viewFile(file.getAbsolutePath(), "*/*", mContext);
} catch (final IOException e) {
GB.toast("Failed to open file", Toast.LENGTH_LONG, GB.ERROR, e);
}
}
});
}
@Override
public int getItemCount() {
return fileList.size();
}
public Filter getFilter() {
return fileFilter;
}
public static class FileManagerViewHolder extends RecyclerView.ViewHolder {
final ImageView icon;
final TextView name;
final TextView description;
final ImageView menu;
FileManagerViewHolder(View itemView) {
super(itemView);
icon = itemView.findViewById(R.id.file_icon);
name = itemView.findViewById(R.id.file_name);
description = itemView.findViewById(R.id.file_description);
menu = itemView.findViewById(R.id.file_menu);
}
}
private static final DecimalFormat SIZE_FORMAT = new DecimalFormat("#,##0.#");
public static String formatFileSize(final long size) {
if (size <= 0) return "0";
final String[] units = new String[]{"B", "kB", "MB", "GB", "TB", "PB", "EB"};
int digitGroups = (int) (Math.log10(size) / Math.log10(1024));
return SIZE_FORMAT.format(size / Math.pow(1024, digitGroups)) + " " + units[digitGroups];
}
private class FileFilter extends Filter {
private final FileManagerAdapter adapter;
private final List originalList;
private final List filteredList;
private FileFilter(final FileManagerAdapter adapter, final List originalList) {
super();
this.originalList = new ArrayList<>(originalList);
this.filteredList = new ArrayList<>();
this.adapter = adapter;
}
@Override
protected FilterResults performFiltering(final CharSequence filter) {
filteredList.clear();
final Filter.FilterResults results = new Filter.FilterResults();
if (filter == null || filter.length() == 0)
filteredList.addAll(originalList);
else {
final String filterPattern = filter.toString().toLowerCase().trim();
for (File f : originalList) {
final CharSequence name = f.getName();
if (name.toString().toLowerCase().contains(filterPattern)) {
filteredList.add(f);
}
}
}
results.values = filteredList;
results.count = filteredList.size();
return results;
}
@Override
protected void publishResults(final CharSequence constraint, final FilterResults filterResults) {
adapter.fileList.clear();
adapter.fileList.addAll((List) filterResults.values);
adapter.notifyDataSetChanged();
}
}
}