com.ubuntuone.android.files.provider.MetaUtilities.java Source code

Java tutorial

Introduction

Here is the source code for com.ubuntuone.android.files.provider.MetaUtilities.java

Source

/*
 * Ubuntu One Files - access Ubuntu One cloud storage on Android platform.
 * 
 * Copyright 2011-2012 Canonical Ltd.
 *   
 * This file is part of Ubuntu One Files.
 *  
 * This program 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.
 *  
 * This program 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 http://www.gnu.org/licenses 
 */

package com.ubuntuone.android.files.provider;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.net.URI;
import java.util.ArrayList;
import java.util.Date;
import java.util.Locale;
import java.util.Set;
import java.util.TreeSet;

import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;

import android.content.ContentResolver;
import android.content.ContentValues;
import android.database.Cursor;
import android.database.MergeCursor;
import android.net.Uri;
import android.provider.MediaStore.MediaColumns;
import android.text.TextUtils;

import com.ubuntuone.android.files.Preferences;
import com.ubuntuone.android.files.UbuntuOneFiles;
import com.ubuntuone.android.files.provider.MetaContract.Nodes;
import com.ubuntuone.android.files.provider.MetaContract.ResourceState;
import com.ubuntuone.android.files.provider.MetaContract.Volumes;
import com.ubuntuone.android.files.util.FileUtilities;
import com.ubuntuone.android.files.util.Log;
import com.ubuntuone.api.files.model.U1File;
import com.ubuntuone.api.files.model.U1Node;
import com.ubuntuone.api.files.model.U1NodeKind;
import com.ubuntuone.api.files.util.HashUtils;

public final class MetaUtilities {
    private static final String TAG = MetaUtilities.class.getSimpleName();

    private static ContentResolver sResolver;

    static {
        sResolver = UbuntuOneFiles.getInstance().getContentResolver();
    }

    private MetaUtilities() {
    }

    public static void notifyChange(Uri uri) {
        sResolver.notifyChange(uri, null);
    }

    final static String sSelection = Nodes.NODE_RESOURCE_PATH + "=?";

    public static String getStringField(Uri uri, String columnName) {
        String result = null;
        final String[] projection = new String[] { columnName };
        final Cursor c = sResolver.query(uri, projection, null, null, null);
        try {
            if (c.moveToFirst()) {
                result = c.getString(c.getColumnIndex(columnName));
            }
        } finally {
            c.close();
        }
        return result;
    }

    public static String getStringField(String resourcePath, String columnName) {
        String result = null;
        final String[] projection = new String[] { columnName };
        final String[] selectionArgs = new String[] { resourcePath };
        final Cursor c = sResolver.query(Nodes.CONTENT_URI, projection, sSelection, selectionArgs, null);
        try {
            if (c.moveToFirst()) {
                result = c.getString(c.getColumnIndex(columnName));
            }
        } finally {
            c.close();
        }
        return result;
    }

    public static Cursor getNodeCursorByResourcePath(String resourcePath, String[] projection) {
        final String[] selectionArgs = new String[] { resourcePath };
        final Cursor cursor = sResolver.query(Nodes.CONTENT_URI, projection, sSelection, selectionArgs, null);
        return cursor;
    }

