com.github.mjdev.libaums.usbfileman.MainActivity.java Source code

Java tutorial

Introduction

Here is the source code for com.github.mjdev.libaums.usbfileman.MainActivity.java

Source

/*
 * (C) Copyright 2014-2016 mjahnen <jahnen@in.tum.de>
 *
 * 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.github.mjdev.libaums.usbfileman;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.nio.ByteBuffer;
import java.util.ArrayDeque;
import java.util.Deque;
import java.util.NoSuchElementException;

import android.Manifest;
import android.app.Activity;
import android.app.AlertDialog;
import android.app.Dialog;
import android.app.DialogFragment;
import android.app.PendingIntent;
import android.app.ProgressDialog;
import android.content.ActivityNotFoundException;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.ServiceConnection;
import android.content.pm.PackageManager;
import android.database.Cursor;
import android.hardware.usb.UsbDevice;
import android.hardware.usb.UsbManager;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.Build;
import android.os.Bundle;
import android.os.Environment;
import android.os.IBinder;
import android.provider.OpenableColumns;
import android.support.annotation.NonNull;
import android.support.v4.app.ActivityCompat;
import android.support.v4.content.ContextCompat;
import android.support.v4.content.FileProvider;
import android.support.v7.app.ActionBar;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.view.ContextMenu;
import android.view.ContextMenu.ContextMenuInfo;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.widget.AdapterView;
import android.widget.AdapterView.AdapterContextMenuInfo;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.EditText;
import android.widget.LinearLayout;
import android.widget.ListView;
import android.widget.TextView;
import android.widget.Toast;

import com.github.mjdev.libaums.UsbMassStorageDevice;
import com.github.mjdev.libaums.fs.FileSystem;
import com.github.mjdev.libaums.fs.UsbFile;
import com.github.mjdev.libaums.fs.UsbFileInputStream;
import com.github.mjdev.libaums.fs.UsbFileOutputStream;
import com.github.mjdev.libaums.fs.UsbFileStreamFactory;
import com.github.mjdev.libaums.server.http.UsbFileHttpServerService;
import com.github.mjdev.libaums.server.http.server.AsyncHttpServer;
import com.github.mjdev.libaums.server.http.server.NanoHttpdServer;

/**
 * MainActivity of the demo application which shows the contents of the first
 * partition.
 * 
 * @author mjahnen
 * 
 */
public class MainActivity extends AppCompatActivity implements OnItemClickListener {

    /**
     * Action string to request the permission to communicate with an UsbDevice.
     */
    private static final String ACTION_USB_PERMISSION = "com.github.mjdev.libaums.USB_PERMISSION";
    private static final String TAG = MainActivity.class.getSimpleName();

    private static final int COPY_STORAGE_PROVIDER_RESULT = 0;
    private static final int OPEN_STORAGE_PROVIDER_RESULT = 1;

    private static final int REQUEST_EXT_STORAGE_WRITE_PERM = 0;

    private final BroadcastReceiver usbReceiver = new BroadcastReceiver() {
        @Override
        public void onReceive(Context context, Intent intent) {

            String action = intent.getAction();
            if (ACTION_USB_PERMISSION.equals(action)) {

                UsbDevice device = (UsbDevice) intent.getParcelableExtra(UsbManager.EXTRA_DEVICE);
                if (intent.getBooleanExtra(UsbManager.EXTRA_PERMISSION_GRANTED, false)) {

                    if (device != null) {
                        setupDevice();
                    }
                }

            } else if (UsbManager.ACTION_USB_DEVICE_ATTACHED.equals(action)) {
                UsbDevice device = (UsbDevice) intent.getParcelableExtra(UsbManager.EXTRA_DEVICE);

                Log.d(TAG, "USB device attached");

                // determine if connected device is a mass storage devuce
                if (device != null) {
                    discoverDevice();
                }
            } else if (UsbManager.ACTION_USB_DEVICE_DETACHED.equals(action)) {
                UsbDevice device = (UsbDevice) intent.getParcelableExtra(UsbManager.EXTRA_DEVICE);

                Log.d(TAG, "USB device detached");

                // determine if connected device is a mass storage devuce
                if (device != null) {
                    if (MainActivity.this.device != null) {
                        MainActivity.this.device.close();
                    }
                    // check if there are other devices or set action bar title
                    // to no device if not
                    discoverDevice();
                }
            }

        }
    };

