Java tutorial
/* * Copyright (c) 2015. Simas Abramovas * * This file is part of VideoClipper. * * VideoClipper 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. * * VideoClipper 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 VideoClipper. If not, see <http://www.gnu.org/licenses/>. */ package com.simas.vc.file_chooser; import android.app.Activity; import android.app.AlertDialog; import android.app.Dialog; import android.content.DialogInterface; import android.graphics.drawable.Drawable; import android.os.Bundle; import android.support.annotation.NonNull; import android.support.v4.app.DialogFragment; import android.util.Log; import android.view.Gravity; import android.view.KeyEvent; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.view.WindowManager; import android.widget.AdapterView; import android.widget.BaseAdapter; import android.widget.TextView; import com.simas.vc.R; import com.simas.vc.VC; import com.simas.vc.nav_drawer.NavItem; import com.simas.vc.helpers.Utils; import java.io.File; import java.util.ArrayList; import java.util.List; // ToDo save scroll position when going deeper // ToDo on-click-outside doesn't dismiss on Galaxy S2 /** * Custom dialog for choosing a file from the device's drive. */ public class FileChooser extends DialogFragment implements AdapterView.OnItemClickListener, DialogInterface.OnKeyListener { public static final String TAG = "FileChooser"; public static final String ARG_FROM_TOOLBAR = "from_toolbar"; private LayoutInflater mInflater; private FileChooserAdapter mAdapter; private OnFileChosenListener mChoiceListener; private static File sCurrentPath = new File("/sdcard/Movies"); // ToDo do not hardcode path private static FileChooser sInstance; public static FileChooser getInstance() { if (sInstance == null) { sInstance = new FileChooser(); } return sInstance; } public FileChooser() { } @Override public void onAttach(Activity activity) { super.onAttach(activity); // (Re)init the inflater mInflater = LayoutInflater.from(activity); // If previous directory was deleted/doesn't exist, go back to the root if (!sCurrentPath.exists()) sCurrentPath = new File("/"); } @NonNull @Override public Dialog onCreateDialog(Bundle savedInstanceState) { mAdapter = new FileChooserAdapter(); AlertDialog dialog = new AlertDialog.Builder(getActivity()).setAdapter(mAdapter, null).create(); /* Configure the dialog */ // Back listener dialog.setOnKeyListener(this); // Item click listener dialog.getListView().setOnItemClickListener(this); // Add header (up navigation) // Do it with the adapter removed, so lower APIs don't crash! dialog.getListView().setAdapter(null); View header = createHeader(dialog.getListView()); dialog.getListView().addHeaderView(header); dialog.getListView().setAdapter(mAdapter); // Show sub-files mAdapter.setFiles(getSubFiles(sCurrentPath)); return dialog; } @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { View view = super.onCreateView(inflater, container, savedInstanceState); // Make the window always appear on the top of the screen try { WindowManager.LayoutParams lp = new WindowManager.LayoutParams(); lp.copyFrom(getDialog().getWindow().getAttributes()); lp.gravity = Gravity.TOP | Gravity.CENTER_HORIZONTAL; getDialog().getWindow().setAttributes(lp); } catch (NullPointerException e) { Log.e(TAG, "Failed to set FileChooser window gravity!", e); } return view; } private View createHeader(ViewGroup parent) { View header = mInflater.inflate(R.layout.file_chooser_header, parent, false); header.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { navigateUp(); } }); header.setOnLongClickListener(new View.OnLongClickListener() { @Override public boolean onLongClick(View v) { dismiss(); return true; } }); return header; } @Override public void onItemClick(AdapterView<?> parent, View view, int position, long id) { parent.setSelected(false); view.setSelected(false); if (position != 0) { File file = mAdapter.getItem(position - 1); if (file.isDirectory()) { navigateTo(file); } else { getDialog().dismiss(); if (mChoiceListener != null && file.exists()) { mChoiceListener.onFileChosen(file); } } } } /** * Will go up by a level if possible, otherwise will dismiss the dialog. */ private void navigateUp() { File parent = sCurrentPath.getParentFile(); if (parent != null) { navigateTo(parent); } else { dismiss(); } } private void navigateTo(File directory) { // Set the current path sCurrentPath = directory; // Add all it's sub-files mAdapter.setFiles(getSubFiles(directory)); } private List<File> getSubFiles(File root) { List<File> validSubFiles = new ArrayList<>(); File[] files = root.listFiles(); if (files == null || files.length == 0) return validSubFiles; for (File file : files) { if (file.isDirectory()) { // Add all dirs validSubFiles.add(file); } else { // Add only files with specific extensions NavItem.Type type = NavItem.determineExtensionType(file); if (type == NavItem.Type.VIDEO || type == NavItem.Type.PICTURE) { validSubFiles.add(file); } // ToDo audio extension } } return validSubFiles; } @Override public boolean onKey(DialogInterface dialog, int keyCode, KeyEvent event) { if (keyCode == KeyEvent.KEYCODE_BACK && event.isLongPress()) { dismiss(); return true; } else if (keyCode == KeyEvent.KEYCODE_BACK && event.getAction() == KeyEvent.ACTION_UP) { navigateUp(); return true; } else { return false; } } private static final Drawable DIRECTORY_DRAWABLE, VIDEO_DRAWABLE, PICTURE_DRAWABLE; static { // Load drawables if (android.os.Build.VERSION.SDK_INT >= 21) { DIRECTORY_DRAWABLE = VC.getAppResources().getDrawable(R.drawable.ic_menu_archive, null); VIDEO_DRAWABLE = VC.getAppResources().getDrawable(R.drawable.ic_media_video_poster, null); PICTURE_DRAWABLE = VC.getAppResources().getDrawable(R.drawable.ic_menu_gallery, null); } else { DIRECTORY_DRAWABLE = VC.getAppResources().getDrawable(R.drawable.ic_menu_archive); VIDEO_DRAWABLE = VC.getAppResources().getDrawable(R.drawable.ic_media_video_poster); PICTURE_DRAWABLE = VC.getAppResources().getDrawable(R.drawable.ic_menu_gallery); } int pixels = (int) Utils.dpToPx(30); if (DIRECTORY_DRAWABLE != null && VIDEO_DRAWABLE != null && PICTURE_DRAWABLE != null) { DIRECTORY_DRAWABLE.setBounds(0, 0, pixels, pixels); VIDEO_DRAWABLE.setBounds(0, 0, pixels, pixels); PICTURE_DRAWABLE.setBounds(0, 0, pixels, pixels); } } private class FileChooserAdapter extends BaseAdapter { private List<File> mFiles; private class ViewHolder { TextView textView; } public void setFiles(List<File> files) { mFiles = files; notifyDataSetChanged(); } @Override public int getCount() { return (mFiles == null) ? 0 : mFiles.size(); } @Override public File getItem(int position) { return mFiles.get(position); } @Override public long getItemId(int position) { return position; } @Override public View getView(int position, View convertView, ViewGroup parent) { ViewHolder holder; if (convertView == null) { // Inflate the layout convertView = mInflater.inflate(R.layout.file_chooser_item, parent, false); // Save holder for later re-use holder = new ViewHolder(); holder.textView = (TextView) convertView.findViewById(R.id.file_item); convertView.setTag(holder); } else { holder = (ViewHolder) convertView.getTag(); } if (getItem(position).isDirectory()) { holder.textView.setCompoundDrawables(DIRECTORY_DRAWABLE, null, null, null); } else { // Drawable based on the extension switch (NavItem.determineExtensionType(getItem(position))) { case VIDEO: holder.textView.setCompoundDrawables(VIDEO_DRAWABLE, null, null, null); break; case PICTURE: holder.textView.setCompoundDrawables(PICTURE_DRAWABLE, null, null, null); break; case AUDIO: // ToDo audio drawable break; default: throw new IllegalArgumentException("Unrecognized extension!"); } } holder.textView.setText(getItem(position).getName()); return convertView; } } public void setOnFileChosenListener(OnFileChosenListener choiceListener) { mChoiceListener = choiceListener; } public interface OnFileChosenListener { void onFileChosen(File file); } }