    public static U1Node getNodeByKey(String key) {
        String[] selectionArgs = new String[] { key };
        String selection = Nodes.NODE_KEY + "=?";
        String[] projection = Nodes.getDefaultProjection();
        final Cursor c = sResolver.query(Nodes.CONTENT_URI, projection, selection, selectionArgs, null);
        if (c != null) {
            try {
                if (c.moveToFirst()) {
                    String resourcePath;
                    U1NodeKind kind;
                    Boolean isLive = true;
                    String path;
                    String parentPath;
                    String volumePath;
                    Date whenCreated;
                    Date whenChanged;
                    Long generation;
                    Long generationCreated;
                    String contentPath;

                    resourcePath = c.getString(c.getColumnIndex(Nodes.NODE_RESOURCE_PATH));
                    kind = U1NodeKind
                            .valueOf(c.getString(c.getColumnIndex(Nodes.NODE_KIND)).toUpperCase(Locale.US));
                    isLive = c.getInt(c.getColumnIndex(Nodes.NODE_IS_LIVE)) != 0;
                    path = c.getString(c.getColumnIndex(Nodes.NODE_PATH));
                    parentPath = c.getString(c.getColumnIndex(Nodes.NODE_PARENT_PATH));
                    volumePath = c.getString(c.getColumnIndex(Nodes.NODE_VOLUME_PATH));
                    whenCreated = new Date(c.getLong(c.getColumnIndex(Nodes.NODE_WHEN_CREATED)));
                    whenChanged = new Date(c.getLong(c.getColumnIndex(Nodes.NODE_WHEN_CHANGED)));
                    generation = c.getLong(c.getColumnIndex(Nodes.NODE_GENERATION));
                    generationCreated = c.getLong(c.getColumnIndex(Nodes.NODE_GENERATION_CREATED));
                    contentPath = c.getString(c.getColumnIndex(Nodes.NODE_CONTENT_PATH));

                    return new U1Node(resourcePath, kind, isLive, path, parentPath, volumePath, key, whenCreated,
                            whenChanged, generation, generationCreated, contentPath);
                } else {
                    return null;
                }
            } finally {
                c.close();
            }
        }
        return null;
    }

    public static U1Node getNodeByResourcePath(String resourcePath) {
        String[] selectionArgs = new String[] { resourcePath };
        String selection = Nodes.NODE_RESOURCE_PATH + "=?";
        String[] projection = Nodes.getDefaultProjection();
        final Cursor c = sResolver.query(Nodes.CONTENT_URI, projection, selection, selectionArgs, null);
        if (c != null) {
            try {
                if (c.moveToFirst()) {
                    String key;
                    U1NodeKind kind;
                    Boolean isLive = true;
                    String path;
                    String parentPath;
                    String volumePath;
                    Date whenCreated;
                    Date whenChanged;
                    Long generation;
                    Long generationCreated;
                    String contentPath;

                    key = c.getString(c.getColumnIndex(Nodes.NODE_KEY));
                    kind = U1NodeKind
                            .valueOf(c.getString(c.getColumnIndex(Nodes.NODE_KIND)).toUpperCase(Locale.US));
                    isLive = c.getInt(c.getColumnIndex(Nodes.NODE_IS_LIVE)) != 0;
                    path = c.getString(c.getColumnIndex(Nodes.NODE_PATH));
                    parentPath = c.getString(c.getColumnIndex(Nodes.NODE_PARENT_PATH));
                    volumePath = c.getString(c.getColumnIndex(Nodes.NODE_VOLUME_PATH));
                    whenCreated = new Date(c.getLong(c.getColumnIndex(Nodes.NODE_WHEN_CREATED)));
                    whenChanged = new Date(c.getLong(c.getColumnIndex(Nodes.NODE_WHEN_CHANGED)));
                    generation = c.getLong(c.getColumnIndex(Nodes.NODE_GENERATION));
                    generationCreated = c.getLong(c.getColumnIndex(Nodes.NODE_GENERATION_CREATED));
                    contentPath = c.getString(c.getColumnIndex(Nodes.NODE_CONTENT_PATH));

                    return new U1Node(resourcePath, kind, isLive, path, parentPath, volumePath, key, whenCreated,
                            whenChanged, generation, generationCreated, contentPath);
                } else {
                    return null;
                }
            } finally {
                c.close();
            }
        }
        return null;
    }

    public static Cursor getChildDirectoriesCursorByResourcePath(String resourcePath, String[] projection) {
        final String selection = Nodes.NODE_PARENT_PATH + "=?" + " AND " + Nodes.NODE_KIND + "=?";
        final String[] selectionArgs = new String[] { resourcePath, U1NodeKind.DIRECTORY.toString() };
        final Cursor cursor = sResolver.query(Nodes.CONTENT_URI, projection, selection, selectionArgs, null);
        return cursor;
    }

