android:icon="@drawable/ic_action_create_dir"
android:orderInCategory="2"
android:showAsAction="always"
- android:title="@string/actionbar_mkdir"/>
+ android:title="@string/actionbar_mkdir"
+ android:contentDescription="@string/actionbar_mkdir"/>
<item
- android:id="@+id/action_sync_account"
- android:icon="@drawable/ic_action_refresh"
- android:orderInCategory="2"
- android:showAsAction="never"
- android:title="@string/actionbar_sync"
- android:contentDescription="@string/actionbar_sync"/>
- <item
- android:id="@+id/action_settings"
- android:icon="@drawable/ic_action_settings"
- android:orderInCategory="2"
- android:showAsAction="never"
- android:title="@string/actionbar_settings"
- android:contentDescription="@string/actionbar_settings"/>
- <item
- android:id="@+id/action_logger"
- android:icon="@drawable/ic_action_settings"
- android:orderInCategory="2"
- android:showAsAction="never"
- android:title="@string/actionbar_logger"
- android:contentDescription="@string/actionbar_logger"/>
- <item
android:id="@+id/action_sort"
- android:icon="@android:drawable/ic_menu_sort_alphabetically"
+ android:icon="@android:drawable/ic_menu_sort_by_size"
android:orderInCategory="2"
- android:showAsAction="never"
+ android:showAsAction="always"
- android:title="@string/actionbar_sort"/>
+ android:title="@string/actionbar_sort"
- android:contentDescription="@string/actionbar_sort"/>
++ android:contentDescription="@string/actionbar_sort"/>/>
<!-- <item android:id="@+id/search" android:title="@string/actionbar_search" android:icon="@drawable/ic_action_search"></item> -->
<color name="filelist_icon_backgorund">#DDDDDD</color>
<color name="owncloud_blue_bright">#00ddff</color>
<color name="list_item_lastmod_and_filesize_text">#989898</color>
+ <color name="black">#000000</color>
+ <color name="textColor">#303030</color>
+ <color name="list_divider_background">#fff0f0f0</color>
</resources>
<string name="prefs_category_security">Security</string>
<string name="prefs_instant_video_upload_path_title">Upload Video Path</string>
+ <string name="download_folder_failed_content">Download of %1$s folder could not be completed</string>
- <string name="subject_token">%1$s shared \"%2$s\" with you</string>
+ <string name="shared_subject_header">shared</string>
+ <string name="with_you_subject_header">with you</string>
++
+ <string name="subject_token">%1$s %2$s >>%3$s<< %4$s</string>
- <string name="username">Username</string>
+ <string name="auth_refresh_button">Refresh connection</string>
+ <string name="auth_host_address">Server address</string>
+ <string name="common_error_out_memory">Not enough memory</string>
+
++ <string name="username">Username</string>
</resources>
public static String getLogName() {
return getAppContext().getResources().getString(R.string.log_name);
}
+
+ public static void showOnlyFilesOnDevice(boolean state){
+ mOnlyOnDevice = state;
+ }
+
+ public static boolean getOnlyOnDevice(){
+ return mOnlyOnDevice;
+ }
+
+ // user agent
+ public static String getUserAgent() {
+ String appString = getAppContext().getResources().getString(R.string.user_agent);
+ String packageName = getAppContext().getPackageName();
+ String version = "";
+
+ PackageInfo pInfo = null;
+ try {
+ pInfo = getAppContext().getPackageManager().getPackageInfo(packageName, 0);
+ if (pInfo != null) {
+ version = pInfo.versionName;
+ }
+ } catch (PackageManager.NameNotFoundException e) {
+ Log_OC.e(TAG, "Trying to get packageName", e.getCause());
+ }
+
+ // Mozilla/5.0 (Android) ownCloud-android/1.7.0
+ String userAgent = String.format(appString, version);
+
+ return userAgent;
+ }
}
import android.database.Cursor;
import android.net.Uri;
import android.os.RemoteException;
+ import android.provider.MediaStore;
+import com.owncloud.android.MainApp;
+import com.owncloud.android.db.ProviderMeta.ProviderTableMeta;
+import com.owncloud.android.lib.common.utils.Log_OC;
+import com.owncloud.android.lib.resources.files.FileUtils;
+import com.owncloud.android.lib.resources.shares.OCShare;
+import com.owncloud.android.lib.resources.shares.ShareType;
+import com.owncloud.android.utils.FileStorageUtils;
+
public class FileDataStorageManager {
public static final int ROOT_PARENT_ID = 0;
private boolean removeLocalFolder(OCFile folder) {
boolean success = true;
- File localFolder = new File(FileStorageUtils.getDefaultSavePathFor(mAccount.name, folder));
+ String localFolderPath = FileStorageUtils.getDefaultSavePathFor(mAccount.name, folder);
+ File localFolder = new File(localFolderPath);
if (localFolder.exists()) {
// stage 1: remove the local files already registered in the files database
- Vector<OCFile> files = getFolderContent(folder.getFileId());
+ Vector<OCFile> files = getFolderContent(folder.getFileId(), false);
if (files != null) {
for (OCFile file : files) {
if (file.isFolder()) {
--- /dev/null
- mChildren = mStorageManager.getFolderContent(mLocalFolder);
+ /**
+ * ownCloud Android client application
+ *
+ * @author David A. Velasco
+ * Copyright (C) 2015 ownCloud Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2,
+ * as published by the Free Software Foundation.
+ *
+ * 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+ package com.owncloud.android.operations;
+
+ import java.io.File;
+ import java.io.FileInputStream;
+ import java.io.FileOutputStream;
+ import java.io.IOException;
+ import java.io.InputStream;
+ import java.io.OutputStream;
+ import java.util.ArrayList;
+ import java.util.HashMap;
+ import java.util.List;
+ import java.util.Map;
+ import java.util.Vector;
+
+ import org.apache.http.HttpStatus;
+ import android.accounts.Account;
+ import android.content.Context;
+ import android.content.Intent;
+ import android.util.Log;
+ //import android.support.v4.content.LocalBroadcastManager;
+
+ import com.owncloud.android.MainApp;
+ import com.owncloud.android.datamodel.FileDataStorageManager;
+ import com.owncloud.android.datamodel.OCFile;
+
+ import com.owncloud.android.lib.common.OwnCloudClient;
+ import com.owncloud.android.lib.resources.shares.OCShare;
+ import com.owncloud.android.lib.common.operations.RemoteOperation;
+ import com.owncloud.android.lib.common.operations.RemoteOperationResult;
+ import com.owncloud.android.lib.common.operations.RemoteOperationResult.ResultCode;
+ import com.owncloud.android.lib.common.utils.Log_OC;
+ import com.owncloud.android.lib.resources.shares.GetRemoteSharesForFileOperation;
+ import com.owncloud.android.lib.resources.files.FileUtils;
+ import com.owncloud.android.lib.resources.files.ReadRemoteFileOperation;
+ import com.owncloud.android.lib.resources.files.ReadRemoteFolderOperation;
+ import com.owncloud.android.lib.resources.files.RemoteFile;
+
+ import com.owncloud.android.syncadapter.FileSyncAdapter;
+ import com.owncloud.android.utils.FileStorageUtils;
+
+
+
+ /**
+ * Remote operation performing the synchronization of the list of files contained
+ * in a folder identified with its remote path.
+ *
+ * Fetches the list and properties of the files contained in the given folder, including their
+ * properties, and updates the local database with them.
+ *
+ * Does NOT enter in the child folders to synchronize their contents also.
+ */
+ public class RefreshFolderOperation extends RemoteOperation {
+
+ private static final String TAG = RefreshFolderOperation.class.getSimpleName();
+
+ public static final String EVENT_SINGLE_FOLDER_CONTENTS_SYNCED =
+ RefreshFolderOperation.class.getName() + ".EVENT_SINGLE_FOLDER_CONTENTS_SYNCED";
+ public static final String EVENT_SINGLE_FOLDER_SHARES_SYNCED =
+ RefreshFolderOperation.class.getName() + ".EVENT_SINGLE_FOLDER_SHARES_SYNCED";
+
+ /** Time stamp for the synchronization process in progress */
+ private long mCurrentSyncTime;
+
+ /** Remote folder to synchronize */
+ private OCFile mLocalFolder;
+
+ /** Access to the local database */
+ private FileDataStorageManager mStorageManager;
+
+ /** Account where the file to synchronize belongs */
+ private Account mAccount;
+
+ /** Android context; necessary to send requests to the download service */
+ private Context mContext;
+
+ /** Files and folders contained in the synchronized folder after a successful operation */
+ private List<OCFile> mChildren;
+
+ /** Counter of conflicts found between local and remote files */
+ private int mConflictsFound;
+
+ /** Counter of failed operations in synchronization of kept-in-sync files */
+ private int mFailsInFavouritesFound;
+
+ /**
+ * Map of remote and local paths to files that where locally stored in a location
+ * out of the ownCloud folder and couldn't be copied automatically into it
+ **/
+ private Map<String, String> mForgottenLocalFiles;
+
+ /** 'True' means that this operation is part of a full account synchronization */
+ private boolean mSyncFullAccount;
+
+ /** 'True' means that Share resources bound to the files into should be refreshed also */
+ private boolean mIsShareSupported;
+
+ /** 'True' means that the remote folder changed and should be fetched */
+ private boolean mRemoteFolderChanged;
+
+ /** 'True' means that Etag will be ignored */
+ private boolean mIgnoreETag;
+
+
+ /**
+ * Creates a new instance of {@link RefreshFolderOperation}.
+ *
+ * @param folder Folder to synchronize.
+ * @param currentSyncTime Time stamp for the synchronization process in progress.
+ * @param syncFullAccount 'True' means that this operation is part of a full account
+ * synchronization.
+ * @param isShareSupported 'True' means that the server supports the sharing API.
+ * @param ignoreETag 'True' means that the content of the remote folder should
+ * be fetched and updated even though the 'eTag' did not
+ * change.
+ * @param dataStorageManager Interface with the local database.
+ * @param account ownCloud account where the folder is located.
+ * @param context Application context.
+ */
+ public RefreshFolderOperation(OCFile folder,
+ long currentSyncTime,
+ boolean syncFullAccount,
+ boolean isShareSupported,
+ boolean ignoreETag,
+ FileDataStorageManager dataStorageManager,
+ Account account,
+ Context context) {
+ mLocalFolder = folder;
+ mCurrentSyncTime = currentSyncTime;
+ mSyncFullAccount = syncFullAccount;
+ mIsShareSupported = isShareSupported;
+ mStorageManager = dataStorageManager;
+ mAccount = account;
+ mContext = context;
+ mForgottenLocalFiles = new HashMap<String, String>();
+ mRemoteFolderChanged = false;
+ mIgnoreETag = ignoreETag;
+ }
+
+
+ public int getConflictsFound() {
+ return mConflictsFound;
+ }
+
+ public int getFailsInFavouritesFound() {
+ return mFailsInFavouritesFound;
+ }
+
+ public Map<String, String> getForgottenLocalFiles() {
+ return mForgottenLocalFiles;
+ }
+
+ /**
+ * Returns the list of files and folders contained in the synchronized folder,
+ * if called after synchronization is complete.
+ *
+ * @return List of files and folders contained in the synchronized folder.
+ */
+ public List<OCFile> getChildren() {
+ return mChildren;
+ }
+
+ /**
+ * Performs the synchronization.
+ *
+ * {@inheritDoc}
+ */
+ @Override
+ protected RemoteOperationResult run(OwnCloudClient client) {
+ RemoteOperationResult result = null;
+ mFailsInFavouritesFound = 0;
+ mConflictsFound = 0;
+ mForgottenLocalFiles.clear();
+
+ if (FileUtils.PATH_SEPARATOR.equals(mLocalFolder.getRemotePath()) && !mSyncFullAccount) {
+ updateOCVersion(client);
+ }
+
+ result = checkForChanges(client);
+
+ if (result.isSuccess()) {
+ if (mRemoteFolderChanged) {
+ result = fetchAndSyncRemoteFolder(client);
+ } else {
- List<OCFile> localFiles = mStorageManager.getFolderContent(mLocalFolder);
++ mChildren = mStorageManager.getFolderContent(mLocalFolder, false);
+ }
+ }
+
+ if (!mSyncFullAccount) {
+ sendLocalBroadcast(
+ EVENT_SINGLE_FOLDER_CONTENTS_SYNCED, mLocalFolder.getRemotePath(), result
+ );
+ }
+
+ if (result.isSuccess() && mIsShareSupported && !mSyncFullAccount) {
+ refreshSharesForFolder(client); // share result is ignored
+ }
+
+ if (!mSyncFullAccount) {
+ sendLocalBroadcast(
+ EVENT_SINGLE_FOLDER_SHARES_SYNCED, mLocalFolder.getRemotePath(), result
+ );
+ }
+
+ return result;
+
+ }
+
+
+ private void updateOCVersion(OwnCloudClient client) {
+ UpdateOCVersionOperation update = new UpdateOCVersionOperation(mAccount, mContext);
+ RemoteOperationResult result = update.execute(client);
+ if (result.isSuccess()) {
+ mIsShareSupported = update.getOCVersion().isSharedSupported();
+ }
+ }
+
+
+ private RemoteOperationResult checkForChanges(OwnCloudClient client) {
+ mRemoteFolderChanged = true;
+ RemoteOperationResult result = null;
+ String remotePath = null;
+
+ remotePath = mLocalFolder.getRemotePath();
+ Log_OC.d(TAG, "Checking changes in " + mAccount.name + remotePath);
+
+ // remote request
+ ReadRemoteFileOperation operation = new ReadRemoteFileOperation(remotePath);
+ result = operation.execute(client);
+ if (result.isSuccess()){
+ OCFile remoteFolder = FileStorageUtils.fillOCFile((RemoteFile) result.getData().get(0));
+
+ if (!mIgnoreETag) {
+ // check if remote and local folder are different
+ mRemoteFolderChanged =
+ !(remoteFolder.getEtag().equalsIgnoreCase(mLocalFolder.getEtag()));
+ }
+
+ result = new RemoteOperationResult(ResultCode.OK);
+
+ Log_OC.i(TAG, "Checked " + mAccount.name + remotePath + " : " +
+ (mRemoteFolderChanged ? "changed" : "not changed"));
+
+ } else {
+ // check failed
+ if (result.getCode() == ResultCode.FILE_NOT_FOUND) {
+ removeLocalFolder();
+ }
+ if (result.isException()) {
+ Log_OC.e(TAG, "Checked " + mAccount.name + remotePath + " : " +
+ result.getLogMessage(), result.getException());
+ } else {
+ Log_OC.e(TAG, "Checked " + mAccount.name + remotePath + " : " +
+ result.getLogMessage());
+ }
+ }
+
+ return result;
+ }
+
+
+ private RemoteOperationResult fetchAndSyncRemoteFolder(OwnCloudClient client) {
+ String remotePath = mLocalFolder.getRemotePath();
+ ReadRemoteFolderOperation operation = new ReadRemoteFolderOperation(remotePath);
+ RemoteOperationResult result = operation.execute(client);
+ Log_OC.d(TAG, "Synchronizing " + mAccount.name + remotePath);
+
+ if (result.isSuccess()) {
+ synchronizeData(result.getData(), client);
+ if (mConflictsFound > 0 || mFailsInFavouritesFound > 0) {
+ result = new RemoteOperationResult(ResultCode.SYNC_CONFLICT);
+ // should be a different result code, but will do the job
+ }
+ } else {
+ if (result.getCode() == ResultCode.FILE_NOT_FOUND)
+ removeLocalFolder();
+ }
+
+ return result;
+ }
+
+
+ private void removeLocalFolder() {
+ if (mStorageManager.fileExists(mLocalFolder.getFileId())) {
+ String currentSavePath = FileStorageUtils.getSavePath(mAccount.name);
+ mStorageManager.removeFolder(
+ mLocalFolder,
+ true,
+ ( mLocalFolder.isDown() &&
+ mLocalFolder.getStoragePath().startsWith(currentSavePath)
+ )
+ );
+ }
+ }
+
+
+ /**
+ * Synchronizes the data retrieved from the server about the contents of the target folder
+ * with the current data in the local database.
+ *
+ * Grants that mChildren is updated with fresh data after execution.
+ *
+ * @param folderAndFiles Remote folder and children files in Folder
+ *
+ * @param client Client instance to the remote server where the data were
+ * retrieved.
+ * @return 'True' when any change was made in the local data, 'false' otherwise
+ */
+ private void synchronizeData(ArrayList<Object> folderAndFiles, OwnCloudClient client) {
+ // get 'fresh data' from the database
+ mLocalFolder = mStorageManager.getFileByPath(mLocalFolder.getRemotePath());
+
+ // parse data from remote folder
+ OCFile remoteFolder = fillOCFile((RemoteFile)folderAndFiles.get(0));
+ remoteFolder.setParentId(mLocalFolder.getParentId());
+ remoteFolder.setFileId(mLocalFolder.getFileId());
+
+ Log_OC.d(TAG, "Remote folder " + mLocalFolder.getRemotePath()
+ + " changed - starting update of local data ");
+
+ List<OCFile> updatedFiles = new Vector<OCFile>(folderAndFiles.size() - 1);
+ List<SynchronizeFileOperation> filesToSyncContents = new Vector<SynchronizeFileOperation>();
+
+ // get current data about local contents of the folder to synchronize
++ List<OCFile> localFiles = mStorageManager.getFolderContent(mLocalFolder, true);
+ Map<String, OCFile> localFilesMap = new HashMap<String, OCFile>(localFiles.size());
+ for (OCFile file : localFiles) {
+ localFilesMap.put(file.getRemotePath(), file);
+ }
+
+ // loop to update every child
+ OCFile remoteFile = null, localFile = null;
+ for (int i=1; i<folderAndFiles.size(); i++) {
+ /// new OCFile instance with the data from the server
+ remoteFile = fillOCFile((RemoteFile)folderAndFiles.get(i));
+ remoteFile.setParentId(mLocalFolder.getFileId());
+
+ /// retrieve local data for the read file
+ // localFile = mStorageManager.getFileByPath(remoteFile.getRemotePath());
+ localFile = localFilesMap.remove(remoteFile.getRemotePath());
+
+ /// add to the remoteFile (the new one) data about LOCAL STATE (not existing in server)
+ remoteFile.setLastSyncDateForProperties(mCurrentSyncTime);
+ if (localFile != null) {
+ // some properties of local state are kept unmodified
+ remoteFile.setFileId(localFile.getFileId());
+ remoteFile.setKeepInSync(localFile.keepInSync());
+ remoteFile.setLastSyncDateForData(localFile.getLastSyncDateForData());
+ remoteFile.setModificationTimestampAtLastSyncForData(
+ localFile.getModificationTimestampAtLastSyncForData()
+ );
+ remoteFile.setStoragePath(localFile.getStoragePath());
+ // eTag will not be updated unless contents are synchronized
+ // (Synchronize[File|Folder]Operation with remoteFile as parameter)
+ remoteFile.setEtag(localFile.getEtag());
+ if (remoteFile.isFolder()) {
+ remoteFile.setFileLength(localFile.getFileLength());
+ // TODO move operations about size of folders to FileContentProvider
+ } else if (mRemoteFolderChanged && remoteFile.isImage() &&
+ remoteFile.getModificationTimestamp() !=
+ localFile.getModificationTimestamp()) {
+ remoteFile.setNeedsUpdateThumbnail(true);
+ Log.d(TAG, "Image " + remoteFile.getFileName() + " updated on the server");
+ }
+ remoteFile.setPublicLink(localFile.getPublicLink());
+ remoteFile.setShareByLink(localFile.isShareByLink());
+ } else {
+ // remote eTag will not be updated unless contents are synchronized
+ // (Synchronize[File|Folder]Operation with remoteFile as parameter)
+ remoteFile.setEtag("");
+ }
+
+ /// check and fix, if needed, local storage path
+ checkAndFixForeignStoragePath(remoteFile); // policy - local files are COPIED
+ // into the ownCloud local folder;
+ searchForLocalFileInDefaultPath(remoteFile); // legacy
+
+ /// prepare content synchronization for kept-in-sync files
+ if (remoteFile.keepInSync()) {
+ SynchronizeFileOperation operation = new SynchronizeFileOperation( localFile,
+ remoteFile,
+ mAccount,
+ true,
+ mContext
+ );
+
+ filesToSyncContents.add(operation);
+ }
+
+ updatedFiles.add(remoteFile);
+ }
+
+ // save updated contents in local database
+ mStorageManager.saveFolder(remoteFolder, updatedFiles, localFilesMap.values());
+
+ // request for the synchronization of file contents AFTER saving current remote properties
+ startContentSynchronizations(filesToSyncContents, client);
+
+ mChildren = updatedFiles;
+ }
+
+ /**
+ * Performs a list of synchronization operations, determining if a download or upload is needed
+ * or if exists conflict due to changes both in local and remote contents of the each file.
+ *
+ * If download or upload is needed, request the operation to the corresponding service and goes
+ * on.
+ *
+ * @param filesToSyncContents Synchronization operations to execute.
+ * @param client Interface to the remote ownCloud server.
+ */
+ private void startContentSynchronizations(
+ List<SynchronizeFileOperation> filesToSyncContents, OwnCloudClient client
+ ) {
+ RemoteOperationResult contentsResult = null;
+ for (SynchronizeFileOperation op: filesToSyncContents) {
+ contentsResult = op.execute(mStorageManager, mContext); // async
+ if (!contentsResult.isSuccess()) {
+ if (contentsResult.getCode() == ResultCode.SYNC_CONFLICT) {
+ mConflictsFound++;
+ } else {
+ mFailsInFavouritesFound++;
+ if (contentsResult.getException() != null) {
+ Log_OC.e(TAG, "Error while synchronizing favourites : "
+ + contentsResult.getLogMessage(), contentsResult.getException());
+ } else {
+ Log_OC.e(TAG, "Error while synchronizing favourites : "
+ + contentsResult.getLogMessage());
+ }
+ }
+ } // won't let these fails break the synchronization process
+ }
+ }
+
+
+ public boolean isMultiStatus(int status) {
+ return (status == HttpStatus.SC_MULTI_STATUS);
+ }
+
+ /**
+ * Creates and populates a new {@link OCFile} object with the data read from the server.
+ *
+ * @param remote remote file read from the server (remote file or folder).
+ * @return New OCFile instance representing the remote resource described by we.
+ */
+ private OCFile fillOCFile(RemoteFile remote) {
+ OCFile file = new OCFile(remote.getRemotePath());
+ file.setCreationTimestamp(remote.getCreationTimestamp());
+ file.setFileLength(remote.getLength());
+ file.setMimetype(remote.getMimeType());
+ file.setModificationTimestamp(remote.getModifiedTimestamp());
+ file.setEtag(remote.getEtag());
+ file.setPermissions(remote.getPermissions());
+ file.setRemoteId(remote.getRemoteId());
+ return file;
+ }
+
+
+ /**
+ * Checks the storage path of the OCFile received as parameter.
+ * If it's out of the local ownCloud folder, tries to copy the file inside it.
+ *
+ * If the copy fails, the link to the local file is nullified. The account of forgotten
+ * files is kept in {@link #mForgottenLocalFiles}
+ *)
+ * @param file File to check and fix.
+ */
+ private void checkAndFixForeignStoragePath(OCFile file) {
+ String storagePath = file.getStoragePath();
+ String expectedPath = FileStorageUtils.getDefaultSavePathFor(mAccount.name, file);
+ if (storagePath != null && !storagePath.equals(expectedPath)) {
+ /// fix storagePaths out of the local ownCloud folder
+ File originalFile = new File(storagePath);
+ if (FileStorageUtils.getUsableSpace(mAccount.name) < originalFile.length()) {
+ mForgottenLocalFiles.put(file.getRemotePath(), storagePath);
+ file.setStoragePath(null);
+
+ } else {
+ InputStream in = null;
+ OutputStream out = null;
+ try {
+ File expectedFile = new File(expectedPath);
+ File expectedParent = expectedFile.getParentFile();
+ expectedParent.mkdirs();
+ if (!expectedParent.isDirectory()) {
+ throw new IOException(
+ "Unexpected error: parent directory could not be created"
+ );
+ }
+ expectedFile.createNewFile();
+ if (!expectedFile.isFile()) {
+ throw new IOException("Unexpected error: target file could not be created");
+ }
+ in = new FileInputStream(originalFile);
+ out = new FileOutputStream(expectedFile);
+ byte[] buf = new byte[1024];
+ int len;
+ while ((len = in.read(buf)) > 0){
+ out.write(buf, 0, len);
+ }
+ file.setStoragePath(expectedPath);
+
+ } catch (Exception e) {
+ Log_OC.e(TAG, "Exception while copying foreign file " + expectedPath, e);
+ mForgottenLocalFiles.put(file.getRemotePath(), storagePath);
+ file.setStoragePath(null);
+
+ } finally {
+ try {
+ if (in != null) in.close();
+ } catch (Exception e) {
+ Log_OC.d(TAG, "Weird exception while closing input stream for "
+ + storagePath + " (ignoring)", e);
+ }
+ try {
+ if (out != null) out.close();
+ } catch (Exception e) {
+ Log_OC.d(TAG, "Weird exception while closing output stream for "
+ + expectedPath + " (ignoring)", e);
+ }
+ }
+ }
+ }
+ }
+
+
+ private RemoteOperationResult refreshSharesForFolder(OwnCloudClient client) {
+ RemoteOperationResult result = null;
+
+ // remote request
+ GetRemoteSharesForFileOperation operation =
+ new GetRemoteSharesForFileOperation(mLocalFolder.getRemotePath(), false, true);
+ result = operation.execute(client);
+
+ if (result.isSuccess()) {
+ // update local database
+ ArrayList<OCShare> shares = new ArrayList<OCShare>();
+ for(Object obj: result.getData()) {
+ shares.add((OCShare) obj);
+ }
+ mStorageManager.saveSharesInFolder(shares, mLocalFolder);
+ }
+
+ return result;
+ }
+
+
+ /**
+ * Scans the default location for saving local copies of files searching for
+ * a 'lost' file with the same full name as the {@link OCFile} received as
+ * parameter.
+ *
+ * @param file File to associate a possible 'lost' local file.
+ */
+ private void searchForLocalFileInDefaultPath(OCFile file) {
+ if (file.getStoragePath() == null && !file.isFolder()) {
+ File f = new File(FileStorageUtils.getDefaultSavePathFor(mAccount.name, file));
+ if (f.exists()) {
+ file.setStoragePath(f.getAbsolutePath());
+ file.setLastSyncDateForData(f.lastModified());
+ }
+ }
+ }
+
+
+ /**
+ * Sends a message to any application component interested in the progress
+ * of the synchronization.
+ *
+ * @param event
+ * @param dirRemotePath Remote path of a folder that was just synchronized
+ * (with or without success)
+ * @param result
+ */
+ private void sendLocalBroadcast(
+ String event, String dirRemotePath, RemoteOperationResult result
+ ) {
+ Log_OC.d(TAG, "Send broadcast " + event);
+ Intent intent = new Intent(event);
+ intent.putExtra(FileSyncAdapter.EXTRA_ACCOUNT_NAME, mAccount.name);
+ if (dirRemotePath != null) {
+ intent.putExtra(FileSyncAdapter.EXTRA_FOLDER_PATH, dirRemotePath);
+ }
+ intent.putExtra(FileSyncAdapter.EXTRA_RESULT, result);
+ mContext.sendStickyBroadcast(intent);
+ //LocalBroadcastManager.getInstance(mContext).sendBroadcast(intent);
+ }
+
+
+ public boolean getRemoteFolderChanged() {
+ return mRemoteFolderChanged;
+ }
+
+ }
import android.content.Context;
import android.content.Intent;
import android.util.Log;
- //import android.support.v4.content.LocalBroadcastManager;
+ import com.owncloud.android.MainApp;
import com.owncloud.android.datamodel.FileDataStorageManager;
import com.owncloud.android.datamodel.OCFile;
+ import com.owncloud.android.files.services.FileDownloader;
import com.owncloud.android.lib.common.OwnCloudClient;
+ import com.owncloud.android.lib.common.operations.OperationCancelledException;
+import com.owncloud.android.lib.common.operations.RemoteOperation;
import com.owncloud.android.lib.common.operations.RemoteOperationResult;
import com.owncloud.android.lib.common.operations.RemoteOperationResult.ResultCode;
import com.owncloud.android.lib.common.utils.Log_OC;
import com.owncloud.android.lib.resources.files.ReadRemoteFileOperation;
import com.owncloud.android.lib.resources.files.ReadRemoteFolderOperation;
import com.owncloud.android.lib.resources.files.RemoteFile;
+import com.owncloud.android.lib.resources.shares.GetRemoteSharesForFileOperation;
+import com.owncloud.android.lib.resources.shares.OCShare;
+import com.owncloud.android.syncadapter.FileSyncAdapter;
+ import com.owncloud.android.operations.common.SyncOperation;
+ import com.owncloud.android.services.OperationsService;
import com.owncloud.android.utils.FileStorageUtils;
+ import java.io.File;
+ import java.util.ArrayList;
+ import java.util.HashMap;
+ import java.util.List;
+ import java.util.Map;
+ import java.util.Vector;
+ import java.util.concurrent.atomic.AtomicBoolean;
+
+ //import android.support.v4.content.LocalBroadcastManager;
/**
OCFile remoteFolder = fillOCFile((RemoteFile)folderAndFiles.get(0));
remoteFolder.setParentId(mLocalFolder.getParentId());
remoteFolder.setFileId(mLocalFolder.getFileId());
-
- Log_OC.d(TAG, "Remote folder " + mLocalFolder.getRemotePath()
+
+ Log_OC.d(TAG, "Remote folder " + mLocalFolder.getRemotePath()
+ " changed - starting update of local data ");
-
+
List<OCFile> updatedFiles = new Vector<OCFile>(folderAndFiles.size() - 1);
- List<SynchronizeFileOperation> filesToSyncContents = new Vector<SynchronizeFileOperation>();
+ mFilesForDirectDownload.clear();
+ mFilesToSyncContentsWithoutUpload.clear();
+ mFavouriteFilesToSyncContents.clear();
+
+ if (mCancellationRequested.get()) {
+ throw new OperationCancelledException();
+ }
// get current data about local contents of the folder to synchronize
- List<OCFile> localFiles = mStorageManager.getFolderContent(mLocalFolder, false);
- List<OCFile> localFiles = storageManager.getFolderContent(mLocalFolder);
++ List<OCFile> localFiles = storageManager.getFolderContent(mLocalFolder, true);
Map<String, OCFile> localFilesMap = new HashMap<String, OCFile>(localFiles.size());
for (OCFile file : localFiles) {
localFilesMap.put(file.getRemotePath(), file);
}
// save updated contents in local database
- mStorageManager.saveFolder(remoteFolder, updatedFiles, localFilesMap.values());
+ storageManager.saveFolder(remoteFolder, updatedFiles, localFilesMap.values());
+
+ }
+
+
+ private void prepareOpsFromLocalKnowledge() throws OperationCancelledException {
- List<OCFile> children = getStorageManager().getFolderContent(mLocalFolder);
++ // TODO TOBI ist das richtig?
++ List<OCFile> children = getStorageManager().getFolderContent(mLocalFolder, true);
+ for (OCFile child : children) {
+ /// classify file to sync/download contents later
+ if (child.isFolder()) {
+ /// to download children files recursively
+ synchronized(mCancellationRequested) {
+ if (mCancellationRequested.get()) {
+ throw new OperationCancelledException();
+ }
+ startSyncFolderOperation(child.getRemotePath());
+ }
- // request for the synchronization of file contents AFTER saving current remote properties
- startContentSynchronizations(filesToSyncContents, client);
+ } else {
+ /// prepare limited synchronization for regular files
+ if (!child.isDown()) {
+ mFilesForDirectDownload.add(child);
+ }
+ }
+ }
+ }
- mChildren = updatedFiles;
+
+ private void syncContents(OwnCloudClient client) throws OperationCancelledException {
+ startDirectDownloads();
+ startContentSynchronizations(mFilesToSyncContentsWithoutUpload, client);
+ startContentSynchronizations(mFavouriteFilesToSyncContents, client);
+ }
+
+
+ private void startDirectDownloads() throws OperationCancelledException {
+ for (OCFile file : mFilesForDirectDownload) {
+ synchronized(mCancellationRequested) {
+ if (mCancellationRequested.get()) {
+ throw new OperationCancelledException();
+ }
+ Intent i = new Intent(mContext, FileDownloader.class);
+ i.putExtra(FileDownloader.EXTRA_ACCOUNT, mAccount);
+ i.putExtra(FileDownloader.EXTRA_FILE, file);
+ mContext.startService(i);
+ }
+ }
}
/**
import android.accounts.Account;
import android.accounts.AccountManager;
import android.accounts.AuthenticatorException;
+import android.accounts.OperationCanceledException;
import android.annotation.TargetApi;
import android.app.AlertDialog;
- import android.app.Dialog;
- import android.app.ProgressDialog;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.ContentResolver;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
- import android.os.Environment;
import android.os.IBinder;
import android.preference.PreferenceManager;
- import android.provider.DocumentsContract;
- import android.provider.MediaStore;
import android.provider.OpenableColumns;
+import android.support.v4.app.ActionBarDrawerToggle;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentTransaction;
import com.owncloud.android.operations.UnshareLinkOperation;
import com.owncloud.android.services.observer.FileObserverService;
import com.owncloud.android.syncadapter.FileSyncAdapter;
+import com.owncloud.android.ui.adapter.FileListListAdapter;
+import com.owncloud.android.ui.adapter.NavigationDrawerListAdapter;
+ import com.owncloud.android.ui.dialog.ConfirmationDialogFragment;
import com.owncloud.android.ui.dialog.CreateFolderDialogFragment;
import com.owncloud.android.ui.dialog.SslUntrustedCertDialog;
import com.owncloud.android.ui.dialog.SslUntrustedCertDialog.OnSslUntrustedCertListener;
private boolean mSyncInProgress = false;
- private String DIALOG_UNTRUSTED_CERT;
-
+ private static String DIALOG_UNTRUSTED_CERT = "DIALOG_UNTRUSTED_CERT";
+ private static String DIALOG_CREATE_FOLDER = "DIALOG_CREATE_FOLDER";
+ private static String DIALOG_UPLOAD_SOURCE = "DIALOG_UPLOAD_SOURCE";
+ private static String DIALOG_CERT_NOT_SAVED = "DIALOG_CERT_NOT_SAVED";
+
+
private OCFile mWaitingToSend;
-
+ private DrawerLayout mDrawerLayout;
+ private ActionBarDrawerToggle mDrawerToggle;
+ private boolean showAccounts = false;
+
@Override
protected void onCreate(Bundle savedInstanceState) {
- Log_OC.d(TAG, "onCreate() start");
+ Log_OC.v(TAG, "onCreate() start");
requestWindowFeature(Window.FEATURE_INDETERMINATE_PROGRESS);
-
+
super.onCreate(savedInstanceState); // this calls onAccountChanged() when ownCloud Account is valid
- // PIN CODE request ; best location is to decide, let's try this first
- if (getIntent().getAction() != null && getIntent().getAction().equals(Intent.ACTION_MAIN) && savedInstanceState == null) {
- requestPinCode();
- } else if (getIntent().getAction() == null && savedInstanceState == null) {
- requestPinCode();
- }
-
- /// grant that FileObserverService is watching favourite files
+ /// grant that FileObserverService is watching favorite files
if (savedInstanceState == null) {
Intent initObserversIntent = FileObserverService.makeInitIntent(this);
startService(initObserversIntent);
// Action bar setup
mDirectories = new CustomArrayAdapter<String>(this, R.layout.sherlock_spinner_dropdown_item);
+ getSupportActionBar().setDisplayHomeAsUpEnabled(true);
getSupportActionBar().setHomeButtonEnabled(true); // mandatory since Android ICS, according to the official documentation
+ getSupportActionBar().setDisplayShowCustomEnabled(true); // CRUCIAL - for displaying your custom actionbar
+ getSupportActionBar().setDisplayShowTitleEnabled(true);
setSupportProgressBarIndeterminateVisibility(mSyncInProgress /*|| mRefreshSharesInProgress*/); // always AFTER setContentView(...) ; to work around bug in its implementation
+ mDrawerToggle.syncState();
+
setBackgroundText();
- Log_OC.d(TAG, "onCreate() end");
+ Log_OC.v(TAG, "onCreate() end");
}
@Override
protected void onStart() {
+ Log_OC.v(TAG, "onStart() start");
super.onStart();
getSupportActionBar().setIcon(DisplayUtils.getSeasonalIconId());
+ Log_OC.v(TAG, "onStart() end");
}
+
+ @Override
+ protected void onPostCreate(Bundle savedInstanceState) {
+ super.onPostCreate(savedInstanceState);
+ // Sync the toggle state after onRestoreInstanceState has occurred.
+ mDrawerToggle.syncState();
+ }
+
+ @Override
+ public void onConfigurationChanged(Configuration newConfig) {
+ super.onConfigurationChanged(newConfig);
+ mDrawerToggle.onConfigurationChanged(newConfig);
+ }
@Override
protected void onDestroy() {
boolean retval = true;
switch (item.getItemId()) {
case R.id.action_create_dir: {
- CreateFolderDialogFragment dialog =
- CreateFolderDialogFragment.newInstance(getCurrentDir());
- dialog.show(getSupportFragmentManager(), "createdirdialog");
+ CreateFolderDialogFragment dialog = CreateFolderDialogFragment.newInstance(getCurrentDir());
+ dialog.show(getSupportFragmentManager(), DIALOG_CREATE_FOLDER);
break;
}
- case R.id.action_sync_account: {
- startSynchronization();
- break;
- }
case R.id.action_upload: {
- showDialog(DIALOG_CHOOSE_UPLOAD_SOURCE);
+ UploadSourceDialogFragment dialog = UploadSourceDialogFragment.newInstance(getAccount());
+ dialog.show(getSupportFragmentManager(), DIALOG_UPLOAD_SOURCE);
+
break;
}
- case R.id.action_settings: {
- Intent settingsIntent = new Intent(this, Preferences.class);
- startActivity(settingsIntent);
- break;
- }
- case R.id.action_logger: {
- Intent loggerIntent = new Intent(getApplicationContext(),LogHistoryActivity.class);
- startActivity(loggerIntent);
- break;
- }
case android.R.id.home: {
- FileFragment second = getSecondFragment();
- OCFile currentDir = getCurrentDir();
- if((currentDir != null && currentDir.getParentId() != 0) ||
- (second != null && second.getFile() != null)) {
- onBackPressed();
-
+ if (mDrawerLayout.isDrawerOpen(GravityCompat.START)) {
+ mDrawerLayout.closeDrawer(GravityCompat.START);
+ } else {
+ mDrawerLayout.openDrawer(GravityCompat.START);
}
+ // TODO add hamburger to left of android.R.id.home
break;
}
case R.id.action_sort: {
// only list of files - set for browsing through folders
OCFile currentDir = getCurrentDir();
boolean noRoot = (currentDir != null && currentDir.getParentId() != 0);
- actionBar.setDisplayHomeAsUpEnabled(noRoot);
- actionBar.setDisplayShowTitleEnabled(!noRoot);
+// actionBar.setDisplayHomeAsUpEnabled(noRoot);
+// actionBar.setDisplayShowTitleEnabled(!noRoot);
+ actionBar.setDisplayHomeAsUpEnabled(true);
+ actionBar.setDisplayShowTitleEnabled(true);
if (!noRoot) {
actionBar.setTitle(getString(R.string.default_display_name_for_root_folder));
+ View actionBarTitleView = getWindow().getDecorView().findViewById(actionBarTitleId);
+ if (actionBarTitleView != null) { // it's null in Android 2.x
+ actionBarTitleView.setContentDescription(getString(R.string.default_display_name_for_root_folder));
+ }
}
actionBar.setNavigationMode(!noRoot ? ActionBar.NAVIGATION_MODE_STANDARD : ActionBar.NAVIGATION_MODE_LIST);
actionBar.setListNavigationCallbacks(mDirectories, this); // assuming mDirectories is updated
*/
public void showUntrustedCertDialog(RemoteOperationResult result) {
// Show a dialog with the certificate info
- SslUntrustedCertDialog dialog = SslUntrustedCertDialog.newInstanceForFullSslError((CertificateCombinedException)result.getException());
+ SslUntrustedCertDialog dialog = SslUntrustedCertDialog.newInstanceForFullSslError(
- (CertificateCombinedException)result.getException());
++ (CertificateCombinedException) result.getException());
FragmentManager fm = getSupportFragmentManager();
FragmentTransaction ft = fm.beginTransaction();
dialog.show(ft, DIALOG_UNTRUSTED_CERT);
private void sortByName(boolean ascending){
getListOfFilesFragment().sortByName(ascending);
}
+
+ public void restart(){
+ Intent i = new Intent(this, FileDisplayActivity.class);
+ i.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
+ startActivity(i);
+ }
+
+ public void closeDrawer() {
+ mDrawerLayout.closeDrawers();
+ }
+
}
import com.owncloud.android.MainApp;
import com.owncloud.android.R;
import com.owncloud.android.authentication.AccountAuthenticator;
+import com.owncloud.android.datamodel.FileDataStorageManager;
+import com.owncloud.android.datamodel.OCFile;
+import com.owncloud.android.files.services.FileUploader;
+import com.owncloud.android.lib.common.utils.Log_OC;
++import com.owncloud.android.MainApp;
++import com.owncloud.android.R;
++import com.owncloud.android.authentication.AccountAuthenticator;
+ import com.owncloud.android.datamodel.OCFile;
+ import com.owncloud.android.files.services.FileUploader;
+ import com.owncloud.android.lib.common.operations.RemoteOperation;
+ import com.owncloud.android.lib.common.operations.RemoteOperationResult;
+ import com.owncloud.android.lib.common.utils.Log_OC;
+ import com.owncloud.android.operations.CreateFolderOperation;
+ import com.owncloud.android.ui.dialog.CreateFolderDialogFragment;
+ import com.owncloud.android.ui.dialog.LoadingDialog;
+ import com.owncloud.android.utils.CopyTmpFileAsyncTask;
import com.owncloud.android.utils.DisplayUtils;
+ import com.owncloud.android.utils.ErrorMessageAdapter;
+
/**
* This can be used to upload things to an ownCloud instance.
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
// click on folder in the list
Log_OC.d(TAG, "on item click");
- Vector<OCFile> tmpfiles = mStorageManager.getFolderContent(mFile, false);
- Vector<OCFile> tmpfiles = getStorageManager().getFolderContent(mFile);
++ Vector<OCFile> tmpfiles = getStorageManager().getFolderContent(mFile, false);
if (tmpfiles.size() <= 0) return;
// filter on dirtype
Vector<OCFile> files = new Vector<OCFile>();
actionBar.setHomeButtonEnabled(notRoot);
String full_path = generatePath(mParents);
-
+
Log_OC.d(TAG, "Populating view with content of : " + full_path);
- mFile = mStorageManager.getFileByPath(full_path);
+ mFile = getStorageManager().getFileByPath(full_path);
if (mFile != null) {
- Vector<OCFile> files = mStorageManager.getFolderContent(mFile, false);
- Vector<OCFile> files = getStorageManager().getFolderContent(mFile);
++ Vector<OCFile> files = getStorageManager().getFolderContent(mFile, false);
List<HashMap<String, Object>> data = new LinkedList<HashMap<String,Object>>();
for (OCFile f : files) {
HashMap<String, Object> h = new HashMap<String, Object>();
mAccount = AccountUtils.getCurrentOwnCloudAccount(mContext);\r
}\r
if (mStorageManager != null) {\r
- mFiles = mStorageManager.getFolderContent(mFile, onlyOnDevice);\r
- mFiles = mStorageManager.getFolderContent(mFile);\r
++ mFiles = mStorageManager.getFolderContent(mFile, false);\r
+ mFilesOrig.clear();\r
+ mFilesOrig.addAll(mFiles);\r
+ \r
if (mJustFolders) {\r
mFiles = getFolders(mFiles);\r
}\r
private OCFile mFile = null;
private FileListListAdapter mAdapter;
- private View mFooterView;
+ private boolean mJustFolders;
private OCFile mTargetFile;
-
+
+
/**
* {@inheritDoc}
directory = storageManager.getFileById(directory.getParentId());
}
- mAdapter.swapDirectory(directory, storageManager);
+ mAdapter.swapDirectory(directory, storageManager, onlyOnDevice);
if (mFile == null || !mFile.equals(directory)) {
- mList.setSelectionFromTop(0, 0);
+ mCurrentListView.setSelection(0);
}
mFile = directory;
-
- // Update Footer
- TextView footerText = (TextView) mFooterView.findViewById(R.id.footerText);
- Log_OC.d("footer", String.valueOf(System.currentTimeMillis()));
- footerText.setText(generateFooterText(directory, onlyOnDevice));
- Log_OC.d("footer", String.valueOf(System.currentTimeMillis()));
+
+ updateLayout();
+
}
}
-
- private String generateFooterText(OCFile directory, boolean onlyOnDevice) {
- Integer files = 0;
- Integer folders = 0;
- FileDataStorageManager storageManager = mContainerActivity.getStorageManager();
- Vector<OCFile> mFiles = storageManager.getFolderContent(mFile, onlyOnDevice);
+ private void updateLayout() {
+ if (!mJustFolders) {
+ int filesCount = 0, foldersCount = 0, imagesCount = 0;
+ int count = mAdapter.getCount();
+ OCFile file;
+ for (int i=0; i < count ; i++) {
+ file = (OCFile) mAdapter.getItem(i);
+ if (file.isFolder()) {
+ foldersCount++;
+ } else {
+ filesCount++;
+ if (file.isImage()){
+ imagesCount++;
+ }
+ }
+ }
+ // set footer text
+ setFooterText(generateFooterText(filesCount, foldersCount));
- for (OCFile ocFile : mFiles) {
- if (ocFile.isFolder()) {
- folders++;
+ // decide grid vs list view
+ if (((double)imagesCount / (double)filesCount) >= THUMBNAIL_THRESHOLD) {
+ switchToGridView();
} else {
- files++;
+ switchToListView();
}
}
+ }
+ private String generateFooterText(int filesCount, int foldersCount) {
String output = "";
-
- if (files > 0){
- if (files == 1) {
- output = output + files.toString() + " " + getResources().getString(R.string.file_list_file);
+ if (filesCount > 0){
+ if (filesCount == 1) {
+ output = output + filesCount + " " + getResources().getString(R.string.file_list_file);
} else {
- output = output + files.toString() + " " + getResources().getString(R.string.file_list_files);
+ output = output + filesCount + " " + getResources().getString(R.string.file_list_files);
}
}
- if (folders > 0 && files > 0){
+ if (foldersCount > 0 && filesCount > 0){
output = output + ", ";
}
- if (folders == 1) {
- output = output + folders.toString() + " " + getResources().getString(R.string.file_list_folder);
- } else if (folders > 1) {
- output = output + folders.toString() + " " + getResources().getString(R.string.file_list_folders);
+ if (foldersCount == 1) {
+ output = output + foldersCount + " " + getResources().getString(R.string.file_list_folder);
+ } else if (foldersCount > 1) {
+ output = output + foldersCount + " " + getResources().getString(R.string.file_list_folders);
}
-
+
return output;
}
-
+
+
public void sortByName(boolean descending) {
- mAdapter.setSortOrder(FileListListAdapter.SORT_NAME, descending);
+ mAdapter.setSortOrder(FileStorageUtils.SORT_NAME, descending);
}
public void sortByDate(boolean descending) {
}
public void sortBySize(boolean descending) {
- mAdapter.setSortOrder(FileListListAdapter.SORT_SIZE, descending);
+ mAdapter.setSortOrder(FileStorageUtils.SORT_SIZE, descending);
}
-
+
+
+
}
mAccount = account;
mStorageManager = storageManager;
- mImageFiles = mStorageManager.getFolderImages(parentFolder, onlyOnDevice);
- mImageFiles = mStorageManager.getFolderImages(parentFolder);
++ mImageFiles = mStorageManager.getFolderImages(parentFolder, false);
+
+ mImageFiles = FileStorageUtils.sortFolder(mImageFiles);
+
mObsoleteFragments = new HashSet<Object>();
mObsoletePositions = new HashSet<Integer>();
mDownloadErrors = new HashSet<Integer>();
}
/**
+ * Converts an HSL color value to RGB. Conversion formula
+ * adapted from http://en.wikipedia.org/wiki/HSL_color_space.
+ * Assumes h, s, and l are contained in the set [0, 1] and
+ * returns r, g, and b in the set [0, 255].
+ * from: http://axonflux.com/handy-rgb-to-hsl-and-rgb-to-hsv-color-model-c
+ *
+ * @param integer h The hue
+ * @param Integer s The saturation
+ * @param Integer l The lightness
+ * @return Array The RGB representation
+ */
+ public static int[] hslToRgb(Double h, Double s, Double l){
+ Double r, g, b;
+
+ if(s == 0){
+ r = g = b = l; // achromatic
+ } else {
+ Double q = l < 0.5 ? l * (1 + s) : l + s - l * s;
+ Double p = 2 * l - q;
+ r = hue2rgb(p, q, h + 1/3) * 255;
+ g = hue2rgb(p, q, h) * 255;
+ b = hue2rgb(p, q, h - 1/3) * 255;
+ }
+
+
+ int[] array = {r.intValue(), g.intValue(), b.intValue()};
+ return array;
+ }
+
+ private static Double hue2rgb(Double p, Double q, Double t){
+ if(t < 0) t += 1;
+ if(t > 1) t -= 1;
+ if(t < 1/6) return p + (q - p) * 6 * t;
+ if(t < 1/2) return q;
+ if(t < 2/3) return p + (q - p) * (2/3 - t) * 6;
+ return p;
+ }
+
++
++ /**
+ * Checks if file passed is an image
+ * @param file
+ * @return true/false
+ */
+ public static boolean isImage(File file) {
+ Uri selectedUri = Uri.fromFile(file);
+ String fileExtension = MimeTypeMap.getFileExtensionFromUrl(selectedUri.toString().toLowerCase());
+ String mimeType = MimeTypeMap.getSingleton().getMimeTypeFromExtension(fileExtension);
+
+ return (mimeType != null && mimeType.startsWith("image/"));
+ }
}