    /**
    * Dialog to create new directories.
    * 
    * @author mjahnen
    * 
    */
    public static class NewDirDialog extends DialogFragment {

        @Override
        public Dialog onCreateDialog(Bundle savedInstanceState) {
            final MainActivity activity = (MainActivity) getActivity();
            AlertDialog.Builder builder = new AlertDialog.Builder(activity);
            builder.setTitle("New Directory");
            builder.setMessage("Please enter a name for the new directory");
            final EditText input = new EditText(activity);
            builder.setView(input);

            builder.setPositiveButton("Ok", new DialogInterface.OnClickListener() {
                @Override
                public void onClick(DialogInterface dialog, int whichButton) {

                    UsbFile dir = activity.adapter.getCurrentDir();
                    try {
                        dir.createDirectory(input.getText().toString());
                        activity.adapter.refresh();
                    } catch (Exception e) {
                        Log.e(TAG, "error creating dir!", e);
                    }

                }

            });

            builder.setNegativeButton("Cancel", new DialogInterface.OnClickListener() {
                @Override
                public void onClick(DialogInterface dialog, int whichButton) {
                    dialog.dismiss();
                }
            });

            builder.setCancelable(false);
            return builder.create();
        }

    }

    /**
     * Dialog to create new files.
     * 
     * @author mjahnen
     * 
     */
    public static class NewFileDialog extends DialogFragment {

        @Override
        public Dialog onCreateDialog(Bundle savedInstanceState) {
            final MainActivity activity = (MainActivity) getActivity();
            AlertDialog.Builder builder = new AlertDialog.Builder(activity);
            builder.setTitle("New File");
            builder.setMessage("Please enter a name for the new file and some input");
            final EditText input = new EditText(activity);
            final EditText content = new EditText(activity);
            LinearLayout layout = new LinearLayout(activity);
            layout.setOrientation(LinearLayout.VERTICAL);
            TextView textView = new TextView(activity);
            textView.setText(R.string.name);
            layout.addView(textView);
            layout.addView(input);
            textView = new TextView(activity);
            textView.setText(R.string.content);
            layout.addView(textView);
            layout.addView(content);

            builder.setView(layout);

            builder.setPositiveButton("Ok", new DialogInterface.OnClickListener() {
                @Override
                public void onClick(DialogInterface dialog, int whichButton) {

                    UsbFile dir = activity.adapter.getCurrentDir();
                    try {
                        UsbFile file = dir.createFile(input.getText().toString());
                        file.write(0, ByteBuffer.wrap(content.getText().toString().getBytes()));
                        file.close();
                        activity.adapter.refresh();
                    } catch (Exception e) {
                        Log.e(TAG, "error creating file!", e);
                    }

                }

            });

            builder.setNegativeButton("Cancel", new DialogInterface.OnClickListener() {
                @Override
                public void onClick(DialogInterface dialog, int whichButton) {
                    dialog.dismiss();
                }
            });

            builder.setCancelable(false);
            return builder.create();
        }

    }

    /**
     * Class to hold the files for a copy task. Holds the source and the
     * destination file.
     * 
     * @author mjahnen
     * 
     */
    private static class CopyTaskParam {
        /* package */UsbFile from;
        /* package */File to;
    }

    /**
     * Asynchronous task to copy a file from the mass storage device connected
     * via USB to the internal storage.
     * 
     * @author mjahnen
     * 
     */
    private class CopyTask extends AsyncTask<CopyTaskParam, Integer, Void> {