    public static String getPublicUrl(final String resourcePath) {
        final String[] projection = new String[] { Nodes.NODE_PUBLIC_URL };
        final String selection = Nodes.NODE_RESOURCE_PATH + "=?";
        final String[] selectionArgs = new String[] { resourcePath };
        Cursor c = sResolver.query(Nodes.CONTENT_URI, projection, selection, selectionArgs, null);
        String url = null;
        try {
            if (c.moveToFirst()) {
                url = c.getString(c.getColumnIndex(Nodes.NODE_PUBLIC_URL));
            }
        } finally {
            c.close();
        }
        return TextUtils.isEmpty(url) ? null : url;
    }

    public static long getSize(final String resourcePath) {
        final String[] projection = new String[] { Nodes.NODE_SIZE };
        final String selection = Nodes.NODE_RESOURCE_PATH + "=?";
        final String[] selectionArgs = new String[] { resourcePath };
        Cursor c = sResolver.query(Nodes.CONTENT_URI, projection, selection, selectionArgs, null);
        long size = 0L;
        try {
            if (c.moveToFirst()) {
                size = c.getLong(c.getColumnIndex(Nodes.NODE_SIZE));
            }
        } finally {
            c.close();
        }
        return size;
    }

    public static int getCount(String resourcePath) {
        final String[] projection = new String[] { Nodes._ID };
        final String[] selectionArgs = new String[] { resourcePath };
        final Cursor c = sResolver.query(Nodes.CONTENT_URI, projection, sSelection, selectionArgs, null);
        int count = 0;
        try {
            count = c.getCount();
        } finally {
            c.close();
        }
        return count;
    }

    public static Set<Integer> getRootNodeIds() {
        Set<Integer> ids = new TreeSet<Integer>();
        final String[] projection = new String[] { Nodes._ID };
        final String selection = Nodes.NODE_PARENT_PATH + " IS NULL";
        final Cursor c = sResolver.query(Nodes.CONTENT_URI, projection, selection, null, null);
        try {
            if (c.moveToFirst()) {
                int id;
                do {
                    id = c.getInt(c.getColumnIndex(Nodes._ID));
                    ids.add(id);
                } while (c.moveToNext());
            }
        } finally {
            c.close();
        }
        return ids;
    }

    public static Set<String> getUserNodePaths() {
        Set<String> userNodePaths = new TreeSet<String>();
        final String[] projection = new String[] { Nodes._ID, Nodes.NODE_RESOURCE_PATH };
        final String selection = Nodes.NODE_PARENT_PATH + " IS NULL";
        final Cursor c = sResolver.query(Nodes.CONTENT_URI, projection, selection, null, null);
        try {
            if (c.moveToFirst()) {
                String resourcePath;
                do {
                    resourcePath = c.getString(c.getColumnIndex(Nodes.NODE_RESOURCE_PATH));
                    userNodePaths.add(resourcePath);
                } while (c.moveToNext());
            }
        } finally {
            c.close();
        }
        return userNodePaths;
    }

    public static Set<Integer> getChildrenIds(String resourcePath) {
        Set<Integer> ids = new TreeSet<Integer>();
        final String[] projection = new String[] { Nodes._ID, Nodes.NODE_RESOURCE_STATE };
        final String selection = Nodes.NODE_PARENT_PATH + "=?";
        //+ " AND " + Nodes.NODE_RESOURCE_STATE + " IS NULL"; // FIXME
        final String[] selectionArgs = new String[] { resourcePath };
        final Cursor c = sResolver.query(Nodes.CONTENT_URI, projection, selection, selectionArgs, null);
        try {
            if (c.moveToFirst()) {
                int id;
                do {
                    id = c.getInt(c.getColumnIndex(Nodes._ID));
                    // We check the state, above SQL is failing to filter out
                    // nodes which are in non-null state. No idea why.
                    String s = c.getString(c.getColumnIndex(Nodes.NODE_RESOURCE_STATE));
                    if (s == null) {
                        ids.add(id);
                    } else {
                        Log.d("MetaUtilities", "child state != null, ignoring");
                    }
                } while (c.moveToNext());
            }
        } finally {
            c.close();
        }
        return ids;
    }

    public static String getHiddenSelection() {
        return Preferences.getShowHidden() ? null
                : Nodes.NODE_NAME + " NOT LIKE '.%' AND " + Nodes.NODE_NAME + " NOT LIKE '~/.%' ";
    }

    public static Cursor getVisibleVolumesCursor() {
        final Cursor c = sResolver.query(Volumes.CONTENT_URI, Volumes.getDefaultProjection(), null, null, null);
        c.setNotificationUri(sResolver, Volumes.CONTENT_URI);
        return c;
    }

    public static Cursor getVisibleTopNodesCursor() {
        // XXX android.database.CursorIndexOutOfBoundsException: Index -1 requested, with a size of 1
        final String showHidden = Preferences.getShowHidden() ? "" : " AND " + getHiddenSelection();
        final String[] projection = Nodes.getDefaultProjection();

        String selection = Nodes.NODE_RESOURCE_PATH + "=?" + showHidden;
        String[] selectionArgs = new String[] { Preferences.U1_RESOURCE };
        final Cursor ubuntuOne = sResolver.query(Nodes.CONTENT_URI, projection, selection, selectionArgs, null);

        selection = Nodes.NODE_RESOURCE_PATH + "=?" + showHidden;
        selectionArgs = new String[] { Preferences.U1_PURCHASED_MUSIC };
        final Cursor purchasedMusic = sResolver.query(Nodes.CONTENT_URI, projection, selection, selectionArgs,
                null);

        selection = Nodes.NODE_PARENT_PATH + " IS NULL " + "AND " + Nodes.NODE_RESOURCE_PATH + "!=? " + "AND "
                + Nodes.NODE_RESOURCE_PATH + "!=? " + showHidden;
        selectionArgs = new String[] { Preferences.U1_RESOURCE, Preferences.U1_PURCHASED_MUSIC };
        final Cursor cloudFolders = sResolver.query(Nodes.CONTENT_URI, projection, selection, selectionArgs, null);

        final MergeCursor cursor = new MergeCursor(new Cursor[] { ubuntuOne, purchasedMusic, cloudFolders });
        cursor.setNotificationUri(sResolver, Nodes.CONTENT_URI);

        return cursor;
    }

    public static Cursor getVisibleNodesCursorByParent(String parentPath) {
        final String showHidden = Preferences.getShowHidden() ? "" : " AND " + getHiddenSelection();

        final String[] projection = Nodes.getDefaultProjection();
        final String selection = Nodes.NODE_PARENT_PATH + "=? " + showHidden;
        final String[] selectionArgs = new String[] { parentPath };

        final Cursor cursor = sResolver.query(Nodes.CONTENT_URI, projection, selection, selectionArgs, null);
        cursor.setNotificationUri(sResolver, Nodes.CONTENT_URI);

        return cursor;
    }

    /**
     * Calculates directory content size, recursively if necessary.
     * 
     * @param resourcePath
     *            the directory resource path to calculate size of
     * @param recursive
     *            the flag indicating recursive calculation
     * @return the resorucePath defined directory size
     */
    public static long getDirectorySize(final String resourcePath, final boolean recursive) {
        final String[] projection = new String[] { Nodes.NODE_RESOURCE_PATH, Nodes.NODE_KIND, Nodes.NODE_SIZE };
        final String selection = Nodes.NODE_PARENT_PATH + "=?";
        final String[] selectionArgs = new String[] { resourcePath };
        final Cursor c = sResolver.query(Nodes.CONTENT_URI, projection, selection, selectionArgs, null);
        U1NodeKind kind;
        long size = 0L;
        try {
            if (c.moveToFirst()) {
                do {
                    kind = U1NodeKind
                            .valueOf(c.getString(c.getColumnIndex(Nodes.NODE_KIND)).toUpperCase(Locale.US));
                    if (U1NodeKind.FILE == kind) {
                        size += c.getLong(c.getColumnIndex(Nodes.NODE_SIZE));
                    } else if (U1NodeKind.DIRECTORY == kind && recursive) {
                        final String subDirResourcePath = c.getString(c.getColumnIndex(Nodes.NODE_RESOURCE_PATH));
                        size += getDirectorySize(subDirResourcePath, true);
                    }
                } while (c.moveToNext());
            }
        } finally {
            c.close();
        }
        return size;
    }