        private ProgressDialog dialog;
        private CopyTaskParam param;

        public CopyTask() {
            dialog = new ProgressDialog(MainActivity.this);
            dialog.setTitle("Copying file");
            dialog.setMessage("Copying a file to the internal storage, this can take some time!");
            dialog.setIndeterminate(false);
            dialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
        }

        @Override
        protected void onPreExecute() {
            dialog.show();
        }

        @Override
        protected Void doInBackground(CopyTaskParam... params) {
            long time = System.currentTimeMillis();
            param = params[0];
            try {
                OutputStream out = new BufferedOutputStream(new FileOutputStream(param.to));
                InputStream inputStream = UsbFileStreamFactory.createBufferedInputStream(param.from, currentFs);
                byte[] bytes = new byte[4096];
                int count;
                int total = 0;

                while ((count = inputStream.read(bytes)) != -1) {
                    out.write(bytes, 0, count);
                    total += count;
                    publishProgress((int) total);
                }

                out.close();
                inputStream.close();
            } catch (IOException e) {
                Log.e(TAG, "error copying!", e);
            }
            Log.d(TAG, "copy time: " + (System.currentTimeMillis() - time));
            return null;
        }

        @Override
        protected void onPostExecute(Void result) {
            dialog.dismiss();

            Intent myIntent = new Intent(android.content.Intent.ACTION_VIEW);
            File file = new File(param.to.getAbsolutePath());
            String extension = android.webkit.MimeTypeMap.getFileExtensionFromUrl(Uri.fromFile(file).toString());
            String mimetype = android.webkit.MimeTypeMap.getSingleton().getMimeTypeFromExtension(extension);

            Uri uri;
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
                myIntent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
                uri = FileProvider.getUriForFile(MainActivity.this,
                        MainActivity.this.getApplicationContext().getPackageName() + ".provider", file);
            } else {
                uri = Uri.fromFile(file);
            }
            myIntent.setDataAndType(uri, mimetype);
            try {
                startActivity(myIntent);
            } catch (ActivityNotFoundException e) {
                Toast.makeText(MainActivity.this, "Could no find an app for that file!", Toast.LENGTH_LONG).show();
            }
        }