    public static Cursor getFailedTransfers() {
        final String[] projection = new String[] { Nodes.NODE_RESOURCE_PATH, Nodes.NODE_RESOURCE_STATE,
                Nodes.NODE_PARENT_PATH, Nodes.NODE_DATA };
        final String selection = Nodes.NODE_RESOURCE_STATE + " LIKE '%" + ResourceState.FAILED + "'";
        final String sortOrder = Nodes.NODE_RESOURCE_STATE + " ASC";
        return sResolver.query(Nodes.CONTENT_URI, projection, selection, null, sortOrder);
    }

    public static int resetFailedTransfers() {
        int failed = 0;
        failed += resetFailedTransfers(HttpPost.METHOD_NAME);
        failed += resetFailedTransfers(HttpGet.METHOD_NAME);
        return failed;
    }

    public static int resetFailedTransfers(String method) {
        String ongoing;
        String failed;
        if (HttpPost.METHOD_NAME.equals(method)) {
            ongoing = ResourceState.STATE_POSTING;
            failed = ResourceState.STATE_POSTING_FAILED;
        } else if (HttpGet.METHOD_NAME.equals(method)) {
            ongoing = ResourceState.STATE_GETTING;
            failed = ResourceState.STATE_GETTING_FAILED;
        } else {
            Log.e(TAG, "Bad method name: " + method);
            return 0;
        }

        final ContentValues values = new ContentValues(1);
        values.put(Nodes.NODE_RESOURCE_STATE, failed);
        final String where = Nodes.NODE_RESOURCE_STATE + "=?";
        final String[] selectionArgs = new String[] { ongoing };
        return sResolver.update(Nodes.CONTENT_URI, values, where, selectionArgs);
    }

    public static void cancelFailedTransfers() {
        ContentValues values;
        final String where = Nodes.NODE_RESOURCE_STATE + "=?";
        String[] selectionArgs;
        String clearState = null;

        // Reset uploads.
        selectionArgs = new String[] { ResourceState.STATE_POSTING_FAILED };
        sResolver.delete(Nodes.CONTENT_URI, where, selectionArgs);

        // Reset downloads.
        selectionArgs = new String[] { ResourceState.STATE_GETTING_FAILED };
        values = new ContentValues(1);
        values.put(Nodes.NODE_RESOURCE_STATE, clearState);
        sResolver.update(Nodes.CONTENT_URI, values, where, selectionArgs);
    }

    public static void updateLongField(String resourcePath, String column, Long value) {
        final String[] selectionArgs = new String[] { resourcePath };
        final ContentValues values = new ContentValues();
        values.put(column, value);
        sResolver.update(Nodes.CONTENT_URI, values, sSelection, selectionArgs);
    }

    public static void updateStringField(String resourcePath, String column, String value) {
        final String[] selectionArgs = new String[] { resourcePath };
        final ContentValues values = new ContentValues();
        values.put(column, value);
        sResolver.update(Nodes.CONTENT_URI, values, sSelection, selectionArgs);
    }

    public static void setState(final String resourcePath, final String state) {
        final ContentValues values = new ContentValues();
        values.put(Nodes.NODE_RESOURCE_STATE, state);
        final String where = Nodes.NODE_RESOURCE_PATH + "=?";
        final String[] selectionArgs = new String[] { resourcePath };
        sResolver.update(Nodes.CONTENT_URI, values, where, selectionArgs);
    }

    public static void setStateAndData(final String resourcePath, final String state, final String data) {
        final String[] selectionArgs = new String[] { resourcePath };
        final ContentValues values = new ContentValues();
        values.put(Nodes.NODE_RESOURCE_STATE, state);
        values.put(Nodes.NODE_DATA, data);
        sResolver.update(Nodes.CONTENT_URI, values, sSelection, selectionArgs);
    }

    public static void setIsCached(String resourcePath, boolean isCached) {
        ContentValues values = new ContentValues(2);
        values.put(Nodes.NODE_RESOURCE_PATH, resourcePath);
        values.put(Nodes.NODE_IS_CACHED, isCached);
        String where = Nodes.NODE_RESOURCE_PATH + "=?";
        String[] selectionArgs = new String[] { resourcePath };
        sResolver.update(Nodes.CONTENT_URI, values, where, selectionArgs);
        sResolver.notifyChange(Nodes.CONTENT_URI, null);
    }

    public static boolean isCached(String resourcePath) {
        String[] projection = new String[] { Nodes.NODE_IS_CACHED };
        String selection = Nodes.NODE_RESOURCE_PATH + "=?";
        String[] selectionArgs = new String[] { resourcePath };
        Cursor c = sResolver.query(Nodes.CONTENT_URI, projection, selection, selectionArgs, null);
        if (c != null) {
            try {
                if (c.moveToFirst()) {
                    return c.getInt(c.getColumnIndex(Nodes.NODE_IS_CACHED)) != 0;
                }
            } finally {
                c.close();
            }
        }
        return false;
    }

    public static void deleteByResourcePath(String resourcePath) {
        String where = Nodes.NODE_RESOURCE_PATH + "=?";
        String[] selectionArgs = new String[] { resourcePath };
        sResolver.delete(Nodes.CONTENT_URI, where, selectionArgs);
    }

    public static void cleanupTreeByResourcePath(String resourcePath) {
        Log.i(TAG, "cleaning up tree of: " + resourcePath);
        String kindString = MetaUtilities.getStringField(resourcePath, Nodes.NODE_KIND);
        if (kindString == null) {
            return;
        }

        U1NodeKind kind = U1NodeKind.valueOf(kindString.toUpperCase(Locale.US));
        if (kind == U1NodeKind.FILE) {
            String data = FileUtilities.getFilePathFromResourcePath(resourcePath);
            FileUtilities.removeSilently(data);
            MetaUtilities.deleteByResourcePath(resourcePath);
        } else {
            String resourcePathFmt = resourcePath + "/%";
            String[] projection = new String[] { Nodes.NODE_RESOURCE_PATH, Nodes.NODE_DATA };
            String selection = Nodes.NODE_RESOURCE_PATH + " LIKE ?";
            String[] selectionArgs = new String[] { resourcePathFmt };

            Cursor c = sResolver.query(Nodes.CONTENT_URI, projection, selection, selectionArgs, null);
            if (c != null) {
                try {
                    while (c.moveToNext()) {
                        String data = FileUtilities.getFilePathFromResourcePath(resourcePathFmt);
                        FileUtilities.removeSilently(data);
                    }
                } finally {
                    c.close();
                }
            }

            sResolver.delete(Nodes.CONTENT_URI, selection, selectionArgs);

            selection = Nodes.NODE_RESOURCE_PATH + "=?";
            selectionArgs = new String[] { resourcePath };
            sResolver.delete(Nodes.CONTENT_URI, selection, selectionArgs);
        }
    }

    public static Uri buildNodeUri(int id) {
        return Nodes.CONTENT_URI.buildUpon().appendPath(String.valueOf(id)).build();
    }

    public static boolean isValidUri(Uri uri) {
        try {
            final InputStream in = sResolver.openInputStream(uri);
            in.close();
        } catch (FileNotFoundException e) {
            return false;
        } catch (IOException e) {
            return false;
        }
        return true;
    }

    public static boolean isValidUriTarget(String data) {
        if (data == null) {
            Log.d(TAG, "uri is null");
            return false;
        } else if (data.startsWith(ContentResolver.SCHEME_CONTENT)) {
            Log.d(TAG, "checking content uri");
            return isValidUri(Uri.parse(data));
        } else if (data.startsWith(ContentResolver.SCHEME_FILE)) {
            try {
                Log.d(TAG, "checking file uri");
                return new File(URI.create(data)).exists();
            } catch (IllegalArgumentException e) {
                // We have received wrong uri. Failed upload shall be cleaned up.
                return false;
            }
        } else if (new File(data).exists()) {
            return true;
        } else {
            Log.e(TAG, "unknown uri: " + data);
        }
        return false;
    }