        @Override
        protected void onProgressUpdate(Integer... values) {
            dialog.setMax((int) param.from.getLength());
            dialog.setProgress(values[0]);
        }

    }

    /**
     * Class to hold the files for a copy task. Holds the source and the
     * destination file.
     *
     * @author mjahnen
     *
     */
    private static class CopyToUsbTaskParam {
        /* package */Uri from;
    }

    /**
     * Asynchronous task to copy a file from the mass storage device connected
     * via USB to the internal storage.
     *
     * @author mjahnen
     *
     */
    private class CopyToUsbTask extends AsyncTask<CopyToUsbTaskParam, Integer, Void> {

        private ProgressDialog dialog;
        private CopyToUsbTaskParam param;

        private String name;
        private int size = -1;

        public CopyToUsbTask() {
            dialog = new ProgressDialog(MainActivity.this);
            dialog.setTitle("Copying file");
            dialog.setMessage("Copying a file to the USB drive, this can take some time!");
            dialog.setIndeterminate(true);
            dialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
        }

        private void queryUriMetaData(Uri uri) {
            Cursor cursor = getContentResolver().query(uri, null, null, null, null, null);

            // TODO: query created and modified times to write it USB 
            if (cursor != null && cursor.moveToFirst()) {
                name = cursor.getString(cursor.getColumnIndex(OpenableColumns.DISPLAY_NAME));
                Log.i(TAG, "Display Name: " + name);
                int sizeIndex = cursor.getColumnIndex(OpenableColumns.SIZE);
                if (!cursor.isNull(sizeIndex)) {
                    size = cursor.getInt(sizeIndex);
                }
                Log.i(TAG, "Size: " + size);

                cursor.close();
            }
        }

        @Override
        protected void onPreExecute() {
            dialog.show();
        }

        @Override
        protected Void doInBackground(CopyToUsbTaskParam... params) {
            long time = System.currentTimeMillis();
            param = params[0];

            queryUriMetaData(param.from);

            if (name == null) {
                String[] segments = param.from.getPath().split("/");
                name = segments[segments.length - 1];
            }

            try {
                UsbFile file = adapter.getCurrentDir().createFile(name);

                if (size > 0) {
                    file.setLength(size);
                }

                InputStream inputStream = getContentResolver().openInputStream(param.from);
                OutputStream outputStream = UsbFileStreamFactory.createBufferedOutputStream(file, currentFs);

                byte[] bytes = new byte[1337];
                int count;
                int total = 0;

                while ((count = inputStream.read(bytes)) != -1) {
                    outputStream.write(bytes, 0, count);
                    if (size > 0) {
                        total += count;
                        publishProgress((int) total);
                    }
                }

                outputStream.close();
                inputStream.close();
            } catch (IOException e) {
                Log.e(TAG, "error copying!", e);
            }
            Log.d(TAG, "copy time: " + (System.currentTimeMillis() - time));
            return null;
        }

        @Override
        protected void onPostExecute(Void result) {
            dialog.dismiss();
            try {
                adapter.refresh();
            } catch (IOException e) {
                Log.e(TAG, "Error refreshing adapter", e);
            }
        }

        @Override
        protected void onProgressUpdate(Integer... values) {
            dialog.setIndeterminate(false);
            dialog.setMax(size);
            dialog.setProgress(values[0]);
        }

    }

    ServiceConnection serviceConnection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            Log.d(TAG, "on service connected " + name);
            UsbFileHttpServerService.ServiceBinder binder = (UsbFileHttpServerService.ServiceBinder) service;
            serverService = binder.getService();
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {
            Log.d(TAG, "on service disconnected " + name);
            serverService = null;
        }
    };

    private ListView listView;
    private UsbMassStorageDevice device;
    /* package */UsbFileListAdapter adapter;
    private Deque<UsbFile> dirs = new ArrayDeque<UsbFile>();
    private FileSystem currentFs;

    private Intent serviceIntent = null;
    private UsbFileHttpServerService serverService;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        serviceIntent = new Intent(this, UsbFileHttpServerService.class);

        setContentView(R.layout.activity_main);

        listView = (ListView) findViewById(R.id.listview);

        listView.setOnItemClickListener(this);
        registerForContextMenu(listView);

        IntentFilter filter = new IntentFilter(ACTION_USB_PERMISSION);
        filter.addAction(UsbManager.ACTION_USB_DEVICE_ATTACHED);
        filter.addAction(UsbManager.ACTION_USB_DEVICE_DETACHED);
        registerReceiver(usbReceiver, filter);
        discoverDevice();
    }

    @Override
    protected void onStart() {
        super.onStart();

        startService(serviceIntent);
        bindService(serviceIntent, serviceConnection, Context.BIND_AUTO_CREATE);
    }

    @Override
    protected void onStop() {
        super.onStop();

        unbindService(serviceConnection);
    }

    /**
    * Searches for connected mass storage devices, and initializes them if it
    * could find some.
    */
    private void discoverDevice() {
        UsbManager usbManager = (UsbManager) getSystemService(Context.USB_SERVICE);
        UsbMassStorageDevice[] devices = UsbMassStorageDevice.getMassStorageDevices(this);

        if (devices.length == 0) {
            Log.w(TAG, "no device found!");
            android.support.v7.app.ActionBar actionBar = getSupportActionBar();
            actionBar.setTitle("No device");
            listView.setAdapter(null);
            return;
        }

        // we only use the first device
        device = devices[0];

        UsbDevice usbDevice = (UsbDevice) getIntent().getParcelableExtra(UsbManager.EXTRA_DEVICE);

        if (usbDevice != null && usbManager.hasPermission(usbDevice)) {
            Log.d(TAG, "received usb device via intent");
            // requesting permission is not needed in this case
            setupDevice();
        } else {
            // first request permission from user to communicate with the
            // underlying
            // UsbDevice
            PendingIntent permissionIntent = PendingIntent.getBroadcast(this, 0, new Intent(ACTION_USB_PERMISSION),
                    0);
            usbManager.requestPermission(device.getUsbDevice(), permissionIntent);
        }
    }

    /**
     * Sets the device up and shows the contents of the root directory.
     */
    private void setupDevice() {
        try {
            device.init();

            // we always use the first partition of the device
            currentFs = device.getPartitions().get(0).getFileSystem();
            Log.d(TAG, "Capacity: " + currentFs.getCapacity());
            Log.d(TAG, "Occupied Space: " + currentFs.getOccupiedSpace());
            Log.d(TAG, "Free Space: " + currentFs.getFreeSpace());
            Log.d(TAG, "Chunk size: " + currentFs.getChunkSize());
            UsbFile root = currentFs.getRootDirectory();

            ActionBar actionBar = getSupportActionBar();
            actionBar.setTitle(currentFs.getVolumeLabel());

            listView.setAdapter(adapter = new UsbFileListAdapter(this, root));
        } catch (IOException e) {
            Log.e(TAG, "error setting up device", e);
        }

    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        // Inflate the menu; this adds items to the action bar if it is present.
        getMenuInflater().inflate(R.menu.main, menu);
        return true;
    }

    @Override
    public boolean onPrepareOptionsMenu(Menu menu) {
        MoveClipboard cl = MoveClipboard.getInstance();
        menu.findItem(R.id.paste).setEnabled(cl.getFile() != null);
        menu.findItem(R.id.stop_http_server).setEnabled(serverService != null && serverService.isServerRunning());
        return true;
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        // Handle item selection
        switch (item.getItemId()) {
        case R.id.create_file:
            new NewFileDialog().show(getFragmentManager(), "NEW_FILE");
            return true;
        case R.id.create_dir:
            new NewDirDialog().show(getFragmentManager(), "NEW_DIR");
            return true;
        case R.id.create_big_file:
            createBigFile();
            return true;
        case R.id.paste:
            move();
            return true;
        case R.id.stop_http_server:
            if (serverService != null) {
                serverService.stopServer();
            }
            return true;
        case R.id.run_tests:
            startActivity(new Intent(this, LibAumsTest.class));
            return true;
        case R.id.open_storage_provider:
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
                if (device != null) {
                    Log.d(TAG, "Closing device first");
                    device.close();
                }
                Intent intent = new Intent();
                intent.setAction(Intent.ACTION_OPEN_DOCUMENT);
                intent.addCategory(Intent.CATEGORY_OPENABLE);
                intent.setType("*/*");
                String[] extraMimeTypes = { "image/*", "video/*" };
                intent.putExtra(Intent.EXTRA_MIME_TYPES, extraMimeTypes);
                intent.putExtra(Intent.EXTRA_ALLOW_MULTIPLE, true);
                startActivityForResult(intent, OPEN_STORAGE_PROVIDER_RESULT);
            }
            return true;
        case R.id.copy_from_storage_provider:

            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
                Intent intent = new Intent();
                intent.setAction(Intent.ACTION_OPEN_DOCUMENT);
                intent.addCategory(Intent.CATEGORY_OPENABLE);
                intent.setType("*/*");

                startActivityForResult(intent, COPY_STORAGE_PROVIDER_RESULT);
            }
            return true;
        default:
            return super.onOptionsItemSelected(item);
        }

    }

    @Override
    public void onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo) {
        super.onCreateContextMenu(menu, v, menuInfo);
        MenuInflater inflater = getMenuInflater();
        inflater.inflate(R.menu.context, menu);
    }

    @Override
    public boolean onContextItemSelected(MenuItem item) {
        AdapterContextMenuInfo info = (AdapterContextMenuInfo) item.getMenuInfo();
        final UsbFile entry = adapter.getItem((int) info.id);
        switch (item.getItemId()) {
        case R.id.delete_item:
            try {
                entry.delete();
                adapter.refresh();
            } catch (IOException e) {
                Log.e(TAG, "error deleting!", e);
            }
            return true;
        case R.id.rename_item:
            AlertDialog.Builder builder = new AlertDialog.Builder(this);
            builder.setTitle("Rename");
            builder.setMessage("Please enter a name for renaming");
            final EditText input = new EditText(this);
            input.setText(entry.getName());
            builder.setView(input);

            builder.setPositiveButton("Ok", new DialogInterface.OnClickListener() {
                @Override
                public void onClick(DialogInterface dialog, int whichButton) {
                    try {
                        entry.setName(input.getText().toString());
                        adapter.refresh();
                    } catch (IOException e) {
                        Log.e(TAG, "error renaming!", e);
                    }
                }

            });

            builder.setNegativeButton("Cancel", new DialogInterface.OnClickListener() {
                @Override
                public void onClick(DialogInterface dialog, int whichButton) {
                    dialog.dismiss();
                }
            });
            builder.setCancelable(false);
            builder.create().show();
            return true;
        case R.id.move_item:
            MoveClipboard cl = MoveClipboard.getInstance();
            cl.setFile(entry);
            return true;
        case R.id.start_http_server:
            startHttpServer(entry);
            return true;
        default:
            return super.onContextItemSelected(item);
        }
    }

    @Override
    public void onItemClick(AdapterView<?> parent, View view, int position, long rowId) {
        UsbFile entry = adapter.getItem(position);
        try {
            if (entry.isDirectory()) {
                dirs.push(adapter.getCurrentDir());
                listView.setAdapter(adapter = new UsbFileListAdapter(this, entry));
            } else {

                if (ContextCompat.checkSelfPermission(this,
                        Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {

                    if (ActivityCompat.shouldShowRequestPermissionRationale(this,
                            Manifest.permission.WRITE_EXTERNAL_STORAGE)) {
                        Toast.makeText(this, R.string.request_write_storage_perm, Toast.LENGTH_LONG).show();
                    } else {
                        ActivityCompat.requestPermissions(this,
                                new String[] { Manifest.permission.WRITE_EXTERNAL_STORAGE },
                                REQUEST_EXT_STORAGE_WRITE_PERM);
                    }

                    return;
                }

                CopyTaskParam param = new CopyTaskParam();
                param.from = entry;
                File f = new File(
                        Environment.getExternalStorageDirectory().getAbsolutePath() + "/usbfileman/cache");
                f.mkdirs();
                int index = entry.getName().lastIndexOf(".") > 0 ? entry.getName().lastIndexOf(".")
                        : entry.getName().length();
                String prefix = entry.getName().substring(0, index);
                String ext = entry.getName().substring(index);
                // prefix must be at least 3 characters
                if (prefix.length() < 3) {
                    prefix += "pad";
                }
                param.to = File.createTempFile(prefix, ext, f);
                new CopyTask().execute(param);
            }
        } catch (IOException e) {
            Log.e(TAG, "error staring to copy!", e);
        }
    }

    private void startHttpServer(final UsbFile file) {

        Log.d(TAG, "starting HTTP server");

        if (serverService == null) {
            Toast.makeText(MainActivity.this, "serverService == null!", Toast.LENGTH_LONG).show();
            return;
        }

        if (serverService.isServerRunning()) {
            Log.d(TAG, "Stopping existing server service");
            serverService.stopServer();
        }

        // now start the server
        try {
            serverService.startServer(file, new AsyncHttpServer(8000));
            Toast.makeText(MainActivity.this, "HTTP server up and running", Toast.LENGTH_LONG).show();
        } catch (IOException e) {
            Log.e(TAG, "Error starting HTTP server", e);
            Toast.makeText(MainActivity.this, "Could not start HTTP server", Toast.LENGTH_LONG).show();
        }

        if (file.isDirectory()) {
            // only open activity when serving a file
            return;
        }

        Intent myIntent = new Intent(android.content.Intent.ACTION_VIEW);
        myIntent.setData(Uri.parse(serverService.getServer().getBaseUrl() + file.getName()));
        try {
            startActivity(myIntent);
        } catch (ActivityNotFoundException e) {
            Toast.makeText(MainActivity.this, "Could no find an app for that file!", Toast.LENGTH_LONG).show();
        }
    }

    @Override
    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions,
            @NonNull int[] grantResults) {
        switch (requestCode) {
        case REQUEST_EXT_STORAGE_WRITE_PERM: {
            // If request is cancelled, the result arrays are empty.
            if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {

                Toast.makeText(this, R.string.permission_granted, Toast.LENGTH_LONG).show();
            } else {
                Toast.makeText(this, R.string.permission_denied, Toast.LENGTH_LONG).show();
            }
        }

        }
    }

    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        super.onActivityResult(requestCode, resultCode, data);

        if (resultCode != Activity.RESULT_OK) {
            Log.w(TAG, "Activity result is not ok");
            return;
        }

        if (requestCode == OPEN_STORAGE_PROVIDER_RESULT) {
            Uri uri;
            if (data != null) {
                uri = data.getData();
                Log.i(TAG, "Uri: " + uri.toString());
                Intent i = new Intent(Intent.ACTION_VIEW);
                i.setData(uri);
                startActivity(i);
            }
        } else if (requestCode == COPY_STORAGE_PROVIDER_RESULT) {
            Uri uri;
            if (data != null) {
                uri = data.getData();
                Log.i(TAG, "Uri: " + uri.toString());

                CopyToUsbTaskParam params = new CopyToUsbTaskParam();
                params.from = uri;

                new CopyToUsbTask().execute(params);
            }
        }
    }

    /**
     * This methods creates a very big file for testing purposes. It writes only
     * a small chunk of bytes in every loop iteration, so the offset where the
     * write starts will not always be a multiple of the cluster or block size
     * of the file system or block device. As a plus the file has to be grown
     * after every loop iteration which tests for example on FAT32 the dynamic
     * growth of a cluster chain.
     */
    private void createBigFile() {
        UsbFile dir = adapter.getCurrentDir();
        UsbFile file;
        try {
            file = dir.createFile("big_file_test.txt");
            OutputStream outputStream = UsbFileStreamFactory.createBufferedOutputStream(file, currentFs);
            outputStream.write("START\n".getBytes());
            int i;

            for (i = 6; i < 9000; i += 5) {
                outputStream.write("TEST\n".getBytes());
            }

            outputStream.write("END\n".getBytes());

            outputStream.close();

            adapter.refresh();
        } catch (IOException e) {
            Log.e(TAG, "error creating big file!", e);
        }
    }

    /**
     * This method moves the file located in the {@link MoveClipboard} into the
     * current shown directory.
     */
    private void move() {
        MoveClipboard cl = MoveClipboard.getInstance();
        UsbFile file = cl.getFile();
        try {
            file.moveTo(adapter.getCurrentDir());
            adapter.refresh();
        } catch (IOException e) {
            Log.e(TAG, "error moving!", e);
        }
        cl.setFile(null);
    }

    @Override
    public void onBackPressed() {
        try {
            UsbFile dir = dirs.pop();
            listView.setAdapter(adapter = new UsbFileListAdapter(this, dir));
        } catch (NoSuchElementException e) {
            super.onBackPressed();
        } catch (IOException e) {
            Log.e(TAG, "error initializing adapter!", e);
        }
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        unregisterReceiver(usbReceiver);

        if (!serverService.isServerRunning()) {
            Log.d(TAG, "Stopping service");
            stopService(serviceIntent);

            if (device != null) {
                Log.d(TAG, "Closing device");

                device.close();
            }
        }
    }
}