    public static boolean isDirectory(long id) {
        String[] projection = new String[] { Nodes.NODE_KIND };
        String selection = Nodes._ID + "=?";
        String[] selectionArgs = new String[] { String.valueOf(id) };
        final Cursor c = sResolver.query(Nodes.CONTENT_URI, projection, selection, selectionArgs, null);
        String type = null;
        try {
            if (c.moveToFirst()) {
                type = c.getString(c.getColumnIndex(Nodes.NODE_KIND));
            }
        } finally {
            c.close();
        }
        return (type != null) ? U1NodeKind.DIRECTORY == U1NodeKind.valueOf(type.toUpperCase(Locale.US)) : false;
    }

    public static boolean isDirectory(Cursor c) {
        final String type = c.getString(c.getColumnIndex(Nodes.NODE_KIND));
        return U1NodeKind.DIRECTORY == U1NodeKind.valueOf(type.toUpperCase(Locale.US));
    }

    /**
     * For a given file {@link Uri} string, we check if its hash has a
     * corresponding entry in the {@link MetaProvider}, telling thus whether the
     * file from under given {@link Uri} string has been already uploaded.
     * 
     * @param uriString
     *            the uri string which content we are checking
     * @return resourcePath if content under uri has been already uploaded,
     *         null otherwise
     */
    public static String isUploaded(String uriString) {
        File file = null;
        String fileHash = null;

        if (uriString.startsWith(ContentResolver.SCHEME_CONTENT)) {
            final String[] projection = new String[] { MediaColumns.DATA };
            final Cursor c = sResolver.query(Uri.parse(uriString), projection, null, null, null);
            try {
                if (c.moveToFirst()) {
                    String data = c.getString(c.getColumnIndex(MediaColumns.DATA));
                    file = new File(data);
                } else {
                    return null;
                }
            } finally {
                c.close();
            }
        } else if (uriString.startsWith(ContentResolver.SCHEME_FILE)) {
            final URI fileURI = URI.create(Uri.encode(uriString, ":/"));
            file = new File(fileURI);
        } else {
            Log.e(TAG, "Tried to check malformed uri string: " + uriString);
            return null;
        }

        try {
            if (file != null && file.exists()) {
                fileHash = HashUtils.getSha1(file);
                Log.d(TAG, String.format("Computed hash: '%s'", fileHash));
            } else {
                throw new FileNotFoundException("isUploaded()");
            }
        } catch (Exception e) {
            Log.e(TAG, "Can't compute file hash!", e);
            return null;
        }

        final String[] projection = new String[] { Nodes.NODE_RESOURCE_PATH };
        final String selection = Nodes.NODE_HASH + "=?";
        final String[] selectionArgs = new String[] { fileHash };
        final Cursor c = sResolver.query(Nodes.CONTENT_URI, projection, selection, selectionArgs, null);
        String resourcePath = null;
        try {
            if (c.moveToFirst()) {
                resourcePath = c.getString(c.getColumnIndex(Nodes.NODE_RESOURCE_PATH));
                Log.d(TAG, "Corresponding file hash found: " + resourcePath);
            } else {
                Log.d(TAG, "Corresponding file hash not found.");
            }
        } finally {
            c.close();
        }
        return resourcePath;
    }

    public static void updateNode(ContentResolver contentResolver, U1Node node, String data) {
        final String resourcePath = node.getResourcePath();
        final boolean isDirectory = node.getKind() == U1NodeKind.DIRECTORY;

        if (!isDirectory && ((U1File) node).getSize() == null) {
            // Ignore files with null size.
            return;
        }

        ContentValues values = Nodes.valuesFromRepr(node, data);
        String selection = Nodes.NODE_KEY + "=?";
        String[] selectionArgs = new String[] { node.getKey() };
        if (node.getIsLive()) {
            int updated = contentResolver.update(Nodes.CONTENT_URI, values, selection, selectionArgs);
            if (updated == 0) {
                boolean isNonEmptyFile = !isDirectory && ((U1File) node).getSize() != null;
                if (isDirectory || isNonEmptyFile) {
                    contentResolver.insert(Nodes.CONTENT_URI, values);
                }
            }
        } else {
            if (node.getKind() == U1NodeKind.FILE) {
                FileUtilities.removeSilently(FileUtilities.getFilePathFromResourcePath(resourcePath));
            }
            MetaUtilities.deleteByResourcePath(resourcePath);
        }
        MetaUtilities.notifyChange(Nodes.CONTENT_URI);
    }

    public static long getVolumeGeneration(String resourcePath) {
        String[] projection = new String[] { Volumes.VOLUME_GENERATION };
        String selection = Volumes.VOLUME_RESOURCE_PATH + "=?";
        String[] selectionArgs = new String[] { resourcePath };
        Cursor c = sResolver.query(Volumes.CONTENT_URI, projection, selection, selectionArgs, null);
        if (c != null) {
            try {
                if (c.moveToFirst()) {
                    return c.getLong(c.getColumnIndex(Volumes.VOLUME_GENERATION));
                }
            } finally {
                c.close();
            }
        }
        return 0;
    }

    public static ArrayList<U1Node> getPhotoNodesFromDirectory(String directoryResourcePath) {
        String[] projection = Nodes.getDefaultProjection();
        String selection = Nodes.NODE_PARENT_PATH + "=? AND " + Nodes.NODE_MIME + "=?";
        String[] selectionArgs = new String[] { directoryResourcePath, "image/jpeg" };

        Cursor c = sResolver.query(Nodes.CONTENT_URI, projection, selection, selectionArgs, null);

        ArrayList<U1Node> photoNodes = null;
        if (c != null) {
            photoNodes = new ArrayList<U1Node>();
            try {
                if (c.moveToFirst()) {
                    do {
                        String resourcePath;
                        String key;
                        U1NodeKind kind;
                        Boolean isLive = true;
                        String path;
                        String parentPath;
                        String volumePath;
                        Date whenCreated;
                        Date whenChanged;
                        Long generation;
                        Long generationCreated;
                        String contentPath;

                        resourcePath = c.getString(c.getColumnIndex(Nodes.NODE_RESOURCE_PATH));
                        key = c.getString(c.getColumnIndex(Nodes.NODE_KEY));
                        kind = U1NodeKind
                                .valueOf(c.getString(c.getColumnIndex(Nodes.NODE_KIND)).toUpperCase(Locale.US));
                        isLive = c.getInt(c.getColumnIndex(Nodes.NODE_IS_LIVE)) != 0;
                        path = c.getString(c.getColumnIndex(Nodes.NODE_PATH));
                        parentPath = c.getString(c.getColumnIndex(Nodes.NODE_PARENT_PATH));
                        volumePath = c.getString(c.getColumnIndex(Nodes.NODE_VOLUME_PATH));
                        whenCreated = new Date(c.getLong(c.getColumnIndex(Nodes.NODE_WHEN_CREATED)));
                        whenChanged = new Date(c.getLong(c.getColumnIndex(Nodes.NODE_WHEN_CHANGED)));
                        generation = c.getLong(c.getColumnIndex(Nodes.NODE_GENERATION));
                        generationCreated = c.getLong(c.getColumnIndex(Nodes.NODE_GENERATION_CREATED));
                        contentPath = c.getString(c.getColumnIndex(Nodes.NODE_CONTENT_PATH));

                        U1Node node = new U1Node(resourcePath, kind, isLive, path, parentPath, volumePath, key,
                                whenCreated, whenChanged, generation, generationCreated, contentPath);
                        photoNodes.add(node);
                    } while (c.moveToNext());
                }
            } finally {
                c.close();
            }
        }
        return photoNodes;
    }
}