From cd96d9a787e8177bcc1e8db523c2d11876af8113 Mon Sep 17 00:00:00 2001 From: AndyScherzinger Date: Mon, 3 Apr 2017 18:47:17 +0200 Subject: [PATCH 001/206] add video fetching --- .../android/datamodel/MediaProvider.java | 87 +++++++++++++++++-- .../datamodel/ThumbnailsCacheManager.java | 52 +++++++++++ .../owncloud/android/utils/BitmapUtils.java | 18 ++++ 3 files changed, 148 insertions(+), 9 deletions(-) diff --git a/src/main/java/com/owncloud/android/datamodel/MediaProvider.java b/src/main/java/com/owncloud/android/datamodel/MediaProvider.java index 4bc356dd43c4..81105cbbffcf 100644 --- a/src/main/java/com/owncloud/android/datamodel/MediaProvider.java +++ b/src/main/java/com/owncloud/android/datamodel/MediaProvider.java @@ -47,12 +47,15 @@ public class MediaProvider { private static final String TAG = MediaProvider.class.getSimpleName(); // fixed query parameters - private static final Uri MEDIA_URI = android.provider.MediaStore.Images.Media.EXTERNAL_CONTENT_URI; + private static final Uri IMAGES_MEDIA_URI = android.provider.MediaStore.Images.Media.EXTERNAL_CONTENT_URI; private static final String[] FILE_PROJECTION = new String[]{MediaStore.MediaColumns.DATA}; - private static final String FILE_SELECTION = MediaStore.Images.Media.BUCKET_ID + "="; - private static final String[] FOLDER_PROJECTION = { "Distinct " + MediaStore.Images.Media.BUCKET_ID, + private static final String IMAGES_FILE_SELECTION = MediaStore.Images.Media.BUCKET_ID + "="; + private static final String[] IMAGES_FOLDER_PROJECTION = { "Distinct " + MediaStore.Images.Media.BUCKET_ID, MediaStore.Images.Media.BUCKET_DISPLAY_NAME }; - private static final String FOLDER_SORT_ORDER = MediaStore.Images.Media.BUCKET_DISPLAY_NAME + " ASC"; + private static final String IMAGES_FOLDER_SORT_ORDER = MediaStore.Images.Media.BUCKET_DISPLAY_NAME + " ASC"; + + private static final String[] VIDEOS_FOLDER_PROJECTION = { "Distinct " + MediaStore.Video.Media.BUCKET_ID, + MediaStore.Video.Media.BUCKET_DISPLAY_NAME }; /** * Getting All Images Paths. @@ -61,7 +64,7 @@ public class MediaProvider { * @param itemLimit the number of media items (usually images) to be returned per media folder. * @return list with media folders */ - public static List getMediaFolders(ContentResolver contentResolver, int itemLimit, + public static List getImageFolders(ContentResolver contentResolver, int itemLimit, final Activity activity) { // check permissions if (!PermissionUtil.checkSelfPermission(activity.getApplicationContext(), @@ -92,7 +95,7 @@ public void onClick(View v) { Cursor cursorFolders = null; if (PermissionUtil.checkSelfPermission(activity.getApplicationContext(), Manifest.permission.WRITE_EXTERNAL_STORAGE)) { - cursorFolders = contentResolver.query(MEDIA_URI, FOLDER_PROJECTION, null, null, FOLDER_SORT_ORDER); + cursorFolders = contentResolver.query(IMAGES_MEDIA_URI, IMAGES_FOLDER_PROJECTION, null, null, IMAGES_FOLDER_SORT_ORDER); } List mediaFolders = new ArrayList<>(); String dataPath = MainApp.getStoragePath() + File.separator + MainApp.getDataFolder(); @@ -113,7 +116,7 @@ public void onClick(View v) { mediaFolder.filePaths = new ArrayList<>(); // query images - cursorImages = contentResolver.query(MEDIA_URI, FILE_PROJECTION, FILE_SELECTION + folderId, null, + cursorImages = contentResolver.query(IMAGES_MEDIA_URI, FILE_PROJECTION, IMAGES_FILE_SELECTION + folderId, null, fileSortOrder); Log.d(TAG, "Reading images for " + mediaFolder.folderName); @@ -138,9 +141,75 @@ public void onClick(View v) { // count images Cursor count = contentResolver.query( - MEDIA_URI, + IMAGES_MEDIA_URI, + FILE_PROJECTION, + IMAGES_FILE_SELECTION + folderId, + null, + null); + + if (count != null) { + mediaFolder.numberOfFiles = count.getCount(); + count.close(); + } + + mediaFolders.add(mediaFolder); + } + } + } + cursorFolders.close(); + } + + return mediaFolders; + } + + public static List getMediaFolders(ContentResolver contentResolver, int itemLimit) { + Cursor cursorFolders = contentResolver.query(MediaStore.Video.Media.EXTERNAL_CONTENT_URI, + VIDEOS_FOLDER_PROJECTION, null, null, null); + List mediaFolders = new ArrayList<>(); + String dataPath = MainApp.getStoragePath() + File.separator + MainApp.getDataFolder(); + + if (cursorFolders != null) { + String folderName; + String fileSortOrder = MediaStore.Video.Media.DATE_TAKEN + " DESC LIMIT " + itemLimit; + Cursor cursorImages; + + while (cursorFolders.moveToNext()) { + String folderId = cursorFolders.getString(cursorFolders.getColumnIndex(MediaStore.Video.Media + .BUCKET_ID)); + + MediaFolder mediaFolder = new MediaFolder(); + folderName = cursorFolders.getString(cursorFolders.getColumnIndex( + MediaStore.Video.Media.BUCKET_DISPLAY_NAME)); + mediaFolder.folderName = folderName; + mediaFolder.filePaths = new ArrayList<>(); + + // query images + cursorImages = contentResolver.query( + MediaStore.Video.Media.EXTERNAL_CONTENT_URI, + FILE_PROJECTION, + MediaStore.Video.Media.BUCKET_ID + "=" + folderId, + null, + fileSortOrder); + Log.d(TAG, "Reading videos for " + mediaFolder.folderName); + + if (cursorImages != null) { + String filePath; + while (cursorImages.moveToNext()) { + filePath = cursorImages.getString(cursorImages.getColumnIndexOrThrow( + MediaStore.MediaColumns.DATA)); + mediaFolder.filePaths.add(filePath); + mediaFolder.absolutePath = filePath.substring(0, filePath.lastIndexOf("/")); + } + cursorImages.close(); + + // only do further work if folder is not within the Nextcloud app itself + if (!mediaFolder.absolutePath.startsWith(dataPath)) { + + // count images + Cursor count = contentResolver.query( + MediaStore.Video.Media.EXTERNAL_CONTENT_URI, FILE_PROJECTION, - FILE_SELECTION + folderId, + MediaStore.Video.Media.BUCKET_ID + "=" + folderId, null, null); diff --git a/src/main/java/com/owncloud/android/datamodel/ThumbnailsCacheManager.java b/src/main/java/com/owncloud/android/datamodel/ThumbnailsCacheManager.java index a9ceadc77472..126606fd90be 100644 --- a/src/main/java/com/owncloud/android/datamodel/ThumbnailsCacheManager.java +++ b/src/main/java/com/owncloud/android/datamodel/ThumbnailsCacheManager.java @@ -30,6 +30,7 @@ import android.graphics.Paint; import android.graphics.drawable.BitmapDrawable; import android.graphics.drawable.Drawable; +import android.media.MediaMetadataRetriever; import android.media.ThumbnailUtils; import android.net.Uri; import android.os.AsyncTask; @@ -440,6 +441,8 @@ protected Bitmap doInBackground(Object... params) { if (MimeTypeUtil.isImage(mFile)) { thumbnail = doFileInBackground(mFile); + } else if (MimeTypeUtil.isVideo(mFile)) { + thumbnail = doVideoInBackground(mFile); } } } // the app should never break due to a problem with thumbnails @@ -464,6 +467,7 @@ protected void onPostExecute(Bitmap bitmap) { if (bitmap != null) { if (tagId.equals(String.valueOf(imageView.getTag()))) { + Log_OC.e(TAG, "SET RENDERED VIDEO THUMBNAIL"); imageView.setImageBitmap(bitmap); } } else { @@ -473,6 +477,7 @@ protected void onPostExecute(Bitmap bitmap) { } else { if (MimeTypeUtil.isVideo(mFile)) { imageView.setImageBitmap(ThumbnailsCacheManager.mDefaultVideo); + Log_OC.e(TAG, "SET VIDEO THUMBNAIL"); } else { imageView.setImageDrawable(MimeTypeUtil.getFileTypeIcon(null, mFile.getName(), null)); } @@ -507,6 +512,53 @@ private Bitmap doFileInBackground(File file) { } return thumbnail; } + + private Bitmap doVideoInBackground(File file) { + Bitmap bitmap = null; + final String imageKey; + + if (mImageKey != null) { + imageKey = mImageKey; + } else { + imageKey = String.valueOf(file.hashCode()); + } + + // Check disk cache in background thread + Bitmap thumbnail = getBitmapFromDiskCache(imageKey); + + // Not found in disk cache + if (thumbnail == null) { + MediaMetadataRetriever retriever = new MediaMetadataRetriever(); + try { + retriever.setDataSource(file.getAbsolutePath()); + bitmap = retriever.getFrameAtTime(-1); + } catch (Exception ex) { + // can't create a bitmap + } finally { + try { + retriever.release(); + } catch (RuntimeException ex) { + // Ignore failure at this point. + } + } + + if (bitmap == null) { + return null; + } + + // Scale down bitmap if too large. + int px = getThumbnailDimension(); + int width = bitmap.getWidth(); + int height = bitmap.getHeight(); + int max = Math.max(width, height); + if (max > px) { + bitmap = BitmapUtils.scaleBitmap(bitmap, px, width, height, max); + bitmap = addThumbnailToCache(imageKey, bitmap, file.getPath(), px); + } + } + + return bitmap; + } } public static class AvatarGenerationTask extends AsyncTask { diff --git a/src/main/java/com/owncloud/android/utils/BitmapUtils.java b/src/main/java/com/owncloud/android/utils/BitmapUtils.java index 7726cac3991d..1682a2c96aee 100644 --- a/src/main/java/com/owncloud/android/utils/BitmapUtils.java +++ b/src/main/java/com/owncloud/android/utils/BitmapUtils.java @@ -106,6 +106,24 @@ private static int calculateSampleFactor(Options options, int reqWidth, int reqH return inSampleSize; } + /** + * scales a given bitmap depending on the given size parameters. + * + * @param bitmap the bitmap to be scaled + * @param px the target pixel size + * @param width the width + * @param height the height + * @param max the max(height, width) + * @return the scaled bitmap + */ + public static Bitmap scaleBitmap(Bitmap bitmap, float px, int width, int height, int max) { + float scale = px / max; + int w = Math.round(scale * width); + int h = Math.round(scale * height); + bitmap = Bitmap.createScaledBitmap(bitmap, w, h, true); + return bitmap; + } + /** * Rotate bitmap according to EXIF orientation. * Cf. http://www.daveperrett.com/articles/2012/07/28/exif-orientation-handling-is-a-ghetto/ From 8c601c0fc551587ec2b6f1aef409e3ae17a7aef6 Mon Sep 17 00:00:00 2001 From: AndyScherzinger Date: Mon, 3 Apr 2017 19:13:31 +0200 Subject: [PATCH 002/206] add videos to the list --- .../java/com/owncloud/android/datamodel/MediaFolder.java | 6 ++++++ .../com/owncloud/android/datamodel/MediaProvider.java | 4 +++- .../owncloud/android/ui/activity/FolderSyncActivity.java | 9 ++++++++- 3 files changed, 17 insertions(+), 2 deletions(-) diff --git a/src/main/java/com/owncloud/android/datamodel/MediaFolder.java b/src/main/java/com/owncloud/android/datamodel/MediaFolder.java index e3e29861db4a..c641c8b5c281 100644 --- a/src/main/java/com/owncloud/android/datamodel/MediaFolder.java +++ b/src/main/java/com/owncloud/android/datamodel/MediaFolder.java @@ -27,6 +27,10 @@ * Business object representing a media folder with all information that are gathered via media queries. */ public class MediaFolder { + public enum FOLDER_TYPE { + AUDIO, VIDEO, IMAGE, IMAGE_VIDEO, CUSTOM + } + /** name of the folder. */ public String folderName; @@ -38,4 +42,6 @@ public class MediaFolder { /** total number of files in the media folder. */ public long numberOfFiles; + + public FOLDER_TYPE type; } diff --git a/src/main/java/com/owncloud/android/datamodel/MediaProvider.java b/src/main/java/com/owncloud/android/datamodel/MediaProvider.java index 81105cbbffcf..4ec5c6929374 100644 --- a/src/main/java/com/owncloud/android/datamodel/MediaProvider.java +++ b/src/main/java/com/owncloud/android/datamodel/MediaProvider.java @@ -112,6 +112,7 @@ public void onClick(View v) { MediaFolder mediaFolder = new MediaFolder(); folderName = cursorFolders.getString(cursorFolders.getColumnIndex( MediaStore.Images.Media.BUCKET_DISPLAY_NAME)); + mediaFolder.type = MediaFolder.FOLDER_TYPE.IMAGE; mediaFolder.folderName = folderName; mediaFolder.filePaths = new ArrayList<>(); @@ -162,7 +163,7 @@ public void onClick(View v) { return mediaFolders; } - public static List getMediaFolders(ContentResolver contentResolver, int itemLimit) { + public static List getVideoFolders(ContentResolver contentResolver, int itemLimit) { Cursor cursorFolders = contentResolver.query(MediaStore.Video.Media.EXTERNAL_CONTENT_URI, VIDEOS_FOLDER_PROJECTION, null, null, null); List mediaFolders = new ArrayList<>(); @@ -180,6 +181,7 @@ public static List getMediaFolders(ContentResolver contentResolver, MediaFolder mediaFolder = new MediaFolder(); folderName = cursorFolders.getString(cursorFolders.getColumnIndex( MediaStore.Video.Media.BUCKET_DISPLAY_NAME)); + mediaFolder.type = MediaFolder.FOLDER_TYPE.VIDEO; mediaFolder.folderName = folderName; mediaFolder.filePaths = new ArrayList<>(); diff --git a/src/main/java/com/owncloud/android/ui/activity/FolderSyncActivity.java b/src/main/java/com/owncloud/android/ui/activity/FolderSyncActivity.java index c2fcb5d347e6..6d6236feac40 100644 --- a/src/main/java/com/owncloud/android/ui/activity/FolderSyncActivity.java +++ b/src/main/java/com/owncloud/android/ui/activity/FolderSyncActivity.java @@ -48,6 +48,7 @@ import com.owncloud.android.datamodel.SyncedFolder; import com.owncloud.android.datamodel.SyncedFolderDisplayItem; import com.owncloud.android.datamodel.SyncedFolderProvider; +import com.owncloud.android.lib.common.utils.Log_OC; import com.owncloud.android.files.services.FileUploader; import com.owncloud.android.ui.adapter.FolderSyncAdapter; import com.owncloud.android.ui.decoration.MediaGridItemDecoration; @@ -177,8 +178,14 @@ private void load(final int perFolderMediaItemLimit, boolean force) { new Thread(new Runnable() { @Override public void run() { - final List mediaFolders = MediaProvider.getMediaFolders(getContentResolver(), + final List mediaFolders = MediaProvider.getImageFolders(getContentResolver(), perFolderMediaItemLimit, FolderSyncActivity.this); + Log_OC.w(TAG, "Picture Folders: " + mediaFolders.size()); + mediaFolders.addAll(MediaProvider.getVideoFolders(getContentResolver(),perFolderMediaItemLimit)); + Log_OC.w(TAG, "Picture+Video Folders: " + mediaFolders.size()); + + //TODO properly merge image and video lists to remove duplicates + List syncedFolderArrayList = mSyncedFolderProvider.getSyncedFolders(); List currentAccountSyncedFoldersList = new ArrayList<>(); Account currentAccount = AccountUtils.getCurrentOwnCloudAccount(FolderSyncActivity.this); From 6b385eca7af145ddcbff3c7436b6cc7693db034c Mon Sep 17 00:00:00 2001 From: AndyScherzinger Date: Mon, 3 Apr 2017 19:16:07 +0200 Subject: [PATCH 003/206] remove logs --- .../com/owncloud/android/datamodel/ThumbnailsCacheManager.java | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/main/java/com/owncloud/android/datamodel/ThumbnailsCacheManager.java b/src/main/java/com/owncloud/android/datamodel/ThumbnailsCacheManager.java index 126606fd90be..dc4537932650 100644 --- a/src/main/java/com/owncloud/android/datamodel/ThumbnailsCacheManager.java +++ b/src/main/java/com/owncloud/android/datamodel/ThumbnailsCacheManager.java @@ -467,7 +467,6 @@ protected void onPostExecute(Bitmap bitmap) { if (bitmap != null) { if (tagId.equals(String.valueOf(imageView.getTag()))) { - Log_OC.e(TAG, "SET RENDERED VIDEO THUMBNAIL"); imageView.setImageBitmap(bitmap); } } else { @@ -477,7 +476,6 @@ protected void onPostExecute(Bitmap bitmap) { } else { if (MimeTypeUtil.isVideo(mFile)) { imageView.setImageBitmap(ThumbnailsCacheManager.mDefaultVideo); - Log_OC.e(TAG, "SET VIDEO THUMBNAIL"); } else { imageView.setImageDrawable(MimeTypeUtil.getFileTypeIcon(null, mFile.getName(), null)); } From d37ed3eb56ed0675f4d34274abff94c0644236ee Mon Sep 17 00:00:00 2001 From: AndyScherzinger Date: Mon, 3 Apr 2017 20:43:31 +0200 Subject: [PATCH 004/206] fix video thumbnails --- .../owncloud/android/datamodel/ThumbnailsCacheManager.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/java/com/owncloud/android/datamodel/ThumbnailsCacheManager.java b/src/main/java/com/owncloud/android/datamodel/ThumbnailsCacheManager.java index dc4537932650..edb561f66462 100644 --- a/src/main/java/com/owncloud/android/datamodel/ThumbnailsCacheManager.java +++ b/src/main/java/com/owncloud/android/datamodel/ThumbnailsCacheManager.java @@ -512,7 +512,7 @@ private Bitmap doFileInBackground(File file) { } private Bitmap doVideoInBackground(File file) { - Bitmap bitmap = null; + Bitmap bitmap; final String imageKey; if (mImageKey != null) { @@ -522,10 +522,10 @@ private Bitmap doVideoInBackground(File file) { } // Check disk cache in background thread - Bitmap thumbnail = getBitmapFromDiskCache(imageKey); + bitmap = getBitmapFromDiskCache(imageKey); // Not found in disk cache - if (thumbnail == null) { + if (bitmap == null) { MediaMetadataRetriever retriever = new MediaMetadataRetriever(); try { retriever.setDataSource(file.getAbsolutePath()); From 6fa0b7665b94fb020f06153f485b937c9f8ed4b2 Mon Sep 17 00:00:00 2001 From: AndyScherzinger Date: Mon, 3 Apr 2017 20:53:57 +0200 Subject: [PATCH 005/206] simplified thumbnail generation --- .../datamodel/ThumbnailsCacheManager.java | 87 ++++++++----------- 1 file changed, 36 insertions(+), 51 deletions(-) diff --git a/src/main/java/com/owncloud/android/datamodel/ThumbnailsCacheManager.java b/src/main/java/com/owncloud/android/datamodel/ThumbnailsCacheManager.java index edb561f66462..7ed0a3a8bd8c 100644 --- a/src/main/java/com/owncloud/android/datamodel/ThumbnailsCacheManager.java +++ b/src/main/java/com/owncloud/android/datamodel/ThumbnailsCacheManager.java @@ -419,6 +419,7 @@ private Bitmap doFileInBackground() { } public static class MediaThumbnailGenerationTask extends AsyncTask { + private enum Type {IMAGE, VIDEO} private final WeakReference mImageViewReference; private File mFile; private String mImageKey = null; @@ -440,9 +441,9 @@ protected Bitmap doInBackground(Object... params) { } if (MimeTypeUtil.isImage(mFile)) { - thumbnail = doFileInBackground(mFile); + thumbnail = doFileInBackground(mFile, Type.IMAGE); } else if (MimeTypeUtil.isVideo(mFile)) { - thumbnail = doVideoInBackground(mFile); + thumbnail = doFileInBackground(mFile, Type.VIDEO); } } } // the app should never break due to a problem with thumbnails @@ -485,7 +486,7 @@ protected void onPostExecute(Bitmap bitmap) { } } - private Bitmap doFileInBackground(File file) { + private Bitmap doFileInBackground(File file, Type type) { final String imageKey; if (mImageKey != null) { @@ -500,62 +501,46 @@ private Bitmap doFileInBackground(File file) { // Not found in disk cache if (thumbnail == null) { - int px = getThumbnailDimension(); - - Bitmap bitmap = BitmapUtils.decodeSampledBitmapFromFile(file.getAbsolutePath(), px, px); - - if (bitmap != null) { - thumbnail = addThumbnailToCache(imageKey, bitmap, file.getPath(), px); - } - } - return thumbnail; - } - - private Bitmap doVideoInBackground(File file) { - Bitmap bitmap; - final String imageKey; + if (Type.IMAGE.equals(type)) { + int px = getThumbnailDimension(); - if (mImageKey != null) { - imageKey = mImageKey; - } else { - imageKey = String.valueOf(file.hashCode()); - } + Bitmap bitmap = BitmapUtils.decodeSampledBitmapFromFile(file.getAbsolutePath(), px, px); - // Check disk cache in background thread - bitmap = getBitmapFromDiskCache(imageKey); - - // Not found in disk cache - if (bitmap == null) { - MediaMetadataRetriever retriever = new MediaMetadataRetriever(); - try { - retriever.setDataSource(file.getAbsolutePath()); - bitmap = retriever.getFrameAtTime(-1); - } catch (Exception ex) { - // can't create a bitmap - } finally { + if (bitmap != null) { + thumbnail = addThumbnailToCache(imageKey, bitmap, file.getPath(), px); + } + } else if (Type.VIDEO.equals(type)) { + MediaMetadataRetriever retriever = new MediaMetadataRetriever(); try { - retriever.release(); - } catch (RuntimeException ex) { - // Ignore failure at this point. + retriever.setDataSource(file.getAbsolutePath()); + thumbnail = retriever.getFrameAtTime(-1); + } catch (Exception ex) { + // can't create a bitmap + Log_OC.w(TAG, "Failed to create bitmap from video " + file.getAbsolutePath()); + } finally { + try { + retriever.release(); + } catch (RuntimeException ex) { + // Ignore failure at this point. + Log_OC.w(TAG, "Failed release MediaMetadataRetriever for " + file.getAbsolutePath()); + } } - } - - if (bitmap == null) { - return null; - } - // Scale down bitmap if too large. - int px = getThumbnailDimension(); - int width = bitmap.getWidth(); - int height = bitmap.getHeight(); - int max = Math.max(width, height); - if (max > px) { - bitmap = BitmapUtils.scaleBitmap(bitmap, px, width, height, max); - bitmap = addThumbnailToCache(imageKey, bitmap, file.getPath(), px); + if (thumbnail != null) { + // Scale down bitmap if too large. + int px = getThumbnailDimension(); + int width = thumbnail.getWidth(); + int height = thumbnail.getHeight(); + int max = Math.max(width, height); + if (max > px) { + thumbnail = BitmapUtils.scaleBitmap(thumbnail, px, width, height, max); + thumbnail = addThumbnailToCache(imageKey, thumbnail, file.getPath(), px); + } + } } } - return bitmap; + return thumbnail; } } From 63475bd164a6f8216cff45cd5ddbc53d5b54c123 Mon Sep 17 00:00:00 2001 From: AndyScherzinger Date: Mon, 3 Apr 2017 21:37:00 +0200 Subject: [PATCH 006/206] add icons to the grid headers depending on the type --- .../android/datamodel/MediaFolder.java | 10 +++++--- .../android/datamodel/MediaProvider.java | 4 +-- .../datamodel/SyncedFolderDisplayItem.java | 14 ++++++++-- .../ui/activity/FolderSyncActivity.java | 6 +++-- .../android/ui/adapter/FolderSyncAdapter.java | 8 ++++++ src/main/res/drawable-hdpi/ic_image_18dp.png | Bin 0 -> 432 bytes src/main/res/drawable-hdpi/ic_video_18dp.png | Bin 0 -> 336 bytes src/main/res/drawable-mdpi/ic_image_18dp.png | Bin 0 -> 339 bytes src/main/res/drawable-mdpi/ic_video_18dp.png | Bin 0 -> 318 bytes src/main/res/drawable-xhdpi/ic_image_18dp.png | Bin 0 -> 529 bytes src/main/res/drawable-xhdpi/ic_video_18dp.png | Bin 0 -> 363 bytes .../res/drawable-xxhdpi/ic_image_18dp.png | Bin 0 -> 742 bytes .../res/drawable-xxhdpi/ic_video_18dp.png | Bin 0 -> 436 bytes .../res/drawable-xxxhdpi/ic_image_18dp.png | Bin 0 -> 936 bytes .../res/drawable-xxxhdpi/ic_video_18dp.png | Bin 0 -> 460 bytes .../res/layout/folder_sync_item_header.xml | 24 ++++++++++++++++-- 16 files changed, 54 insertions(+), 12 deletions(-) create mode 100644 src/main/res/drawable-hdpi/ic_image_18dp.png create mode 100644 src/main/res/drawable-hdpi/ic_video_18dp.png create mode 100644 src/main/res/drawable-mdpi/ic_image_18dp.png create mode 100644 src/main/res/drawable-mdpi/ic_video_18dp.png create mode 100644 src/main/res/drawable-xhdpi/ic_image_18dp.png create mode 100644 src/main/res/drawable-xhdpi/ic_video_18dp.png create mode 100644 src/main/res/drawable-xxhdpi/ic_image_18dp.png create mode 100644 src/main/res/drawable-xxhdpi/ic_video_18dp.png create mode 100644 src/main/res/drawable-xxxhdpi/ic_image_18dp.png create mode 100644 src/main/res/drawable-xxxhdpi/ic_video_18dp.png diff --git a/src/main/java/com/owncloud/android/datamodel/MediaFolder.java b/src/main/java/com/owncloud/android/datamodel/MediaFolder.java index c641c8b5c281..194ed0624bbb 100644 --- a/src/main/java/com/owncloud/android/datamodel/MediaFolder.java +++ b/src/main/java/com/owncloud/android/datamodel/MediaFolder.java @@ -27,9 +27,11 @@ * Business object representing a media folder with all information that are gathered via media queries. */ public class MediaFolder { - public enum FOLDER_TYPE { - AUDIO, VIDEO, IMAGE, IMAGE_VIDEO, CUSTOM - } + public static final int AUDIO = 3; + public static final int VIDEO = 1; + public static final int IMAGE = 2; + public static final int IMAGE_VIDEO = 3; + public static final int CUSTOM = 4; /** name of the folder. */ public String folderName; @@ -43,5 +45,5 @@ public enum FOLDER_TYPE { /** total number of files in the media folder. */ public long numberOfFiles; - public FOLDER_TYPE type; + public int type; } diff --git a/src/main/java/com/owncloud/android/datamodel/MediaProvider.java b/src/main/java/com/owncloud/android/datamodel/MediaProvider.java index 4ec5c6929374..b36f913eb917 100644 --- a/src/main/java/com/owncloud/android/datamodel/MediaProvider.java +++ b/src/main/java/com/owncloud/android/datamodel/MediaProvider.java @@ -112,7 +112,7 @@ public void onClick(View v) { MediaFolder mediaFolder = new MediaFolder(); folderName = cursorFolders.getString(cursorFolders.getColumnIndex( MediaStore.Images.Media.BUCKET_DISPLAY_NAME)); - mediaFolder.type = MediaFolder.FOLDER_TYPE.IMAGE; + mediaFolder.type = MediaFolder.IMAGE; mediaFolder.folderName = folderName; mediaFolder.filePaths = new ArrayList<>(); @@ -181,7 +181,7 @@ public static List getVideoFolders(ContentResolver contentResolver, MediaFolder mediaFolder = new MediaFolder(); folderName = cursorFolders.getString(cursorFolders.getColumnIndex( MediaStore.Video.Media.BUCKET_DISPLAY_NAME)); - mediaFolder.type = MediaFolder.FOLDER_TYPE.VIDEO; + mediaFolder.type = MediaFolder.VIDEO; mediaFolder.folderName = folderName; mediaFolder.filePaths = new ArrayList<>(); diff --git a/src/main/java/com/owncloud/android/datamodel/SyncedFolderDisplayItem.java b/src/main/java/com/owncloud/android/datamodel/SyncedFolderDisplayItem.java index 5ee7e90bef4d..d7e45aacfab9 100644 --- a/src/main/java/com/owncloud/android/datamodel/SyncedFolderDisplayItem.java +++ b/src/main/java/com/owncloud/android/datamodel/SyncedFolderDisplayItem.java @@ -31,6 +31,7 @@ public class SyncedFolderDisplayItem extends SyncedFolder { private List filePaths; private String folderName; private long numberOfFiles; + private int type; /** * constructor for the display item specialization for a synced folder object. @@ -47,14 +48,16 @@ public class SyncedFolderDisplayItem extends SyncedFolder { * @param filePaths the UI info for the file path * @param folderName the UI info for the folder's name * @param numberOfFiles the UI info for number of files within the folder + * @param type the type of the folder */ public SyncedFolderDisplayItem(long id, String localPath, String remotePath, Boolean wifiOnly, Boolean chargingOnly, Boolean subfolderByDate, String account, Integer uploadAction, Boolean enabled, - List filePaths, String folderName, long numberOfFiles) { + List filePaths, String folderName, long numberOfFiles, int type) { super(id, localPath, remotePath, wifiOnly, chargingOnly, subfolderByDate, account, uploadAction, enabled); this.filePaths = filePaths; this.folderName = folderName; this.numberOfFiles = numberOfFiles; + this.type = type; } public SyncedFolderDisplayItem(long id, String localPath, String remotePath, Boolean wifiOnly, Boolean chargingOnly, @@ -64,7 +67,6 @@ public SyncedFolderDisplayItem(long id, String localPath, String remotePath, Boo this.folderName = folderName; } - public List getFilePaths() { return filePaths; } @@ -88,4 +90,12 @@ public long getNumberOfFiles() { public void setNumberOfFiles(long numberOfFiles) { this.numberOfFiles = numberOfFiles; } + + public int getType() { + return type; + } + + public void setType(int type) { + this.type = type; + } } diff --git a/src/main/java/com/owncloud/android/ui/activity/FolderSyncActivity.java b/src/main/java/com/owncloud/android/ui/activity/FolderSyncActivity.java index 6d6236feac40..720733b977ec 100644 --- a/src/main/java/com/owncloud/android/ui/activity/FolderSyncActivity.java +++ b/src/main/java/com/owncloud/android/ui/activity/FolderSyncActivity.java @@ -318,7 +318,8 @@ private SyncedFolderDisplayItem createSyncedFolder(@NonNull SyncedFolder syncedF syncedFolder.isEnabled(), mediaFolder.filePaths, mediaFolder.folderName, - mediaFolder.numberOfFiles); + mediaFolder.numberOfFiles, + mediaFolder.type); } /** @@ -341,7 +342,8 @@ private SyncedFolderDisplayItem createSyncedFolderFromMediaFolder(@NonNull Media false, mediaFolder.filePaths, mediaFolder.folderName, - mediaFolder.numberOfFiles); + mediaFolder.numberOfFiles, + mediaFolder.type); } /** diff --git a/src/main/java/com/owncloud/android/ui/adapter/FolderSyncAdapter.java b/src/main/java/com/owncloud/android/ui/adapter/FolderSyncAdapter.java index 92ca34aefd0d..0723fa437b30 100644 --- a/src/main/java/com/owncloud/android/ui/adapter/FolderSyncAdapter.java +++ b/src/main/java/com/owncloud/android/ui/adapter/FolderSyncAdapter.java @@ -33,6 +33,7 @@ import com.afollestad.sectionedrecyclerview.SectionedRecyclerViewAdapter; import com.owncloud.android.R; +import com.owncloud.android.datamodel.MediaFolder; import com.owncloud.android.datamodel.SyncedFolderDisplayItem; import com.owncloud.android.datamodel.ThumbnailsCacheManager; import com.owncloud.android.utils.ThemeUtils; @@ -90,6 +91,11 @@ public int getItemCount(int section) { @Override public void onBindHeaderViewHolder(final MainViewHolder holder, final int section) { holder.title.setText(mSyncFolderItems.get(section).getFolderName()); + if (MediaFolder.VIDEO == mSyncFolderItems.get(section).getType()) { + holder.type.setImageResource(R.drawable.ic_video_18dp); + } else { + holder.type.setImageResource(R.drawable.ic_image_18dp); + } holder.syncStatusButton.setVisibility(View.VISIBLE); holder.syncStatusButton.setTag(section); holder.syncStatusButton.setOnClickListener(new View.OnClickListener() { @@ -175,6 +181,7 @@ public interface ClickListener { static class MainViewHolder extends RecyclerView.ViewHolder { private final ImageView image; private final TextView title; + private final ImageView type; private final ImageButton menuButton; private final ImageButton syncStatusButton; private final LinearLayout counterBar; @@ -185,6 +192,7 @@ private MainViewHolder(View itemView) { super(itemView); image = (ImageView) itemView.findViewById(R.id.thumbnail); title = (TextView) itemView.findViewById(R.id.title); + type = (ImageView) itemView.findViewById(R.id.type); menuButton = (ImageButton) itemView.findViewById(R.id.settingsButton); syncStatusButton = (ImageButton) itemView.findViewById(R.id.syncStatusButton); counterBar = (LinearLayout) itemView.findViewById(R.id.counterLayout); diff --git a/src/main/res/drawable-hdpi/ic_image_18dp.png b/src/main/res/drawable-hdpi/ic_image_18dp.png new file mode 100644 index 0000000000000000000000000000000000000000..957495d07a7da6f60180f945ec3e9daa34460169 GIT binary patch literal 432 zcmeAS@N?(olHy`uVBq!ia0vp^(jd&i1|)m0d(}6TtKf~3pEvFe67_B{B978;gzrD86>#&2&@sICWUq8DdUbJGJ z-q9UaKiF0(G<78`e8zONya8jIt#{Tzr{l&#PwfcD81}nQ~v~NAb_v zzTG^HR-#9qusaHDX)sMbvnYGz5|$ksuEkAF0*dq)PIsMMV7%3evub}z@-0!v$hn#R ze0%ORE$v!x$nN~sqpGH1$^W}NFCC6C@I3zbV})eJ8ZO}ijy=zxT`!b*|LynR?Iyn0 z8&lGJCh;CFh`$uZ#%$wxOM-_@o*_`=F7xfpN#;Oh3=!G>jmioiZO(7Mw$D$G=kNy> zi}IuMqU$xBH%OFV@(lt3A7{o?G=j zZ)3CH^5*0=hhizSGxK~GJk=8Y!niYeO3AVF;^x^?zSgF%`>#-{byEG-yVkGf&k7`g PVanj?>gTe~DWM4fI*YHn literal 0 HcmV?d00001 diff --git a/src/main/res/drawable-hdpi/ic_video_18dp.png b/src/main/res/drawable-hdpi/ic_video_18dp.png new file mode 100644 index 0000000000000000000000000000000000000000..484034e2798fa2354d63ef5284f8e714201ccbde GIT binary patch literal 336 zcmeAS@N?(olHy`uVBq!ia0vp^(jd&i1|)m0d(}6TtKf~3pEvJE;+nz3tAs)w*6C_v{Cv0GFOYw7dX1@IW{r$E7 z{{21exXRJT$H#ovu3hsU1RriX^Yh!==)3>_|9>qB1RktF*8e%PXKNc*etP0>u<_ER zpx^J_y=yLDUzi!NRAEZR&K)~g66S2$w5g7bt<6N_esF??UqD#cHIP20lEPKdZKsvk z*tS?K7Wwo0`}+3AMn#e7Hd|}Oy8I`p6`JYjM*Etdo-t#FmxQFG`i0Z!Jh8E{tvhs< zc+PpaN#LlFo7=J5A~!z%yuCeNeu8dFLc)yJBs-(_YfJxWNU%1uGcX*|_jz{X^^e&= PZ!&ng`njxgN@xNAsgH+{ literal 0 HcmV?d00001 diff --git a/src/main/res/drawable-mdpi/ic_image_18dp.png b/src/main/res/drawable-mdpi/ic_image_18dp.png new file mode 100644 index 0000000000000000000000000000000000000000..eb568329950179b3895b35ed452cf2d46b9d2176 GIT binary patch literal 339 zcmeAS@N?(olHy`uVBq!ia0vp^LLkh+1|-AI^@Rf|wj^(N7a$D;Kb?2i11Zh|kH}&m z?E%JaC$sH9f@KAc=|CE+pF!Co&kM-8=jq}YVsZNIxHb+xAc;kABk!l=4Z>{|BYLz`Ugtljbs}^5_O$Pl!~{Pw8Dz%Z{(j)-49y(z1F8-e4bLC5TRvO!Slj-~_8cm@jn6kOD1X5A zLHvoP(zFAj8)UgQy?s6BbMq3`4^h{@@)Vq_)Xroxv2V&*e|l35OGiiPWs~Qjp?kM} z7u-0pn>~k2jZ##=`ux6We`(MJ7<+2gKxqq?r XoC!~Q+VSij(5DQZu6{1-oD!MFMGaVsZNIg@t)cjsnL%{?B<{GP#LM2oz6?F%Yw$m14BwyAo5Uv9;-^Us(nLoE_s=Cz#%dWyl*)z4*}Q$iB}gZg^s literal 0 HcmV?d00001 diff --git a/src/main/res/drawable-xhdpi/ic_image_18dp.png b/src/main/res/drawable-xhdpi/ic_image_18dp.png new file mode 100644 index 0000000000000000000000000000000000000000..fa93aa3c9adcf5cfd197d16becf824f8ed305329 GIT binary patch literal 529 zcmeAS@N?(olHy`uVBq!ia0vp^Dj>|k1|%Oc%$NbB*pj^6U4S$Y{B+)352QE?JR*yM zvQ&rUPlPeufHm>3#+V#yOrYjv*0;-%j7?#q22J`o6d9U4O*QPWuJd z#FVl$HEu-uusGfbx~H;Xs;iaYm06Knb(S1b=(ee~Kb^-1l_Z|zRT3RZfr=Wco+k^JOjxJKC3xf)&f z*z@@v_NAV8eWIA1DABgJ@Y56RlB~70KH|6Eew(>!UdM@}@!iKyPVNsl93`DTF zG^mqdMYiyLo1HCjo^n5AUNrm96O*mhS)FxU!kAaSWOwXy^T_@`|3dt}JA}43&HH*H z|D0FHlkVUb)qbnaA6Pzb-bsez8go@nN*TFL<$O8KYw7dA#U+jhm((z(9$Ro%R%6d} QV5Bj4y85}Sb4q9e0JM+UGXMYp literal 0 HcmV?d00001 diff --git a/src/main/res/drawable-xhdpi/ic_video_18dp.png b/src/main/res/drawable-xhdpi/ic_video_18dp.png new file mode 100644 index 0000000000000000000000000000000000000000..557c6a7cd266204df714a76863993aef7e0bbc50 GIT binary patch literal 363 zcmeAS@N?(olHy`uVBq!ia0vp^Dj>|k1|%Oc%$NbB*pj^6U4S$Y{B+)352QE?JR*yM zvQ&rUPlPeufHm>3$&Ro2QFoNW|f{*9`NT4J3|!{I20VrHIGxqGGXC z+G*Db61GZ(6Z#})TRQt4Gz^%^SP?6wX*lu2gQ(1}e~+J^_JdK5Ct_M^Wcfw;E2Xh~ zhd+33>iNjv89VR!d*90?|7I2Ey`A%W*WA>9%Q9z`Z8TtT*d%kj@csGKt8RU(jsN*s z(&Li1&KckJc5DY4pJWMsxgBfB5Y~Tfa?>;ePe;WWDGHvm1$Y>qXga9eclxeoe}(Or zu!3hg!}R5h6Kxo8v@V$Rchk1qe#I3_(luD7{t3`un5Hl(Ij3dGZtL@+s+n!G8aL@! wd%O}@IiLI1@x}U~BCgWEpX#38^e^_l)9o<1z~|u)fg!-)>FVdQ&MBb@08@XA!2kdN literal 0 HcmV?d00001 diff --git a/src/main/res/drawable-xxhdpi/ic_image_18dp.png b/src/main/res/drawable-xxhdpi/ic_image_18dp.png new file mode 100644 index 0000000000000000000000000000000000000000..b3e3c2ffffdd378e034781a5a6707e15b83cb851 GIT binary patch literal 742 zcmVZoC~q1 zQc52Hq?8*(bZ1t@dPKCDvG$V?;tB@d4|coV+W=nAnpj9g7b}&@5;O0NsQn>?IOloZ z{@{n5PA3Gg0!?|-B1S~Fq?8-2R;$O%X94Ud)iU!{&-30N^_@~`PgmY_h&_{1-aR}B zg5U#w|I__591-33egAP)CowOZ{))?(Ln`*{XCByZlv73&ev`K(#dLEgL_ z8}mBb09G8wnU>cdnE5S$#{UwlGjo0FRyip8v1R&V%>2FR;YN`*W7--t*|xod*|`fSL&7*%G%!q-HE$m2Uv(7Hu*>7eFIz#{e+L8(0hmgTXM! YFE>Now_AA$xc~qF07*qoM6N<$f`I5q!~g&Q literal 0 HcmV?d00001 diff --git a/src/main/res/drawable-xxhdpi/ic_video_18dp.png b/src/main/res/drawable-xxhdpi/ic_video_18dp.png new file mode 100644 index 0000000000000000000000000000000000000000..cf610376dbae09b06a332cd5130fda95350912f9 GIT binary patch literal 436 zcmeAS@N?(olHy`uVBq!ia0vp^W+2SL1|)l2v+e>Zwj^(N7a$D;Kb?2i11Zh|kH}&m z?E%JaC$sH9f@KAc=|CE+pTY0#i8BlgjP{-`jv*QM-rg|uJ8U4)_V9ezvK`Uu4|L3U zamY7fqJ?8IgS>)|?JRr(kVLxZomD$?%VqN=d&6Nk4Yr= zY~=Rtkqrx7+9L4XspD%9f6=+pb33o^T6cE;fi2fs4WIEINYlKsK5FTex!0;BD(Ao1 zm3P(T?DdVsFQrY6#ke~$9!T4NzuV}T&GgjD$Gac5dTNGG3|Rb|UxR^v@n=JM`|T@N ztVt*ixOw2&qD>903nsO%1fo?#^0mj=E*%EFNTPgg&ebxsLQ0JfvLoB#j- literal 0 HcmV?d00001 diff --git a/src/main/res/drawable-xxxhdpi/ic_image_18dp.png b/src/main/res/drawable-xxxhdpi/ic_image_18dp.png new file mode 100644 index 0000000000000000000000000000000000000000..07f0dfdf5f0be98d38e9d14717113213bd460cf0 GIT binary patch literal 936 zcmeAS@N?(olHy`uVBq!ia0vp^9w5xY1|&n@ZgvM!Y)RhkE=uJQvb0|T?Mr;B4q#jUrq_xlG2iX7j6+-ytceCEydO&^ah zs%PT%3vu2ud6EEAVuHdN|Cm|T&bO9yXEL?9F7BPpY|Wk3v_Q~J!a%3ZgX_t~ec!Am z%-~x3eOv8!+f~nv@0GvX{rWg(R-*qTkt=wB}^5g##WEghL^-(+S%hk)mSRquZ#4V9(BwAwp z%0$fIREpM%swwuLOg7+uEAGg3V8)bB zwt~KGl?D4bv^+FVGbpq~gn8+F)C0>P~pDSFadX>)$YOaWS!NhhDgEV}1K; z-+bNar@w}1eHA(IobTH?PtEY#qAeHubGSY+qMukO!4ApGar-M#4omAZR zd!nynaL}o*&sY_Y$O)!i*jJt-d{N?D+}o&}@5<3l)0Rx}UaQBUXvbJl*4e-%nA##R z)sJuU&HfyYDF5Y`gWqy3nUdd`@$b$iCIhvW8}FV4ME}*uaAe;Yq4Rjxx^#gTKX;nn zo}aiOI_Jw`1y)mrl;WLt)+O%Es@fbY!M6LLn?0A0fZ*NNli(TYf+QatA`(^w6W~ElvOasv?7t0sh9n_e-a>4t+acd#P=8Yw9}sD06R|Jlh7r(j5#pR$PC5Snasuk_LvwnqqN1{X<5WOs} aV_rW)>CC$x)oH-I#Ng@b=d#Wzp$Pyuv5c_* literal 0 HcmV?d00001 diff --git a/src/main/res/drawable-xxxhdpi/ic_video_18dp.png b/src/main/res/drawable-xxxhdpi/ic_video_18dp.png new file mode 100644 index 0000000000000000000000000000000000000000..5b3a9e8e4155dae4b034415f949954d2aa30d5ce GIT binary patch literal 460 zcmeAS@N?(olHy`uVBq!ia0vp^9w5xY1|&n@ZgvM!Y)RhkE=uJQvb0|R5Yr;B4q#jUru4E+u}2(&)DZ@B0q=Ymh6JJ^M` zcWiTvba|YuP{_aT5trWv-btdfLf)VKg7SA7Gn)E--n6yp z`LQWM_P&>=KfT6MVmDuY8{3Cl6|I+}-}yQO&gy2iW{PTJwq}g#Vzy?8YGyvzcV2wK$Kg;V0WsEGRCzdg@oSv}ee0<%u zf5maq&y^zQ@2|<>eX#Q**UDRSpRRblD_HaCfn588)@OfSG~juB?eJ7sFaQJO;xA^q XLsPu9%y()7!<)g=)z4*}Q$iB}u - + + + + + + Date: Mon, 3 Apr 2017 21:50:42 +0200 Subject: [PATCH 007/206] fix codacy --- src/main/java/com/owncloud/android/utils/BitmapUtils.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/main/java/com/owncloud/android/utils/BitmapUtils.java b/src/main/java/com/owncloud/android/utils/BitmapUtils.java index 1682a2c96aee..b88a1b7d61d2 100644 --- a/src/main/java/com/owncloud/android/utils/BitmapUtils.java +++ b/src/main/java/com/owncloud/android/utils/BitmapUtils.java @@ -120,8 +120,7 @@ public static Bitmap scaleBitmap(Bitmap bitmap, float px, int width, int height, float scale = px / max; int w = Math.round(scale * width); int h = Math.round(scale * height); - bitmap = Bitmap.createScaledBitmap(bitmap, w, h, true); - return bitmap; + return Bitmap.createScaledBitmap(bitmap, w, h, true); } /** From 40430c9f1b972218023a0d397a8b4bdf258aa451 Mon Sep 17 00:00:00 2001 From: AndyScherzinger Date: Tue, 4 Apr 2017 17:34:39 +0200 Subject: [PATCH 008/206] add folder type to persistent entity --- .../android/datamodel/MediaFolder.java | 9 ++++----- .../android/datamodel/SyncedFolder.java | 17 +++++++++++++++-- .../datamodel/SyncedFolderDisplayItem.java | 18 ++++-------------- .../datamodel/SyncedFolderProvider.java | 12 ++++++------ .../com/owncloud/android/db/ProviderMeta.java | 1 + .../android/providers/FileContentProvider.java | 15 +++++++++++++++ .../ui/activity/FolderSyncActivity.java | 3 ++- 7 files changed, 47 insertions(+), 28 deletions(-) diff --git a/src/main/java/com/owncloud/android/datamodel/MediaFolder.java b/src/main/java/com/owncloud/android/datamodel/MediaFolder.java index 194ed0624bbb..d7b3ef194294 100644 --- a/src/main/java/com/owncloud/android/datamodel/MediaFolder.java +++ b/src/main/java/com/owncloud/android/datamodel/MediaFolder.java @@ -27,11 +27,10 @@ * Business object representing a media folder with all information that are gathered via media queries. */ public class MediaFolder { - public static final int AUDIO = 3; - public static final int VIDEO = 1; - public static final int IMAGE = 2; - public static final int IMAGE_VIDEO = 3; - public static final int CUSTOM = 4; + public static final Integer IMAGE = 0; + public static final Integer VIDEO = 1; + public static final Integer IMAGE_VIDEO = 2; + public static final Integer CUSTOM = 3; /** name of the folder. */ public String folderName; diff --git a/src/main/java/com/owncloud/android/datamodel/SyncedFolder.java b/src/main/java/com/owncloud/android/datamodel/SyncedFolder.java index a491d993a612..7cac28af5541 100644 --- a/src/main/java/com/owncloud/android/datamodel/SyncedFolder.java +++ b/src/main/java/com/owncloud/android/datamodel/SyncedFolder.java @@ -38,6 +38,7 @@ public class SyncedFolder implements Serializable { private String account; private Integer uploadAction; private boolean enabled; + private Integer type; /** * constructor for already persisted entity. @@ -51,9 +52,10 @@ public class SyncedFolder implements Serializable { * @param account the account owning the synced folder * @param uploadAction the action to be done after the upload * @param enabled flag if synced folder config is active + * @param type the type of the folder */ public SyncedFolder(long id, String localPath, String remotePath, Boolean wifiOnly, Boolean chargingOnly, - Boolean subfolderByDate, String account, Integer uploadAction, Boolean enabled) { + Boolean subfolderByDate, String account, Integer uploadAction, Boolean enabled, Integer type) { this.id = id; this.localPath = localPath; this.remotePath = remotePath; @@ -63,6 +65,7 @@ public SyncedFolder(long id, String localPath, String remotePath, Boolean wifiOn this.account = account; this.uploadAction = uploadAction; this.enabled = enabled; + this.type = type; } /** @@ -76,9 +79,10 @@ public SyncedFolder(long id, String localPath, String remotePath, Boolean wifiOn * @param account the account owning the synced folder * @param uploadAction the action to be done after the upload * @param enabled flag if synced folder config is active + * @param type the type of the folder */ public SyncedFolder(String localPath, String remotePath, Boolean wifiOnly, Boolean chargingOnly, - Boolean subfolderByDate, String account, Integer uploadAction, Boolean enabled) { + Boolean subfolderByDate, String account, Integer uploadAction, Boolean enabled, Integer type) { this.localPath = localPath; this.remotePath = remotePath; this.wifiOnly = wifiOnly; @@ -87,6 +91,7 @@ public SyncedFolder(String localPath, String remotePath, Boolean wifiOnly, Boole this.account = account; this.uploadAction = uploadAction; this.enabled = enabled; + this.type = type; } public long getId() { @@ -160,4 +165,12 @@ public boolean isEnabled() { public void setEnabled(boolean enabled) { this.enabled = enabled; } + + public int getType() { + return type; + } + + public void setType(int type) { + this.type = type; + } } \ No newline at end of file diff --git a/src/main/java/com/owncloud/android/datamodel/SyncedFolderDisplayItem.java b/src/main/java/com/owncloud/android/datamodel/SyncedFolderDisplayItem.java index d7e45aacfab9..f23f13e5d8fe 100644 --- a/src/main/java/com/owncloud/android/datamodel/SyncedFolderDisplayItem.java +++ b/src/main/java/com/owncloud/android/datamodel/SyncedFolderDisplayItem.java @@ -31,7 +31,6 @@ public class SyncedFolderDisplayItem extends SyncedFolder { private List filePaths; private String folderName; private long numberOfFiles; - private int type; /** * constructor for the display item specialization for a synced folder object. @@ -52,18 +51,17 @@ public class SyncedFolderDisplayItem extends SyncedFolder { */ public SyncedFolderDisplayItem(long id, String localPath, String remotePath, Boolean wifiOnly, Boolean chargingOnly, Boolean subfolderByDate, String account, Integer uploadAction, Boolean enabled, - List filePaths, String folderName, long numberOfFiles, int type) { - super(id, localPath, remotePath, wifiOnly, chargingOnly, subfolderByDate, account, uploadAction, enabled); + List filePaths, String folderName, long numberOfFiles, Integer type) { + super(id, localPath, remotePath, wifiOnly, chargingOnly, subfolderByDate, account, uploadAction, enabled, type); this.filePaths = filePaths; this.folderName = folderName; this.numberOfFiles = numberOfFiles; - this.type = type; } public SyncedFolderDisplayItem(long id, String localPath, String remotePath, Boolean wifiOnly, Boolean chargingOnly, Boolean subfolderByDate, String account, Integer uploadAction, Boolean enabled, - String folderName) { - super(id, localPath, remotePath, wifiOnly, chargingOnly, subfolderByDate, account, uploadAction, enabled); + String folderName, Integer type) { + super(id, localPath, remotePath, wifiOnly, chargingOnly, subfolderByDate, account, uploadAction, enabled, type); this.folderName = folderName; } @@ -90,12 +88,4 @@ public long getNumberOfFiles() { public void setNumberOfFiles(long numberOfFiles) { this.numberOfFiles = numberOfFiles; } - - public int getType() { - return type; - } - - public void setType(int type) { - this.type = type; - } } diff --git a/src/main/java/com/owncloud/android/datamodel/SyncedFolderProvider.java b/src/main/java/com/owncloud/android/datamodel/SyncedFolderProvider.java index ee3b1762c708..2b71a92fff51 100644 --- a/src/main/java/com/owncloud/android/datamodel/SyncedFolderProvider.java +++ b/src/main/java/com/owncloud/android/datamodel/SyncedFolderProvider.java @@ -187,7 +187,6 @@ public SyncedFolder findByLocalPath(String localPath) { } return result; - } /** @@ -211,15 +210,12 @@ public int deleteSyncFoldersForAccount(Account account) { * * @param id for the synced folder. */ - private int deleteSyncFolderWithId(long id) { - int result = mContentResolver.delete( + return mContentResolver.delete( ProviderMeta.ProviderTableMeta.CONTENT_URI_SYNCED_FOLDERS, ProviderMeta.ProviderTableMeta._ID + " = ?", new String[]{String.valueOf(id)} ); - - return result; } @@ -325,9 +321,11 @@ private SyncedFolder createSyncedFolderFromCursor(Cursor cursor) { ProviderMeta.ProviderTableMeta.SYNCED_FOLDER_UPLOAD_ACTION)); Boolean enabled = cursor.getInt(cursor.getColumnIndex( ProviderMeta.ProviderTableMeta.SYNCED_FOLDER_ENABLED)) == 1; + Integer type = cursor.getInt(cursor.getColumnIndex( + ProviderMeta.ProviderTableMeta.SYNCED_FOLDER_TYPE)); syncedFolder = new SyncedFolder(id, localPath, remotePath, wifiOnly, chargingOnly, subfolderByDate, - accountName, uploadAction, enabled); + accountName, uploadAction, enabled, type); } return syncedFolder; } @@ -349,6 +347,8 @@ private ContentValues createContentValuesFromSyncedFolder(SyncedFolder syncedFol cv.put(ProviderMeta.ProviderTableMeta.SYNCED_FOLDER_SUBFOLDER_BY_DATE, syncedFolder.getSubfolderByDate()); cv.put(ProviderMeta.ProviderTableMeta.SYNCED_FOLDER_ACCOUNT, syncedFolder.getAccount()); cv.put(ProviderMeta.ProviderTableMeta.SYNCED_FOLDER_UPLOAD_ACTION, syncedFolder.getUploadAction()); + cv.put(ProviderMeta.ProviderTableMeta.SYNCED_FOLDER_TYPE, syncedFolder.getType()); + return cv; } diff --git a/src/main/java/com/owncloud/android/db/ProviderMeta.java b/src/main/java/com/owncloud/android/db/ProviderMeta.java index 764be0943aab..cc10fe13867b 100644 --- a/src/main/java/com/owncloud/android/db/ProviderMeta.java +++ b/src/main/java/com/owncloud/android/db/ProviderMeta.java @@ -176,6 +176,7 @@ static public class ProviderTableMeta implements BaseColumns { public static final String SYNCED_FOLDER_WIFI_ONLY = "wifi_only"; public static final String SYNCED_FOLDER_CHARGING_ONLY = "charging_only"; public static final String SYNCED_FOLDER_ENABLED = "enabled"; + public static final String SYNCED_FOLDER_TYPE = "type"; public static final String SYNCED_FOLDER_SUBFOLDER_BY_DATE = "subfolder_by_date"; public static final String SYNCED_FOLDER_ACCOUNT = "account"; public static final String SYNCED_FOLDER_UPLOAD_ACTION = "upload_option"; diff --git a/src/main/java/com/owncloud/android/providers/FileContentProvider.java b/src/main/java/com/owncloud/android/providers/FileContentProvider.java index 2a6b9d85e611..4630fb679eef 100644 --- a/src/main/java/com/owncloud/android/providers/FileContentProvider.java +++ b/src/main/java/com/owncloud/android/providers/FileContentProvider.java @@ -937,7 +937,22 @@ public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { } finally { db.endTransaction(); } + } + + if (oldVersion < 18 && newVersion >= 18) { + Log_OC.i(SQL, "Entering in the #18 ADD in onUpgrade"); + db.beginTransaction(); + try { + // add type column default being IMAGE(0) + db.execSQL(ALTER_TABLE + ProviderTableMeta.SYNCED_FOLDERS_TABLE_NAME + + ADD_COLUMN + ProviderTableMeta.SYNCED_FOLDER_TYPE + + " INTEGER " + " DEFAULT 0"); + upgraded = true; + db.setTransactionSuccessful(); + } finally { + db.endTransaction(); + } } if (!upgraded) { diff --git a/src/main/java/com/owncloud/android/ui/activity/FolderSyncActivity.java b/src/main/java/com/owncloud/android/ui/activity/FolderSyncActivity.java index 720733b977ec..bb9faeab85b3 100644 --- a/src/main/java/com/owncloud/android/ui/activity/FolderSyncActivity.java +++ b/src/main/java/com/owncloud/android/ui/activity/FolderSyncActivity.java @@ -294,7 +294,8 @@ private SyncedFolderDisplayItem createSyncedFolderWithoutMediaFolder(@NonNull Sy syncedFolder.getAccount(), syncedFolder.getUploadAction(), syncedFolder.isEnabled(), - new File(syncedFolder.getLocalPath()).getName()); + new File(syncedFolder.getLocalPath()).getName(), + syncedFolder.getType()); } /** From f9a84c4e1faaf42a7137d394b18122f8a3c01dd5 Mon Sep 17 00:00:00 2001 From: AndyScherzinger Date: Tue, 4 Apr 2017 18:29:35 +0200 Subject: [PATCH 009/206] create folder icon --- src/main/res/drawable-hdpi/ic_create_folder.png | Bin 0 -> 469 bytes src/main/res/drawable-mdpi/ic_create_folder.png | Bin 0 -> 397 bytes src/main/res/drawable-xhdpi/ic_create_folder.png | Bin 0 -> 573 bytes src/main/res/drawable-xxhdpi/ic_create_folder.png | Bin 0 -> 666 bytes .../res/drawable-xxxhdpi/ic_create_folder.png | Bin 0 -> 780 bytes 5 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 src/main/res/drawable-hdpi/ic_create_folder.png create mode 100644 src/main/res/drawable-mdpi/ic_create_folder.png create mode 100644 src/main/res/drawable-xhdpi/ic_create_folder.png create mode 100644 src/main/res/drawable-xxhdpi/ic_create_folder.png create mode 100644 src/main/res/drawable-xxxhdpi/ic_create_folder.png diff --git a/src/main/res/drawable-hdpi/ic_create_folder.png b/src/main/res/drawable-hdpi/ic_create_folder.png new file mode 100644 index 0000000000000000000000000000000000000000..838a5e6f55cef6d1d7f12f46f63550884c9ce414 GIT binary patch literal 469 zcmV;`0V@89P)Kem6~srEQvaLPE(0fth)kypMSgCL$djn}KYgwLVcwT|4J? zN;d(Y*4mdkg!1G79!XvRSoVI7G3GdmqC4M0svxvqWh2Rv*7~6~p>+T-^ewfKkcfY& zykU$nA0*FweYgT7IgaCaEaKsWJOBXsL9{{WADvJcbqe*ZwXoJgM4s9pv=*IE8KpP- zihPrNE-nB_3e$Izzu817wM+7*PNAZm({kBa*W>eqTfq5fK2FBnMew*-v2WO4bRL;j4${aUX`^XgWSv zbyJ-}htsDLtt(l3g-HH1=#l5nd&oIA2!i0M37a%cUz^C=v7P(@>;PdVo)P*{00000 LNkvXXu0mjf2@See literal 0 HcmV?d00001 diff --git a/src/main/res/drawable-mdpi/ic_create_folder.png b/src/main/res/drawable-mdpi/ic_create_folder.png new file mode 100644 index 0000000000000000000000000000000000000000..85a5c1b8fa94cf4b54754b3316528ca6cda3e165 GIT binary patch literal 397 zcmeAS@N?(olHy`uVBq!ia0vp^5+KaM1|%Pp+x`GjEX7WqAsj$Z!;#Vf4nJ zh#UrCM$rOaC!nBYiEBhjaDG}zd16s2LwR|*US?i)adKios$PCk`s{Z$QVa}?T%InD zAs(G?r)>0Mb`)saZ|_xfVy{cwDL)U<)hsR1>q_3HtQYBCvSFi0jdE?bs8FBu>~5E% zD&^BVSpKJ_x6k~aoIbs^K6fI+oMPn%iCh1NsoZT5sF8M9AJ=X_lX=F?(rKyNV;Q1V zHS?J*bAxm=%g;Tvo)R|6O|j+pT>lFJw#L#<&4OFiN?*h>JWnet)NqItIT}1`+4**+ zH%r{sb5u+&VNFO&og3}7E9U3So3-Kxj9Luh{VU#kC-51}^kMUyRKhgJ@_Ab69*wS~ zS9{+dmw3;hGIz$!~z$=9q^t$j}0-dQOO3_1o+S3j3^P6b@?P)G`Fnw;zB;qtVEO!a=Q8 zyXKsq07(3~5Yc(7)naiq7L}k;#+YH-_AQoZ{)cj@RH9C&^AJmC5eb^j<#I+gn`JB3 zD;JB!bhq2RU$IVPjJOLDQQb7ni@58~B6a~_y%ItMfb~fPyHKT4N#yf+SM&*2WC-BW zvaB;dA?ykWA_3quoKQ^w0FvQAMS&5tfNYYBmk7ZI@aP9bl;WHpE>lRBK+m$QV?Ss# z8adZ>-Ga@H8dg3kRO(*#M?}LnHx|=bb>bz)N_ynMJeN`~Wbq zu3oQaJkNWdcdYUwq`ZLW5lWk;c{HBnoZIboJ9rW^#!~%$f8t3z91gdMXgocFXn{Q< zx*Jat(JO#`KOPJQnQ7~yp!^7F0of=oAnSE{t6Hrd_<`d%JBxOkS;!LDH4Ni9jzY?h zkQR{5{}Ct@3LM}69ygtl|F?kvW6T3^GrnNMZ~$D%ZbGBcXny$u8vkdcI32B100000 LNkvXXu0mjf#47Yz literal 0 HcmV?d00001 diff --git a/src/main/res/drawable-xxhdpi/ic_create_folder.png b/src/main/res/drawable-xxhdpi/ic_create_folder.png new file mode 100644 index 0000000000000000000000000000000000000000..d2e2b8c202cd12d115a43eac0f725f55bf713881 GIT binary patch literal 666 zcmeAS@N?(olHy`uVBq!ia0vp^9w5xY1|&n@ZgvM!EX7WqAsj$Z!;#Vf4nJ z*m;65qp8CBn?OOy64!{5;QX|b^2DN4hVt@qz0ADq;^f4FRK5J7^x5xhq!<{O#64Xc zLn`LHy}38^- z@$sqg=$Lu5Q%U{Q=|c%ObMGzL`Z)Q^pJS8f^#2PDU(V^^0t6-8EBTgWT1DthzrN}h z*8$JPvpZwXrET7F()6}i2)kJKQR~p!=C;QS4XfgO)Fy9vS$aE!TSdwL_MHv+6SEro z97FXVS)AK-<9EPZ7A6M4ncozH(iH99}_*^N_|eKIRaM9 z%q%H!;s8khk23yWQf&`^t}_>L-MBBe#zN*_+}g18-Otute|_9hpvLZrLmX?+Ri)FL z-hZF@`~Bu8bBiBaoa2w3-ai9was3k4CzX>_?)Pl}JL~*&{ngEjFTU^;ah>0@^Wnnu zPg(*Ej19NSgMV{0DeQWGXYOmWy$}AkK^zTp$NhJXzb*DYHoAH0{H5a#>KPB6D#Y!Q zkiEyuJhh`E?08n=w%c#}AIluKUlsNH)(Wu`mgT$;m>Z57@;zk^JsbMbP0l+XkK)f*z- literal 0 HcmV?d00001 diff --git a/src/main/res/drawable-xxxhdpi/ic_create_folder.png b/src/main/res/drawable-xxxhdpi/ic_create_folder.png new file mode 100644 index 0000000000000000000000000000000000000000..8d16949c6da26de34e803923d7e9400f8a7e04ea GIT binary patch literal 780 zcmeAS@N?(olHy`uVBq!ia0vp^2_VeD1|%QND7OGoEX7WqAsj$Z!;#Vf4nJ z1oMC}qwm#U0zg5@64!{5;QX|b^2DN4hVt@qz0ADq;^f4FRK5J7^x5xhq!<{OIz3$+ zLn`LHy|cGVI8fr)$LDuD#TIdB>qTiqRPk%>VPLhk))o|*-C~fv$~Nky%GTD_h{&!E z@r2Fc3Hi!RKUx=PI?buf72`g8k!RNDl(c#AGmX#ed9vS9r~NFG5)@n#6^oYOVY|II z?mc6JH$$I_^ociqfg*}mKi}Av#Lb@nPgJGmGvk4~-#^*#YtBD8MYr0pWy9789XV!; zDP?o!>wWoMd;9+Vde5plqmBL!{GIim>Yu)=UVQO|TydMoqUZ~12Al70bHDL|!Qr=Z zP7T9?yMj`J3>o||*G&xXXC0>m(#?A&RyQgFu!|Ig+%n3&wuvpZg2UV!JQP$V8p6`L2Ozk@4Bb( zySnMR`tQq+u3BZsxFh~?-SIPT(t2OlU;e_de);8>Z~vEWep&KM?!c+<(tPddn{<9J zXSmgb;*2#f_qWYE`?;q4YWLm0hf|CyMWW^VkE@^Bv}fC^zpGYt{k!^k)hxLOb9P_< zIqQ-(!$pSF$ahzE&X4>u|Lhu{Xn~nb6Eqnx3DdtCi`Gr9x3s?HX?C)49jis?=QkN| zx3EscrdX39Qs+Q9!{#Z?TkcH1ap)3*Vhh)S?ybF-ZO^(!2{o{ pmc44|XP7th<^>u>x4O;qP6JaegQu&X%Q~loCIAEiO*sGn literal 0 HcmV?d00001 From d7152797b4ba29627855d37cb3294b14ceb7cdca Mon Sep 17 00:00:00 2001 From: AndyScherzinger Date: Tue, 4 Apr 2017 18:38:27 +0200 Subject: [PATCH 010/206] add item to the top (WiP) --- src/main/res/layout/folder_sync_layout.xml | 47 ++++++++++++++++------ 1 file changed, 34 insertions(+), 13 deletions(-) diff --git a/src/main/res/layout/folder_sync_layout.xml b/src/main/res/layout/folder_sync_layout.xml index 94fb76abaa43..16458ee264c9 100644 --- a/src/main/res/layout/folder_sync_layout.xml +++ b/src/main/res/layout/folder_sync_layout.xml @@ -36,6 +36,27 @@ layout="@layout/toolbar_standard"/> + + + + + + + - - + android:visibility="gone"/> + + Date: Thu, 20 Apr 2017 23:10:18 +0200 Subject: [PATCH 011/206] fixes after rebase for DB upgrade routine --- .../android/datamodel/MediaFolder.java | 8 ++--- .../providers/FileContentProvider.java | 32 +++++++++++-------- 2 files changed, 22 insertions(+), 18 deletions(-) diff --git a/src/main/java/com/owncloud/android/datamodel/MediaFolder.java b/src/main/java/com/owncloud/android/datamodel/MediaFolder.java index d7b3ef194294..76ac825dd66d 100644 --- a/src/main/java/com/owncloud/android/datamodel/MediaFolder.java +++ b/src/main/java/com/owncloud/android/datamodel/MediaFolder.java @@ -27,10 +27,10 @@ * Business object representing a media folder with all information that are gathered via media queries. */ public class MediaFolder { - public static final Integer IMAGE = 0; - public static final Integer VIDEO = 1; - public static final Integer IMAGE_VIDEO = 2; - public static final Integer CUSTOM = 3; + public static final Integer CUSTOM = 0; + public static final Integer IMAGE = 1; + public static final Integer VIDEO = 2; + public static final Integer IMAGE_VIDEO = 3; /** name of the folder. */ public String folderName; diff --git a/src/main/java/com/owncloud/android/providers/FileContentProvider.java b/src/main/java/com/owncloud/android/providers/FileContentProvider.java index 4630fb679eef..4c49e9d105cd 100644 --- a/src/main/java/com/owncloud/android/providers/FileContentProvider.java +++ b/src/main/java/com/owncloud/android/providers/FileContentProvider.java @@ -939,14 +939,17 @@ public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { } } + if (!upgraded) { + Log_OC.i(SQL, String.format(Locale.ENGLISH, UPGRADE_VERSION_MSG, oldVersion, newVersion)); + } + if (oldVersion < 18 && newVersion >= 18) { - Log_OC.i(SQL, "Entering in the #18 ADD in onUpgrade"); + Log_OC.i(SQL, "Entering in the #18 Adding external link column to capabilities"); db.beginTransaction(); try { - // add type column default being IMAGE(0) - db.execSQL(ALTER_TABLE + ProviderTableMeta.SYNCED_FOLDERS_TABLE_NAME + - ADD_COLUMN + ProviderTableMeta.SYNCED_FOLDER_TYPE + - " INTEGER " + " DEFAULT 0"); + db.execSQL(ALTER_TABLE + ProviderTableMeta.CAPABILITIES_TABLE_NAME + + ADD_COLUMN + ProviderTableMeta.CAPABILITIES_EXTERNAL_LINKS + + " INTEGER " + " DEFAULT -1"); upgraded = true; db.setTransactionSuccessful(); @@ -959,14 +962,11 @@ public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { Log_OC.i(SQL, String.format(Locale.ENGLISH, UPGRADE_VERSION_MSG, oldVersion, newVersion)); } - if (oldVersion < 18 && newVersion >= 18) { - Log_OC.i(SQL, "Entering in the #18 Adding external link column to capabilities"); + if (oldVersion < 19 && newVersion >= 19) { + Log_OC.i(SQL, "Entering in the #19 Adding external link column to capabilities"); db.beginTransaction(); try { - db.execSQL(ALTER_TABLE + ProviderTableMeta.CAPABILITIES_TABLE_NAME + - ADD_COLUMN + ProviderTableMeta.CAPABILITIES_EXTERNAL_LINKS + - " INTEGER " + " DEFAULT -1"); - + createExternalLinksTable(db); upgraded = true; db.setTransactionSuccessful(); } finally { @@ -978,11 +978,15 @@ public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { Log_OC.i(SQL, String.format(Locale.ENGLISH, UPGRADE_VERSION_MSG, oldVersion, newVersion)); } - if (oldVersion < 19 && newVersion >= 19) { - Log_OC.i(SQL, "Entering in the #19 Adding external link column to capabilities"); + if (oldVersion < 20 && newVersion >= 20) { + Log_OC.i(SQL, "Entering in the #20 ADD in onUpgrade"); db.beginTransaction(); try { - createExternalLinksTable(db); + // add type column default being IMAGE(0) + db.execSQL(ALTER_TABLE + ProviderTableMeta.SYNCED_FOLDERS_TABLE_NAME + + ADD_COLUMN + ProviderTableMeta.SYNCED_FOLDER_TYPE + + " INTEGER " + " DEFAULT 0"); + upgraded = true; db.setTransactionSuccessful(); } finally { From 1a181974d34b80246ab6636d87670778b20a36c2 Mon Sep 17 00:00:00 2001 From: AndyScherzinger Date: Tue, 25 Apr 2017 10:40:01 +0200 Subject: [PATCH 012/206] custom folder icon --- drawable_resources/ic_folder_star.svg | 1 + .../res/drawable-hdpi/ic_folder_star_18dp.png | Bin 0 -> 556 bytes .../res/drawable-mdpi/ic_folder_star_18dp.png | Bin 0 -> 413 bytes .../res/drawable-xhdpi/ic_folder_star_18dp.png | Bin 0 -> 636 bytes .../res/drawable-xxhdpi/ic_folder_star_18dp.png | Bin 0 -> 955 bytes .../res/drawable-xxxhdpi/ic_folder_star_18dp.png | Bin 0 -> 1183 bytes 6 files changed, 1 insertion(+) create mode 100644 drawable_resources/ic_folder_star.svg create mode 100644 src/main/res/drawable-hdpi/ic_folder_star_18dp.png create mode 100644 src/main/res/drawable-mdpi/ic_folder_star_18dp.png create mode 100644 src/main/res/drawable-xhdpi/ic_folder_star_18dp.png create mode 100644 src/main/res/drawable-xxhdpi/ic_folder_star_18dp.png create mode 100644 src/main/res/drawable-xxxhdpi/ic_folder_star_18dp.png diff --git a/drawable_resources/ic_folder_star.svg b/drawable_resources/ic_folder_star.svg new file mode 100644 index 000000000000..1f6ffa62b43c --- /dev/null +++ b/drawable_resources/ic_folder_star.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/main/res/drawable-hdpi/ic_folder_star_18dp.png b/src/main/res/drawable-hdpi/ic_folder_star_18dp.png new file mode 100644 index 0000000000000000000000000000000000000000..7161e1d2b934fab2df537864259f36452a7860ac GIT binary patch literal 556 zcmV+{0@MA8P)db3l0_&QcWOoKZ$m#Aq@3{}>oq-+wZ_@C=IX5Ti zkfd<4;VjFZcDvmNrC~{u%$wOQASjq004F+~&Ppj*9LLMR@oi)Vz-cr4P&5>V;ULSh zRqy@UUohv~LrDjxUC|hQGP5Jz``2u-vMRJ!()o=;X_%Sa20j+}`zwQmVYmn^ZD+O= zOwww*-F{T7)vf^FtA_Q=>{Ry=N2|I8;!>9IF4@s7phnzKqzT3Ns{?dBZ{IFI5{pj z^$#NH-RNDO=WmNLlYX-eeT|~%RkPXLBk5ug1n+yj-eog;ENN_L+GfxD{r;&S2;N9K zV`jcyufLSE&&4F~s8Z-3bf*4kw7T?!O$CTic;}WS4@!Ydb2Yt4jm1D3x$Z%AJ=^(d)hk=TZ4!6F;CDF^@&hczAx&1;V=KKHe zcizt{et3TxgVkI=&!@|64klh zuiOGn3^xaRGJc4VR5>|)OZe)mzXC;k8LIZ)+n~{9{$PPo#=BP*o|C%Ym#@CM%6k|4 zt8T0Hk2>RSz5Odoi+-EVx`D|1#^j&c1Yw#->}%=1%w&BdRkZR;!DCQ!Ov x#P)CD6N_^f&bWyt>p8j2dww_1>ih95w)ac3|L~t!aS0fJ44$rjF6*2UngCxEqvHSo literal 0 HcmV?d00001 diff --git a/src/main/res/drawable-xhdpi/ic_folder_star_18dp.png b/src/main/res/drawable-xhdpi/ic_folder_star_18dp.png new file mode 100644 index 0000000000000000000000000000000000000000..89b75e492d7d537153d2bcd2897218067dfda37d GIT binary patch literal 636 zcmV-?0)zdDP)+bkkFj^no`f$V9c+wHys5CC{w>uJbmyC>Mf0v8S9RgI<0&X%66ZxoG`=J~OJ8$vT0OIzb}x zWbHsAa^|}3j)r0`FcC0eQNMdVwSWzUl&`LQS_ilSWryQS@U-c#GQKa<=v{)R|yI*xM~ z1VOt_d=La}$8io9!K@5asFUaUvG4m^HwF2=zm@0tF@VlZai(ghMe;}~wK31Bl-eM9 zgc=isu`VO|5WvL(_eJ4fDwA!08pm;38vTC%dbBClPwONx`E&gW0G{VPvTa+D+*V5M zWmy&{Nir$?-DOEkjvp^XBqzBCV7lwhMP!?#1>obnjZqX`bC`TXLz444s7>+{fbVsZ zn5?1BivE9rNd7ViSWDG2XMjZH{api9&zz|t*L8HVWixt{NltOO)!;1&e;6X38R451@1U-o4Al}UGd+b4U zZkc7)tcPMXUV5!+ zvhVkPzu$-7@ZQb>1qB5K1qB6t2k*8}sZ?Gh`H`fTNR~3>Uhq8cl^_Tf`bNw-_lBg? z0CqiK{+`L*Ay%zcABp4mGJuCWRHG+yru~Z~Nse?-3IImpIG(9it0Q@e-SMbNI@pCe zBXJzhIOjg^L^4U&CH+vZ*MCYa)&wdMA8lO<`tAnCOvNltCH zwynk1IO3dpVJC_qNs`fi6*~do->jQS?(avj$Qbi6$*($@v>(K#Yqi=HNuTW~F-e~S zKp2J>0L<R##U~Mo~0Oa{t;~#kR-nE@3Pi@319;EcA+xEtu!2XaGz)=RQdj z?3{bT^SmDcJe##xu1I=1O0Nd zbAy9}F#vC?h4P-*MQd%{81ox|!*_|<3*cC*)w(b+Fz_6Ji&+ZgJ+Wf|_N0t`hU5%@ zR?7GdXCBC({Vv?iLsV663$T8sFdWZ=H9Yl8~CYMeN z0in3TL68%r^bw%N6`T4{Id`9MgT znK$pv$Ih<)0ve4*qtR$I8jVJy88vj&0HsvE-ELncqIm$*0E%%WW@fdtwDd*N0*=B6 z>#SC*XI-h5x@))-L`G}=W4ZjHf->56m*#LhGG0rsZ>q|4LI(CKt$&Xg~CEm z|FIY36L`heg7yi#VrNlM%fH3RL{P_xq*oh(ul@U(Q6$UM5ky4KipAnuu~=LqqGzMX znJGccd{-&eRZ4Z4`R*ukW=T+YZ*T9;!4v;+cYyhNB1?kqFE1~@*;fAJ`@>F}89_v} z+)G>BWJS-U=l8BxI_=A~W0C+(}&v$os?*#@q z&OOsK&oJ{D0Fy*C3E&<7`Dp-!tO|07Xn~nqMD%;TUf+o_Nu$wt4d7-_zm#%U*QAv1kB^Uks+9U`kja!%Cpw+Z z9cI2VXklqMg5JQ)SK96N11aT1)H$S-6YX~UK^$^`lnDX=BD%oL>r%?82y&*Fd7X$Z z#8pq~1o=#Ro;TNTkRLN0XCY}71j8^Mk0RzM(kkecX_~+G8|25linEZk3Ti5)ynciH zm}Z=Xq*4$O1#T6kR9-2Sj}jY&vzU}gXS002ovPDHLkV1l Date: Tue, 25 Apr 2017 10:50:07 +0200 Subject: [PATCH 013/206] also use star folder for action item --- .../res/drawable-hdpi/ic_folder_star_24dp.png | Bin 0 -> 636 bytes .../res/drawable-mdpi/ic_folder_star_24dp.png | Bin 0 -> 418 bytes .../res/drawable-xhdpi/ic_folder_star_24dp.png | Bin 0 -> 802 bytes .../res/drawable-xxhdpi/ic_folder_star_24dp.png | Bin 0 -> 1183 bytes .../res/drawable-xxxhdpi/ic_folder_star_24dp.png | Bin 0 -> 1521 bytes src/main/res/layout/folder_sync_layout.xml | 8 ++++++-- 6 files changed, 6 insertions(+), 2 deletions(-) create mode 100644 src/main/res/drawable-hdpi/ic_folder_star_24dp.png create mode 100644 src/main/res/drawable-mdpi/ic_folder_star_24dp.png create mode 100644 src/main/res/drawable-xhdpi/ic_folder_star_24dp.png create mode 100644 src/main/res/drawable-xxhdpi/ic_folder_star_24dp.png create mode 100644 src/main/res/drawable-xxxhdpi/ic_folder_star_24dp.png diff --git a/src/main/res/drawable-hdpi/ic_folder_star_24dp.png b/src/main/res/drawable-hdpi/ic_folder_star_24dp.png new file mode 100644 index 0000000000000000000000000000000000000000..89b75e492d7d537153d2bcd2897218067dfda37d GIT binary patch literal 636 zcmV-?0)zdDP)+bkkFj^no`f$V9c+wHys5CC{w>uJbmyC>Mf0v8S9RgI<0&X%66ZxoG`=J~OJ8$vT0OIzb}x zWbHsAa^|}3j)r0`FcC0eQNMdVwSWzUl&`LQS_ilSWryQS@U-c#GQKa<=v{)R|yI*xM~ z1VOt_d=La}$8io9!K@5asFUaUvG4m^HwF2=zm@0tF@VlZai(ghMe;}~wK31Bl-eM9 zgc=isu`VO|5WvL(_eJ4fDwA!08pm;38vTC%dbBClPwONx`E&gW0G{VPvTa+D+*V5M zWmy&{Nir$?-DOEkjvp^XBqzBCV7lwhMP!?#1>obnjZqX`bC`TXLz444s7>+{fbVsZ zn5?1BivE9rNd7ViSWDG2XMjZH{api9&zz|t*L8(}6TtKSPDj(q#+`jJlpKjv*e$-%i=+cPK%mb$_ze#i@ah&Iud5 zey03`DJjN#X3u_$iJKCVj+j5-yd&cy5mU=@u;u92^n#syB9{^rgJ!)rFx_PD-n~23 zpKtk@cs_oy>&2|CevOO)r=?AdufP79w@uXI#FhTn)0jPq0!3oqKRV#$mC9r?S*0?n zG(c>tNw2(M?&<8Rr-eH-nq*nzE>$OA4p3yDv@B7A@34W4*Q~r*caI(1JoB#iuPI;N z&H2rgvHdo$v9;0WRZF}dELm~D@WUjPleyb&zAZa`z@lr>>XKyzE|acwKHjnamr~#3 zeM|Ue-rv{3z>pU9*LS^#3%A?3oe5^MO&PVOo;px%&vfpT5XYYH`3|xNs_PH_;n6;O z@b0N%+vVF;e)HOKb*xR^sjXdb?$3f-Q$2TOpSOP~Em;=gZ=&KV3JgUCPgg&ebxsLQ E02L#wB>(^b literal 0 HcmV?d00001 diff --git a/src/main/res/drawable-xhdpi/ic_folder_star_24dp.png b/src/main/res/drawable-xhdpi/ic_folder_star_24dp.png new file mode 100644 index 0000000000000000000000000000000000000000..84cad094b252e6540dd75379d8a1223dab362d7d GIT binary patch literal 802 zcmV+-1Ks?IP)l6sGU(5dH2`$!&!h}|3bv?R2?s7EWMC!O-#M>P>`#D*_6);V zGYn&mi1tR#05eyWQca~)lbNfdW}ta^c=+o0LvY+2B?Ftw%gg_UO>n$<9>HNWaf6w^ z5m6mL9l$RDznQu2`+mm)uhnYVnx?%aq8xxMfNKD9%$y^ln*grHYJiz_W`15OmEQG} zU#V1n0$2^lZQFhT;0+P=ynf(Ah=?*mh%G7Q{8=@ml=DJ}Eh5UCmOHH90gahg9LKp~ z7{-E9s@=~;N-5!a-V!qx`n}Pxo4DY*?z&}JW2YrrmNn+O?mB?Ji;u$qh-gmN^~KZj zbX{L0qPeL0aZRJ&2b7bHfp)vSJ&2s73^X#C%-53v+qS1|+nzod3vwD4&HxeBj%Oj$ zG$*8#6(Piq5MoD4Suss>0sxd!Z6c~g)gStDnwhtQ>G27GC1%c^IA`j*{@8Jx1;a32 zd!Dxq;9(T~IEGNm^SsXhsyJFIgvkP^205*$;&Hstyy!FURtAlCH7`9kq2BAfh~g{iG}H!hTR&Zv(|*ao6|#82}qN`dcq)4ghTUzCTke7I%{_ gCM6{$CFNrA7j%N>wqovrHvj+t07*qoM6N<$f@=g~E&u=k literal 0 HcmV?d00001 diff --git a/src/main/res/drawable-xxhdpi/ic_folder_star_24dp.png b/src/main/res/drawable-xxhdpi/ic_folder_star_24dp.png new file mode 100644 index 0000000000000000000000000000000000000000..fbeb81a7ec198fd17c208e6a64d545dc178002ca GIT binary patch literal 1183 zcmV;Q1YrA#P)XCBC({Vv?iLsV663$T8sFdWZ=H9Yl8~CYMeN z0in3TL68%r^bw%N6`T4{Id`9MgT znK$pv$Ih<)0ve4*qtR$I8jVJy88vj&0HsvE-ELncqIm$*0E%%WW@fdtwDd*N0*=B6 z>#SC*XI-h5x@))-L`G}=W4ZjHf->56m*#LhGG0rsZ>q|4LI(CKt$&Xg~CEm z|FIY36L`heg7yi#VrNlM%fH3RL{P_xq*oh(ul@U(Q6$UM5ky4KipAnuu~=LqqGzMX znJGccd{-&eRZ4Z4`R*ukW=T+YZ*T9;!4v;+cYyhNB1?kqFE1~@*;fAJ`@>F}89_v} z+)G>BWJS-U=l8BxI_=A~W0C+(}&v$os?*#@q z&OOsK&oJ{D0Fy*C3E&<7`Dp-!tO|07Xn~nqMD%;TUf+o_Nu$wt4d7-_zm#%U*QAv1kB^Uks+9U`kja!%Cpw+Z z9cI2VXklqMg5JQ)SK96N11aT1)H$S-6YX~UK^$^`lnDX=BD%oL>r%?82y&*Fd7X$Z z#8pq~1o=#Ro;TNTkRLN0XCY}71j8^Mk0RzM(kkecX_~+G8|25linEZk3Ti5)ynciH zm}Z=Xq*4$O1#T6kR9-2Sj}jY&vzU}gXS002ovPDHLkV1lE`yC~3H`q5BA>Se z0Maq`u+52!Z|1X|L$>cdQr3X=E!?`Mum6R!4WE z^Ap~D!%CbuwrQbpm`18ZppnpJjvw(q=(Jd>LSZ{-NA#Ah=<|Rt=yH^*MXJx8nIBHe zolXv+&#`yk?Fm$GrT}Hehu`xOa|!7Q(zRtxF#VQ#>=4f35c1Tt16YLrfk9&D{26T1WUq z={g|hDf{-fekY8m=@GISe_P~Opn z=|bPs&krsRB~4^bq^tHZTnV!;7^v04j6w*iJ0wTiGV4nDRirlkST0pOxWvosE#t$tM}`-z6* zDyn&q0KiSLUk}%2Qz-V5RwiSS0>zU`Q9`E;6<--V=E=GBmsb=t8|#PsOI0El#(ebC z9ma{`io&&9?Mgy);FznvD&;HC4L3LHl^ZVU$S~l%Lq81IYMCHawEoR)D-g z0N0jE&JRa4gFsP0n;vN9Lf9HAgNwA>ZehPjlpf8b#wqxDIf;CA?}rsJt+g0=q<0*; zv&!%FC=rWIpk*cOy>4`n^bF{MjV3fl@g%1+g8S(3-DLIFmc;jah9uurSij9f$3Z|w zxj5uN6LSJ5NOXC`z`hui#MMYFJNooFraPuZ-0<|HLh@DHsv0GHOPFi0LPvgCw*C6Lvs61?MD5->xk2N@gU^- VVPVTEBVSU;fc@d4a0S^f;a`%ms@nhn literal 0 HcmV?d00001 diff --git a/src/main/res/layout/folder_sync_layout.xml b/src/main/res/layout/folder_sync_layout.xml index 16458ee264c9..600987da505f 100644 --- a/src/main/res/layout/folder_sync_layout.xml +++ b/src/main/res/layout/folder_sync_layout.xml @@ -42,10 +42,14 @@ android:gravity="center"> + android:paddingBottom="@dimen/standard_padding" + android:paddingLeft="@dimen/standard_padding" + android:paddingRight="@dimen/standard_half_padding" + android:paddingTop="@dimen/standard_padding" + android:src="@drawable/ic_folder_star_24dp"/> Date: Thu, 27 Apr 2017 10:03:01 +0200 Subject: [PATCH 014/206] fixed layout not displaying app bar --- src/main/res/layout/folder_sync_layout.xml | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/main/res/layout/folder_sync_layout.xml b/src/main/res/layout/folder_sync_layout.xml index 600987da505f..652bd505931c 100644 --- a/src/main/res/layout/folder_sync_layout.xml +++ b/src/main/res/layout/folder_sync_layout.xml @@ -36,10 +36,12 @@ layout="@layout/toolbar_standard"/> + android:gravity="center" + android:layout_below="@+id/appbar"> From f2c6ff3e6753357bf87f1248abc53d67ad233214 Mon Sep 17 00:00:00 2001 From: Mario Danic Date: Thu, 4 May 2017 00:52:16 +0200 Subject: [PATCH 015/206] Add custom folder icon Signed-off-by: Mario Danic --- .../java/com/owncloud/android/datamodel/MediaFolder.java | 2 +- .../owncloud/android/ui/activity/FolderSyncActivity.java | 2 +- .../com/owncloud/android/ui/adapter/FolderSyncAdapter.java | 6 +++++- src/main/res/values/strings.xml | 3 +++ 4 files changed, 10 insertions(+), 3 deletions(-) diff --git a/src/main/java/com/owncloud/android/datamodel/MediaFolder.java b/src/main/java/com/owncloud/android/datamodel/MediaFolder.java index 76ac825dd66d..71e3fcc12982 100644 --- a/src/main/java/com/owncloud/android/datamodel/MediaFolder.java +++ b/src/main/java/com/owncloud/android/datamodel/MediaFolder.java @@ -30,7 +30,7 @@ public class MediaFolder { public static final Integer CUSTOM = 0; public static final Integer IMAGE = 1; public static final Integer VIDEO = 2; - public static final Integer IMAGE_VIDEO = 3; + public static final Integer LEGACY = 3; /** name of the folder. */ public String folderName; diff --git a/src/main/java/com/owncloud/android/ui/activity/FolderSyncActivity.java b/src/main/java/com/owncloud/android/ui/activity/FolderSyncActivity.java index bb9faeab85b3..9b733bf5ee36 100644 --- a/src/main/java/com/owncloud/android/ui/activity/FolderSyncActivity.java +++ b/src/main/java/com/owncloud/android/ui/activity/FolderSyncActivity.java @@ -181,7 +181,7 @@ public void run() { final List mediaFolders = MediaProvider.getImageFolders(getContentResolver(), perFolderMediaItemLimit, FolderSyncActivity.this); Log_OC.w(TAG, "Picture Folders: " + mediaFolders.size()); - mediaFolders.addAll(MediaProvider.getVideoFolders(getContentResolver(),perFolderMediaItemLimit)); + mediaFolders.addAll(MediaProvider.getVideoFolders(getContentResolver(), perFolderMediaItemLimit)); Log_OC.w(TAG, "Picture+Video Folders: " + mediaFolders.size()); //TODO properly merge image and video lists to remove duplicates diff --git a/src/main/java/com/owncloud/android/ui/adapter/FolderSyncAdapter.java b/src/main/java/com/owncloud/android/ui/adapter/FolderSyncAdapter.java index 0723fa437b30..0ce558639400 100644 --- a/src/main/java/com/owncloud/android/ui/adapter/FolderSyncAdapter.java +++ b/src/main/java/com/owncloud/android/ui/adapter/FolderSyncAdapter.java @@ -91,11 +91,15 @@ public int getItemCount(int section) { @Override public void onBindHeaderViewHolder(final MainViewHolder holder, final int section) { holder.title.setText(mSyncFolderItems.get(section).getFolderName()); + if (MediaFolder.VIDEO == mSyncFolderItems.get(section).getType()) { holder.type.setImageResource(R.drawable.ic_video_18dp); - } else { + } else if (MediaFolder.IMAGE == mSyncFolderItems.get(section).getType()) { holder.type.setImageResource(R.drawable.ic_image_18dp); + } else { + holder.type.setImageResource(R.drawable.ic_folder_star_18dp); } + holder.syncStatusButton.setVisibility(View.VISIBLE); holder.syncStatusButton.setTag(section); holder.syncStatusButton.setOnClickListener(new View.OnClickListener() { diff --git a/src/main/res/values/strings.xml b/src/main/res/values/strings.xml index d77634e709c0..e4c7a6848a63 100644 --- a/src/main/res/values/strings.xml +++ b/src/main/res/values/strings.xml @@ -678,6 +678,9 @@ Privacy File not found! + + Setup a custom folder + Configure folders From e106bb19114b4fafebc53f208a1a630fc1074066 Mon Sep 17 00:00:00 2001 From: Mario Danic Date: Thu, 4 May 2017 16:54:52 +0200 Subject: [PATCH 016/206] Custom folder RV item Signed-off-by: Mario Danic --- .../providers/FileContentProvider.java | 3 +- .../android/ui/adapter/FolderSyncAdapter.java | 57 ++++++-- .../res/layout/folder_sync_item_header.xml | 129 ++++++++++-------- src/main/res/layout/folder_sync_layout.xml | 27 ---- 4 files changed, 122 insertions(+), 94 deletions(-) diff --git a/src/main/java/com/owncloud/android/providers/FileContentProvider.java b/src/main/java/com/owncloud/android/providers/FileContentProvider.java index 4c49e9d105cd..ad48e0b71550 100644 --- a/src/main/java/com/owncloud/android/providers/FileContentProvider.java +++ b/src/main/java/com/owncloud/android/providers/FileContentProvider.java @@ -985,7 +985,7 @@ public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { // add type column default being IMAGE(0) db.execSQL(ALTER_TABLE + ProviderTableMeta.SYNCED_FOLDERS_TABLE_NAME + ADD_COLUMN + ProviderTableMeta.SYNCED_FOLDER_TYPE + - " INTEGER " + " DEFAULT 0"); + " INTEGER " + " DEFAULT 3"); upgraded = true; db.setTransactionSuccessful(); @@ -1178,6 +1178,7 @@ private void createSyncedFoldersTable(SQLiteDatabase db) { + ProviderTableMeta.SYNCED_FOLDER_ENABLED + " INTEGER, " // enabled + ProviderTableMeta.SYNCED_FOLDER_SUBFOLDER_BY_DATE + " INTEGER, " // subfolder by date + ProviderTableMeta.SYNCED_FOLDER_ACCOUNT + " TEXT, " // account + + ProviderTableMeta.SYNCED_FOLDER_TYPE + " INTEGER, " // type + ProviderTableMeta.SYNCED_FOLDER_UPLOAD_ACTION + " INTEGER );" // upload action ); } diff --git a/src/main/java/com/owncloud/android/ui/adapter/FolderSyncAdapter.java b/src/main/java/com/owncloud/android/ui/adapter/FolderSyncAdapter.java index 0ce558639400..5c883d8c3206 100644 --- a/src/main/java/com/owncloud/android/ui/adapter/FolderSyncAdapter.java +++ b/src/main/java/com/owncloud/android/ui/adapter/FolderSyncAdapter.java @@ -29,6 +29,7 @@ import android.widget.ImageButton; import android.widget.ImageView; import android.widget.LinearLayout; +import android.widget.RelativeLayout; import android.widget.TextView; import com.afollestad.sectionedrecyclerview.SectionedRecyclerViewAdapter; @@ -61,6 +62,8 @@ public FolderSyncAdapter(Context context, int gridWidth, ClickListener listener, mListener = listener; mSyncFolderItems = new ArrayList<>(); mLight = light; + + shouldShowHeadersForEmptySections(true); } public void setSyncFolderItems(List syncFolderItems) { @@ -81,6 +84,10 @@ public int getSectionCount() { @Override public int getItemCount(int section) { + if (section == 0) { + return 0; + } + if (mSyncFolderItems.get(section).getFilePaths() != null) { return mSyncFolderItems.get(section).getFilePaths().size(); } else { @@ -90,17 +97,33 @@ public int getItemCount(int section) { @Override public void onBindHeaderViewHolder(final MainViewHolder holder, final int section) { - holder.title.setText(mSyncFolderItems.get(section).getFolderName()); + if (section != 0) { + holder.mainHeaderContainer.setVisibility(View.VISIBLE); + holder.customFolderHeaderContainer.setVisibility(View.GONE); - if (MediaFolder.VIDEO == mSyncFolderItems.get(section).getType()) { - holder.type.setImageResource(R.drawable.ic_video_18dp); - } else if (MediaFolder.IMAGE == mSyncFolderItems.get(section).getType()) { - holder.type.setImageResource(R.drawable.ic_image_18dp); - } else { - holder.type.setImageResource(R.drawable.ic_folder_star_18dp); - } - - holder.syncStatusButton.setVisibility(View.VISIBLE); + holder.title.setText(mSyncFolderItems.get(section).getFolderName()); + + if (MediaFolder.VIDEO == mSyncFolderItems.get(section).getType()) { + holder.type.setImageResource(R.drawable.ic_video_18dp); + } else if (MediaFolder.IMAGE == mSyncFolderItems.get(section).getType()) { + holder.type.setImageResource(R.drawable.ic_image_18dp); + } else { + holder.type.setImageResource(R.drawable.ic_folder_star_18dp); + } + + holder.syncStatusButton.setVisibility(View.VISIBLE); + holder.syncStatusButton.setTag(section); + holder.syncStatusButton.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + mSyncFolderItems.get(section).setEnabled(!mSyncFolderItems.get(section).isEnabled()); + setSyncButtonActiveIcon(holder.syncStatusButton, mSyncFolderItems.get(section).isEnabled()); + mListener.onSyncStatusToggleClick(section, mSyncFolderItems.get(section)); + } + }); + setSyncButtonActiveIcon(holder.syncStatusButton, mSyncFolderItems.get(section).isEnabled()); + + holder.syncStatusButton.setVisibility(View.VISIBLE); holder.syncStatusButton.setTag(section); holder.syncStatusButton.setOnClickListener(new View.OnClickListener() { @Override @@ -124,12 +147,17 @@ public void onClick(View v) { } }); } + } else { + holder.mainHeaderContainer.setVisibility(View.GONE); + holder.customFolderHeaderContainer.setVisibility(View.VISIBLE); + + } } @Override public void onBindViewHolder(MainViewHolder holder, int section, int relativePosition, int absolutePosition) { - if (mSyncFolderItems.get(section).getFilePaths() != null) { + if (section != 0 && mSyncFolderItems.get(section - 1).getFilePaths() != null) { File file = new File(mSyncFolderItems.get(section).getFilePaths().get(relativePosition)); ThumbnailsCacheManager.MediaThumbnailGenerationTask task = @@ -159,7 +187,7 @@ public void onBindViewHolder(MainViewHolder holder, int section, int relativePos holder.thumbnailDarkener.setVisibility(View.GONE); } - //holder.itemView.setTag(String.format(Locale.getDefault(), "%d:%d:%d", section, relativePos, absolutePos)); + //holder.itemView.setTag(String.format(Locale.getDefault(), "%d:%d:%d", adjustedSection, relativePos, absolutePos)); //holder.itemView.setOnClickListener(this); } else { holder.itemView.setTag(relativePosition % mGridWidth); @@ -192,8 +220,13 @@ static class MainViewHolder extends RecyclerView.ViewHolder { private final TextView counterValue; private final ImageView thumbnailDarkener; + private final RelativeLayout mainHeaderContainer; + private final RelativeLayout customFolderHeaderContainer; + private MainViewHolder(View itemView) { super(itemView); + mainHeaderContainer = (RelativeLayout) itemView.findViewById(R.id.header_container); + customFolderHeaderContainer = (RelativeLayout) itemView.findViewById(R.id.custom_folder); image = (ImageView) itemView.findViewById(R.id.thumbnail); title = (TextView) itemView.findViewById(R.id.title); type = (ImageView) itemView.findViewById(R.id.type); diff --git a/src/main/res/layout/folder_sync_item_header.xml b/src/main/res/layout/folder_sync_item_header.xml index 03a9a882de8f..2bd055162019 100644 --- a/src/main/res/layout/folder_sync_item_header.xml +++ b/src/main/res/layout/folder_sync_item_header.xml @@ -20,74 +20,95 @@ --> + android:layout_height="wrap_content"> - + android:paddingBottom="@dimen/alternate_half_padding" + android:paddingLeft="@dimen/standard_padding" + android:paddingTop="@dimen/alternate_half_padding"> - + android:layout_alignBottom="@+id/buttonBar" + android:layout_alignParentLeft="true" + android:layout_alignTop="@+id/buttonBar" + android:layout_toLeftOf="@+id/buttonBar"> - + + + + + + + android:layout_alignParentRight="true"> + + - + + + - + android:minHeight="48dp"> - - - + + - + android:layout_centerInParent="true" + android:text="@string/autoupload_custom_folder"/> + \ No newline at end of file diff --git a/src/main/res/layout/folder_sync_layout.xml b/src/main/res/layout/folder_sync_layout.xml index 652bd505931c..3f1316fd1bb2 100644 --- a/src/main/res/layout/folder_sync_layout.xml +++ b/src/main/res/layout/folder_sync_layout.xml @@ -35,33 +35,6 @@ - - - - - - - Date: Thu, 4 May 2017 17:24:38 +0200 Subject: [PATCH 017/206] Fix all folders Signed-off-by: Mario Danic --- .../com/owncloud/android/ui/activity/FolderSyncActivity.java | 4 ++++ .../com/owncloud/android/ui/adapter/FolderSyncAdapter.java | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/src/main/java/com/owncloud/android/ui/activity/FolderSyncActivity.java b/src/main/java/com/owncloud/android/ui/activity/FolderSyncActivity.java index 9b733bf5ee36..1969d07c58a1 100644 --- a/src/main/java/com/owncloud/android/ui/activity/FolderSyncActivity.java +++ b/src/main/java/com/owncloud/android/ui/activity/FolderSyncActivity.java @@ -201,6 +201,10 @@ public void run() { mHandler.post(new TimerTask() { @Override public void run() { + // strange hack to make everything work as expected + if (syncFolderItems.size() > 0) { + syncFolderItems.add(0, syncFolderItems.get(0)); + } mAdapter.setSyncFolderItems(syncFolderItems); setListShown(true); } diff --git a/src/main/java/com/owncloud/android/ui/adapter/FolderSyncAdapter.java b/src/main/java/com/owncloud/android/ui/adapter/FolderSyncAdapter.java index 5c883d8c3206..f82dc397485f 100644 --- a/src/main/java/com/owncloud/android/ui/adapter/FolderSyncAdapter.java +++ b/src/main/java/com/owncloud/android/ui/adapter/FolderSyncAdapter.java @@ -157,7 +157,7 @@ public void onClick(View v) { @Override public void onBindViewHolder(MainViewHolder holder, int section, int relativePosition, int absolutePosition) { - if (section != 0 && mSyncFolderItems.get(section - 1).getFilePaths() != null) { + if (section != 0 && mSyncFolderItems.get(section).getFilePaths() != null) { File file = new File(mSyncFolderItems.get(section).getFilePaths().get(relativePosition)); ThumbnailsCacheManager.MediaThumbnailGenerationTask task = From d9ad9d58b009cb23d58e5ff0eb6ecfd923479450 Mon Sep 17 00:00:00 2001 From: Mario Danic Date: Thu, 4 May 2017 23:43:49 +0200 Subject: [PATCH 018/206] Enable auto upload for all Signed-off-by: Mario Danic --- src/main/AndroidManifest.xml | 25 -- .../datamodel/UploadsStorageManager.java | 154 ++++++------ .../files/InstantUploadBroadcastReceiver.java | 223 ------------------ .../services/ConnectivityActionReceiver.java | 223 ------------------ .../android/files/services/FileUploader.java | 3 +- .../android/ui/activity/DrawerActivity.java | 6 - .../ui/activity/FileDisplayActivity.java | 5 +- 7 files changed, 77 insertions(+), 562 deletions(-) delete mode 100644 src/main/java/com/owncloud/android/files/InstantUploadBroadcastReceiver.java delete mode 100755 src/main/java/com/owncloud/android/files/services/ConnectivityActionReceiver.java diff --git a/src/main/AndroidManifest.xml b/src/main/AndroidManifest.xml index 03e534ae3cfd..660bdfd93f0a 100644 --- a/src/main/AndroidManifest.xml +++ b/src/main/AndroidManifest.xml @@ -226,32 +226,7 @@ - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/main/java/com/owncloud/android/datamodel/UploadsStorageManager.java b/src/main/java/com/owncloud/android/datamodel/UploadsStorageManager.java index 9d4a7608b7bc..2808a93abdfa 100644 --- a/src/main/java/com/owncloud/android/datamodel/UploadsStorageManager.java +++ b/src/main/java/com/owncloud/android/datamodel/UploadsStorageManager.java @@ -1,22 +1,22 @@ /** - * ownCloud Android client application + * ownCloud Android client application * - * @author LukeOwncloud - * @author David A. Velasco - * @author masensio - * Copyright (C) 2016 ownCloud Inc. + * @author LukeOwncloud + * @author David A. Velasco + * @author masensio + * Copyright (C) 2016 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 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. + * 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 . + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . */ package com.owncloud.android.datamodel; @@ -26,8 +26,6 @@ import android.content.Context; import android.database.Cursor; import android.net.Uri; -import android.os.Build; -import android.support.annotation.RequiresApi; import com.evernote.android.job.JobManager; import com.evernote.android.job.JobRequest; @@ -163,9 +161,9 @@ public int updateUpload(OCUpload ocUpload) { cv.put(ProviderTableMeta.UPLOADS_UPLOAD_END_TIMESTAMP, ocUpload.getUploadEndTimestamp()); int result = getDB().update(ProviderTableMeta.CONTENT_URI_UPLOADS, - cv, - ProviderTableMeta._ID + "=?", - new String[]{String.valueOf(ocUpload.getUploadId())} + cv, + ProviderTableMeta._ID + "=?", + new String[]{String.valueOf(ocUpload.getUploadId())} ); Log_OC.d(TAG, "updateUpload returns with: " + result + " for file: " + ocUpload.getLocalPath()); @@ -188,15 +186,15 @@ private int updateUploadInternal(Cursor c, UploadStatus status, UploadResult res String path = c.getString(c.getColumnIndex(ProviderTableMeta.UPLOADS_LOCAL_PATH)); Log_OC.v( - TAG, - "Updating " + path + " with status:" + status + " and result:" - + (result == null ? "null" : result.toString()) + " (old:" - + upload.toFormattedString() + ")"); + TAG, + "Updating " + path + " with status:" + status + " and result:" + + (result == null ? "null" : result.toString()) + " (old:" + + upload.toFormattedString() + ")"); upload.setUploadStatus(status); upload.setLastResult(result); upload.setRemotePath(remotePath); - if(localPath != null) { + if (localPath != null) { upload.setLocalPath(localPath); } if (status == UploadStatus.UPLOAD_SUCCEEDED) { @@ -236,7 +234,7 @@ public int updateUploadStatus(long id, UploadStatus status, UploadResult result, if (c.getCount() != 1) { Log_OC.e(TAG, c.getCount() + " items for id=" + id - + " available in UploadDb. Expected 1. Failed to update upload db."); + + " available in UploadDb. Expected 1. Failed to update upload db."); } else { returnValue = updateUploadInternal(c, status, result, remotePath, localPath); } @@ -266,7 +264,7 @@ public void notifyObserversNow() { public int removeUpload(OCUpload upload) { int result = getDB().delete( ProviderTableMeta.CONTENT_URI_UPLOADS, - ProviderTableMeta._ID + "=?" , + ProviderTableMeta._ID + "=?", new String[]{Long.toString(upload.getUploadId())} ); Log_OC.d(TAG, "delete returns " + result + " for upload " + upload); @@ -287,7 +285,7 @@ public int removeUpload(OCUpload upload) { public int removeUpload(String accountName, String remotePath) { int result = getDB().delete( ProviderTableMeta.CONTENT_URI_UPLOADS, - ProviderTableMeta.UPLOADS_ACCOUNT_NAME + "=? AND " + ProviderTableMeta.UPLOADS_REMOTE_PATH + "=?" , + ProviderTableMeta.UPLOADS_ACCOUNT_NAME + "=? AND " + ProviderTableMeta.UPLOADS_REMOTE_PATH + "=?", new String[]{accountName, remotePath} ); Log_OC.d(TAG, "delete returns " + result + " for file " + remotePath + " in " + accountName); @@ -379,6 +377,7 @@ private OCUpload createOCUploadFromCursor(Cursor c) { */ public OCUpload[] getCurrentAndPendingUploads() { + OCUpload[] uploads = getUploads( ProviderTableMeta.UPLOADS_STATUS + "==" + UploadStatus.UPLOAD_IN_PROGRESS.value + " OR " + ProviderTableMeta.UPLOADS_LAST_RESULT + "==" + UploadResult.DELAYED_FOR_WIFI.getValue() + " OR " + @@ -386,17 +385,13 @@ public OCUpload[] getCurrentAndPendingUploads() { null ); - // add pending Jobs - if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) { - return uploads; - } else { - List result = getPendingJobs(); - Collections.addAll(result, uploads); - return result.toArray(uploads); - } + //return uploads; + + List result = getPendingJobs(); + Collections.addAll(result, uploads); + return result.toArray(uploads); } - @RequiresApi(Build.VERSION_CODES.LOLLIPOP) private List getPendingJobs() { Set jobRequests = JobManager.create(mContext).getAllJobRequestsForTag(AutoUploadJob.TAG); @@ -404,11 +399,11 @@ private List getPendingJobs() { for (JobRequest ji : jobRequests) { PersistableBundleCompat extras = ji.getExtras(); - OCUpload upload = new OCUpload(extras.getString("filePath", ""), - extras.getString("remotePath", ""), - extras.getString("account", "")); + OCUpload upload = new OCUpload(extras.getString("filePath", ""), + extras.getString("remotePath", ""), + extras.getString("account", "")); - list.add(upload); + list.add(upload); } return list; @@ -416,15 +411,14 @@ private List getPendingJobs() { public void cancelPendingAutoUploadJobsForAccount(Account account) { JobManager jobManager = JobManager.create(mContext); - for (JobRequest ji: jobManager.getAllJobRequestsForTag(AutoUploadJob.TAG)) { + for (JobRequest ji : jobManager.getAllJobRequestsForTag(AutoUploadJob.TAG)) { if (ji.getExtras().getString(AutoUploadJob.ACCOUNT, "").equalsIgnoreCase(account.name)) { jobManager.cancel(ji.getJobId()); } } } - @RequiresApi(Build.VERSION_CODES.LOLLIPOP) - public void cancelPendingJob(String accountName, String remotePath){ + public void cancelPendingJob(String accountName, String remotePath) { JobManager jobManager = JobManager.create(mContext); Set jobRequests = jobManager.getAllJobRequests(); @@ -457,14 +451,14 @@ public OCUpload[] getFinishedUploads() { /** * Get all failed uploads, except for those that were not performed due to lack of Wifi connection - * @return Array of failed uploads, except for those that were not performed due to lack of Wifi connection. + * @return Array of failed uploads, except for those that were not performed due to lack of Wifi connection. */ public OCUpload[] getFailedButNotDelayedUploads() { return getUploads(ProviderTableMeta.UPLOADS_STATUS + "==" + UploadStatus.UPLOAD_FAILED.value + AND + - ProviderTableMeta.UPLOADS_LAST_RESULT + "<>" + UploadResult.DELAYED_FOR_WIFI.getValue() + AND + - ProviderTableMeta.UPLOADS_LAST_RESULT + "<>" + UploadResult.DELAYED_FOR_CHARGING.getValue(), - null + ProviderTableMeta.UPLOADS_LAST_RESULT + "<>" + UploadResult.DELAYED_FOR_WIFI.getValue() + AND + + ProviderTableMeta.UPLOADS_LAST_RESULT + "<>" + UploadResult.DELAYED_FOR_CHARGING.getValue(), + null ); } @@ -474,11 +468,11 @@ private ContentResolver getDB() { public long clearFailedButNotDelayedUploads() { long result = getDB().delete( - ProviderTableMeta.CONTENT_URI_UPLOADS, + ProviderTableMeta.CONTENT_URI_UPLOADS, ProviderTableMeta.UPLOADS_STATUS + "==" + UploadStatus.UPLOAD_FAILED.value + AND + - ProviderTableMeta.UPLOADS_LAST_RESULT + "<>" + UploadResult.DELAYED_FOR_WIFI.getValue() + AND + - ProviderTableMeta.UPLOADS_LAST_RESULT + "<>" + UploadResult.DELAYED_FOR_CHARGING.getValue(), - null + ProviderTableMeta.UPLOADS_LAST_RESULT + "<>" + UploadResult.DELAYED_FOR_WIFI.getValue() + AND + + ProviderTableMeta.UPLOADS_LAST_RESULT + "<>" + UploadResult.DELAYED_FOR_CHARGING.getValue(), + null ); Log_OC.d(TAG, "delete all failed uploads but those delayed for Wifi"); if (result > 0) { @@ -491,7 +485,7 @@ public long clearSuccessfulUploads() { long result = getDB().delete( ProviderTableMeta.CONTENT_URI_UPLOADS, - ProviderTableMeta.UPLOADS_STATUS + "=="+ UploadStatus.UPLOAD_SUCCEEDED.value, null + ProviderTableMeta.UPLOADS_STATUS + "==" + UploadStatus.UPLOAD_SUCCEEDED.value, null ); Log_OC.d(TAG, "delete all successful uploads"); if (result > 0) { @@ -508,8 +502,8 @@ public long clearAllFinishedButNotDelayedUploads() { long result = getDB().delete( ProviderTableMeta.CONTENT_URI_UPLOADS, ProviderTableMeta.UPLOADS_STATUS + "=? OR " + ProviderTableMeta.UPLOADS_STATUS + "=? AND " + - ProviderTableMeta.UPLOADS_LAST_RESULT + "<>" + UploadResult.DELAYED_FOR_WIFI.getValue() + AND + - ProviderTableMeta.UPLOADS_LAST_RESULT + "<>" + UploadResult.DELAYED_FOR_CHARGING.getValue(), + ProviderTableMeta.UPLOADS_LAST_RESULT + "<>" + UploadResult.DELAYED_FOR_WIFI.getValue() + AND + + ProviderTableMeta.UPLOADS_LAST_RESULT + "<>" + UploadResult.DELAYED_FOR_CHARGING.getValue(), whereArgs ); Log_OC.d(TAG, "delete all finished uploads"); @@ -528,28 +522,28 @@ public void updateDatabaseUploadResult(RemoteOperationResult uploadResult, Uploa if (uploadResult.isCancelled()) { removeUpload( - upload.getAccount().name, - upload.getRemotePath() + upload.getAccount().name, + upload.getRemotePath() ); } else { String localPath = (FileUploader.LOCAL_BEHAVIOUR_MOVE == upload.getLocalBehaviour()) - ? upload.getStoragePath() : null; + ? upload.getStoragePath() : null; if (uploadResult.isSuccess()) { updateUploadStatus( - upload.getOCUploadId(), - UploadStatus.UPLOAD_SUCCEEDED, - UploadResult.UPLOADED, - upload.getRemotePath(), - localPath + upload.getOCUploadId(), + UploadStatus.UPLOAD_SUCCEEDED, + UploadResult.UPLOADED, + upload.getRemotePath(), + localPath ); } else { updateUploadStatus( - upload.getOCUploadId(), - UploadStatus.UPLOAD_FAILED, - UploadResult.fromOperationResult(uploadResult), - upload.getRemotePath(), - localPath + upload.getOCUploadId(), + UploadStatus.UPLOAD_FAILED, + UploadResult.fromOperationResult(uploadResult), + upload.getRemotePath(), + localPath ); } } @@ -560,14 +554,14 @@ public void updateDatabaseUploadResult(RemoteOperationResult uploadResult, Uploa */ public void updateDatabaseUploadStart(UploadFileOperation upload) { String localPath = (FileUploader.LOCAL_BEHAVIOUR_MOVE == upload.getLocalBehaviour()) - ? upload.getStoragePath() : null; + ? upload.getStoragePath() : null; updateUploadStatus( - upload.getOCUploadId(), - UploadStatus.UPLOAD_IN_PROGRESS, - UploadResult.UNKNOWN, - upload.getRemotePath(), - localPath + upload.getOCUploadId(), + UploadStatus.UPLOAD_IN_PROGRESS, + UploadResult.UNKNOWN, + upload.getRemotePath(), + localPath ); } @@ -576,7 +570,7 @@ public void updateDatabaseUploadStart(UploadFileOperation upload) { * Changes the status of any in progress upload from UploadStatus.UPLOAD_IN_PROGRESS * to UploadStatus.UPLOAD_FAILED * - * @return Number of uploads which status was changed. + * @return Number of uploads which status was changed. */ public int failInProgressUploads(UploadResult fail) { Log_OC.v(TAG, "Updating state of any killed upload"); @@ -584,16 +578,16 @@ public int failInProgressUploads(UploadResult fail) { ContentValues cv = new ContentValues(); cv.put(ProviderTableMeta.UPLOADS_STATUS, UploadStatus.UPLOAD_FAILED.getValue()); cv.put( - ProviderTableMeta.UPLOADS_LAST_RESULT, - fail != null ? fail.getValue() : UploadResult.UNKNOWN.getValue() + ProviderTableMeta.UPLOADS_LAST_RESULT, + fail != null ? fail.getValue() : UploadResult.UNKNOWN.getValue() ); cv.put(ProviderTableMeta.UPLOADS_UPLOAD_END_TIMESTAMP, Calendar.getInstance().getTimeInMillis()); int result = getDB().update( - ProviderTableMeta.CONTENT_URI_UPLOADS, - cv, - ProviderTableMeta.UPLOADS_STATUS + "=?", - new String[]{String.valueOf(UploadStatus.UPLOAD_IN_PROGRESS.getValue())} + ProviderTableMeta.CONTENT_URI_UPLOADS, + cv, + ProviderTableMeta.UPLOADS_STATUS + "=?", + new String[]{String.valueOf(UploadStatus.UPLOAD_IN_PROGRESS.getValue())} ); if (result == 0) { diff --git a/src/main/java/com/owncloud/android/files/InstantUploadBroadcastReceiver.java b/src/main/java/com/owncloud/android/files/InstantUploadBroadcastReceiver.java deleted file mode 100644 index c5a21b55c991..000000000000 --- a/src/main/java/com/owncloud/android/files/InstantUploadBroadcastReceiver.java +++ /dev/null @@ -1,223 +0,0 @@ -/** - * ownCloud Android client application - * - * @author Bartek Przybylski - * @author David A. Velasco - * Copyright (C) 2012 Bartek Przybylski - * Copyright (C) 2016 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 . - */ - -package com.owncloud.android.files; - -import android.Manifest; -import android.accounts.Account; -import android.content.BroadcastReceiver; -import android.content.Context; -import android.content.Intent; -import android.content.SharedPreferences; -import android.database.Cursor; -import android.os.Build; -import android.provider.MediaStore.Images; -import android.provider.MediaStore.Video; -import android.support.v4.content.ContextCompat; - -import com.owncloud.android.R; -import com.owncloud.android.authentication.AccountUtils; -import com.owncloud.android.db.PreferenceManager; -import com.owncloud.android.files.services.FileUploader; -import com.owncloud.android.lib.common.utils.Log_OC; -import com.owncloud.android.operations.UploadFileOperation; -import com.owncloud.android.utils.FileStorageUtils; - - -public class InstantUploadBroadcastReceiver extends BroadcastReceiver { - - private static final String TAG = InstantUploadBroadcastReceiver.class.getName(); - // Image action - // Unofficial action, works for most devices but not HTC. See: https://github.com/owncloud/android/issues/6 - private static final String NEW_PHOTO_ACTION_UNOFFICIAL = "com.android.camera.NEW_PICTURE"; - // Officially supported action since SDK 14: - // http://developer.android.com/reference/android/hardware/Camera.html#ACTION_NEW_PICTURE - private static final String NEW_PHOTO_ACTION = "android.hardware.action.NEW_PICTURE"; - // Video action - // Officially supported action since SDK 14: - // http://developer.android.com/reference/android/hardware/Camera.html#ACTION_NEW_VIDEO - private static final String NEW_VIDEO_ACTION = "android.hardware.action.NEW_VIDEO"; - - /** - * Because we support NEW_PHOTO_ACTION and NEW_PHOTO_ACTION_UNOFFICIAL it can happen that - * handleNewPictureAction is called twice for the same photo. Use this simple static variable to - * remember last uploaded photo to filter duplicates. Must not be null! - */ - static String lastUploadedPhotoPath = ""; - - @Override - public void onReceive(Context context, Intent intent) { - if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) { - Log_OC.d(TAG, "Received: " + intent.getAction()); - if (intent.getAction().equals(NEW_PHOTO_ACTION_UNOFFICIAL)) { - handleNewPictureAction(context, intent); - Log_OC.d(TAG, "UNOFFICIAL processed: com.android.camera.NEW_PICTURE"); - } else if (intent.getAction().equals(NEW_PHOTO_ACTION)) { - handleNewPictureAction(context, intent); - Log_OC.d(TAG, "OFFICIAL processed: android.hardware.action.NEW_PICTURE"); - } else if (intent.getAction().equals(NEW_VIDEO_ACTION)) { - handleNewVideoAction(context, intent); - Log_OC.d(TAG, "OFFICIAL processed: android.hardware.action.NEW_VIDEO"); - } else { - Log_OC.e(TAG, "Incorrect intent received: " + intent.getAction()); - } - } - } - - private void handleNewPictureAction(Context context, Intent intent) { - Cursor c = null; - String file_path = null; - String file_name = null; - String mime_type = null; - long date_taken = 0; - - Log_OC.i(TAG, "New photo received"); - - if (!PreferenceManager.instantPictureUploadEnabled(context)) { - Log_OC.d(TAG, "Instant picture upload disabled, ignoring new picture"); - return; - } - - Account account = AccountUtils.getCurrentOwnCloudAccount(context); - if (account == null) { - Log_OC.w(TAG, "No account found for instant upload, aborting"); - return; - } - - String[] CONTENT_PROJECTION = { - Images.Media.DATA, Images.Media.DISPLAY_NAME, Images.Media.MIME_TYPE, Images.Media.SIZE}; - - // if < Jelly Bean permission must be accepted during installation - if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.JELLY_BEAN) { - int permissionCheck = ContextCompat.checkSelfPermission(context, Manifest.permission.READ_EXTERNAL_STORAGE); - - if (android.content.pm.PackageManager.PERMISSION_GRANTED != permissionCheck) { - Log_OC.w(TAG, "Read external storage permission isn't granted, aborting"); - return; - } - } - - c = context.getContentResolver().query(intent.getData(), CONTENT_PROJECTION, null, null, null); - if (!c.moveToFirst()) { - Log_OC.e(TAG, "Couldn't resolve given uri: " + intent.getDataString()); - return; - } - file_path = c.getString(c.getColumnIndex(Images.Media.DATA)); - file_name = c.getString(c.getColumnIndex(Images.Media.DISPLAY_NAME)); - mime_type = c.getString(c.getColumnIndex(Images.Media.MIME_TYPE)); - date_taken = System.currentTimeMillis(); - c.close(); - - if (file_path.equals(lastUploadedPhotoPath)) { - Log_OC.d(TAG, "Duplicate detected: " + file_path + ". Ignore."); - return; - } - - lastUploadedPhotoPath = file_path; - Log_OC.d(TAG, "Path: " + file_path + ""); - - new FileUploader.UploadRequester(); - - int behaviour = getUploadBehaviour(context); - Boolean subfolderByDate = PreferenceManager.instantPictureUploadPathUseSubfolders(context); - SharedPreferences pref = PreferenceManager.getDefaultSharedPreferences(context); - String uploadPathdef = context.getString(R.string.instant_upload_path); - String uploadPath = pref.getString("instant_upload_path", uploadPathdef); - - FileUploader.UploadRequester requester = new FileUploader.UploadRequester(); - requester.uploadNewFile( - context, - account, - file_path, - FileStorageUtils.getInstantUploadFilePath(uploadPath, file_name, date_taken, subfolderByDate), - behaviour, - mime_type, - true, // create parent folder if not existent - UploadFileOperation.CREATED_AS_INSTANT_PICTURE - ); - } - - private Integer getUploadBehaviour(Context context) { - SharedPreferences appPreferences = android.preference.PreferenceManager.getDefaultSharedPreferences(context); - String behaviour = appPreferences.getString("prefs_instant_behaviour", "NOTHING"); - - if (behaviour.equalsIgnoreCase("NOTHING")) { - Log_OC.d(TAG, "upload file and do nothing"); - return FileUploader.LOCAL_BEHAVIOUR_FORGET; - } else if (behaviour.equalsIgnoreCase("MOVE")) { - Log_OC.d(TAG, "upload file and move file to oc folder"); - return FileUploader.LOCAL_BEHAVIOUR_MOVE; - } else if (behaviour.equalsIgnoreCase("DELETE")) { - Log_OC.d(TAG, "upload file and delete original file"); - return FileUploader.LOCAL_BEHAVIOUR_DELETE; - } - return FileUploader.LOCAL_BEHAVIOUR_FORGET; - } - - private void handleNewVideoAction(Context context, Intent intent) { - Cursor c = null; - String file_path = null; - String file_name = null; - String mime_type = null; - long date_taken = 0; - - Log_OC.i(TAG, "New video received"); - - if (!PreferenceManager.instantVideoUploadEnabled(context)) { - Log_OC.d(TAG, "Instant video upload disabled, ignoring new video"); - return; - } - - Account account = AccountUtils.getCurrentOwnCloudAccount(context); - if (account == null) { - Log_OC.w(TAG, "No account found for instant upload, aborting"); - return; - } - - String[] CONTENT_PROJECTION = {Video.Media.DATA, Video.Media.DISPLAY_NAME, Video.Media.MIME_TYPE, - Video.Media.SIZE}; - c = context.getContentResolver().query(intent.getData(), CONTENT_PROJECTION, null, null, null); - if (!c.moveToFirst()) { - Log_OC.e(TAG, "Couldn't resolve given uri: " + intent.getDataString()); - return; - } - file_path = c.getString(c.getColumnIndex(Video.Media.DATA)); - file_name = c.getString(c.getColumnIndex(Video.Media.DISPLAY_NAME)); - mime_type = c.getString(c.getColumnIndex(Video.Media.MIME_TYPE)); - c.close(); - date_taken = System.currentTimeMillis(); - Log_OC.d(TAG, file_path + ""); - - int behaviour = getUploadBehaviour(context); - FileUploader.UploadRequester requester = new FileUploader.UploadRequester(); - requester.uploadNewFile( - context, - account, - file_path, - FileStorageUtils.getInstantVideoUploadFilePath(context, file_name, date_taken), - behaviour, - mime_type, - true, // create parent folder if not existent - UploadFileOperation.CREATED_AS_INSTANT_VIDEO - ); - } - -} diff --git a/src/main/java/com/owncloud/android/files/services/ConnectivityActionReceiver.java b/src/main/java/com/owncloud/android/files/services/ConnectivityActionReceiver.java deleted file mode 100755 index f6c34399f00f..000000000000 --- a/src/main/java/com/owncloud/android/files/services/ConnectivityActionReceiver.java +++ /dev/null @@ -1,223 +0,0 @@ -/** - * ownCloud Android client application - * - * @author LukeOwncloud - * Copyright (C) 2016 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 . - * - */ - -package com.owncloud.android.files.services; - -import android.content.BroadcastReceiver; -import android.content.Context; -import android.content.Intent; -import android.net.NetworkInfo; -import android.net.wifi.WifiInfo; -import android.net.wifi.WifiManager; -import android.os.Bundle; - -import com.owncloud.android.db.PreferenceManager; -import com.owncloud.android.db.UploadResult; -import com.owncloud.android.lib.common.utils.Log_OC; - -/** - * Receives all connectivity action from Android OS at all times and performs - * required OC actions. For now that are: - Signal connectivity to - * {@link FileUploader}. - * - * Later can be added: - Signal connectivity to download service, deletion - * service, ... - Handle offline mode (cf. - * https://github.com/owncloud/android/issues/162) - * - * Have fun with the comments :S - */ -public class ConnectivityActionReceiver extends BroadcastReceiver { - private static final String TAG = ConnectivityActionReceiver.class.getSimpleName(); - - /** - * Magic keyword, by Google. - * - * {@see http://developer.android.com/intl/es/reference/android/net/wifi/WifiInfo.html#getSSID()} - */ - private static final String UNKNOWN_SSID = ""; - - - @Override - public void onReceive(final Context context, Intent intent) { - // LOG ALL EVENTS: - Log_OC.v(TAG, "action: " + intent.getAction()); - Log_OC.v(TAG, "component: " + intent.getComponent()); - Bundle extras = intent.getExtras(); - if (extras != null) { - for (String key : extras.keySet()) { - Log_OC.v(TAG, "key [" + key + "]: " + extras.get(key)); - } - } else { - Log_OC.v(TAG, "no extras"); - } - - if (intent.getAction().equals(Intent.ACTION_POWER_CONNECTED) && - (PreferenceManager.instantPictureUploadEnabled(context) && - PreferenceManager.instantPictureUploadWhenChargingOnly(context)) || - (PreferenceManager.instantVideoUploadEnabled(context) && - PreferenceManager.instantVideoUploadWhenChargingOnly(context)) - ) { - // for the moment, only recovery of instant uploads, similar to behaviour in release 1.9.1 - Log_OC.d(TAG, "Requesting retry of instant uploads (& friends) due to charging"); - FileUploader.UploadRequester requester = new FileUploader.UploadRequester(); - requester.retryFailedUploads( - context, - null, - UploadResult.DELAYED_FOR_CHARGING // for the rest of enqueued when Wifi fell - ); - } - - /** - * There is an interesting mess to process WifiManager.NETWORK_STATE_CHANGED_ACTION and - * ConnectivityManager.CONNECTIVITY_ACTION in a simple and reliable way. - * - * The former triggers much more events than what we really need to know about Wifi connection. - * - * But there are annoying uncertainties about ConnectivityManager.CONNECTIVITY_ACTION due - * to the deprecation of ConnectivityManager.EXTRA_NETWORK_INFO in API level 14, and the absence - * of ConnectivityManager.EXTRA_NETWORK_TYPE until API level 17. Dear Google, how should we - * handle API levels 14 to 16? - * - * In the end maybe we need to keep in memory the current knowledge about connectivity - * and update it taking into account several Intents received in a row - * - * But first let's try something "simple" to keep a basic retry of instant uploads in - * version 1.9.2, similar to the existent until 1.9.1. To be improved. - */ - if(intent.getAction().equals(WifiManager.NETWORK_STATE_CHANGED_ACTION)) { - NetworkInfo networkInfo = - intent.getParcelableExtra(WifiManager.EXTRA_NETWORK_INFO); - WifiInfo wifiInfo = - intent.getParcelableExtra(WifiManager.EXTRA_WIFI_INFO); - String bssid = - intent.getStringExtra(WifiManager.EXTRA_BSSID); - if(networkInfo.isConnected() && // not enough; see (*) right below - wifiInfo != null && - !UNKNOWN_SSID.equals(wifiInfo.getSSID().toLowerCase()) && - bssid != null - ) { - Log_OC.d(TAG, "WiFi connected"); - - wifiConnected(context); - } else { - // TODO tons of things to check to conclude disconnection; - // TODO maybe alternative commented below, based on CONNECTIVITY_ACTION is better - Log_OC.d(TAG, "WiFi disconnected ... but don't know if right now"); - } - } - // (*) When WiFi is lost, an Intent with network state CONNECTED and SSID "" is - // received right before another Intent with network state DISCONNECTED; needs to - // be differentiated of a new Wifi connection. - // - // Besides, with a new connection two Intents are received, having only the second the extra - // WifiManager.EXTRA_BSSID, with the BSSID of the access point accessed. - // - // Not sure if this protocol is exact, since it's not documented. Only found mild references in - // - http://developer.android.com/intl/es/reference/android/net/wifi/WifiInfo.html#getSSID() - // - http://developer.android.com/intl/es/reference/android/net/wifi/WifiManager.html#EXTRA_BSSID - // and reproduced in Nexus 5 with Android 6. - - - /** - * Possible alternative attending ConnectivityManager.CONNECTIVITY_ACTION. - * - * Let's see what QA has to say - * - if(intent.getAction().equals(ConnectivityManager.CONNECTIVITY_ACTION)) { - NetworkInfo networkInfo = intent.getParcelableExtra( - ConnectivityManager.EXTRA_NETWORK_INFO // deprecated in API 14 - ); - int networkType = intent.getIntExtra( - ConnectivityManager.EXTRA_NETWORK_TYPE, // only from API level 17 - -1 - ); - boolean couldBeWifiAction = - (networkInfo == null && networkType < 0) || // cases of lack of info - networkInfo.getType() == ConnectivityManager.TYPE_WIFI || - networkType == ConnectivityManager.TYPE_WIFI; - - if (couldBeWifiAction) { - if (ConnectivityUtils.isAppConnectedViaUnmeteredWiFi(context)) { - Log_OC.d(TAG, "WiFi connected"); - wifiConnected(context); - } else { - Log_OC.d(TAG, "WiFi disconnected"); - wifiDisconnected(context); - } - } /* else, CONNECTIVIY_ACTION is (probably) about other network interface (mobile, bluetooth, ...) - } - */ - } - - private void wifiConnected(Context context) { - // for the moment, only recovery of instant uploads, similar to behaviour in release 1.9.1 - if ( - (PreferenceManager.instantPictureUploadEnabled(context) && - PreferenceManager.instantPictureUploadViaWiFiOnly(context)) || - (PreferenceManager.instantVideoUploadEnabled(context) && - PreferenceManager.instantVideoUploadViaWiFiOnly(context)) - ) { - Log_OC.d(TAG, "Requesting retry of instant uploads (& friends)"); - FileUploader.UploadRequester requester = new FileUploader.UploadRequester(); - requester.retryFailedUploads( - context, - null, - UploadResult.NETWORK_CONNECTION // for the interrupted when Wifi fell, if any - // (side effect: any upload failed due to network error will be retried too, instant or not) - ); - requester.retryFailedUploads( - context, - null, - UploadResult.DELAYED_FOR_WIFI // for the rest of enqueued when Wifi fell - ); - } - } - - /** - * - private void wifiDisconnected() { - // TODO something smart - - // NOTE: explicit cancellation of only-wifi instant uploads is not needed anymore, since currently: - // - any upload in progress will be interrupted due to the lack of connectivity while the device - // reconnects through other network interface; - // - FileUploader checks instant upload settings and connection state before executing each - // upload operation, so other pending instant uploads after the current one will not be run - // (currently are silently moved to FAILED state) - } - - - - static public void enableActionReceiver(Context context) { - PackageManager pm = context.getPackageManager(); - ComponentName compName = new ComponentName(context.getApplicationContext(), ConnectivityActionReceiver.class); - pm.setComponentEnabledSetting(compName, PackageManager.COMPONENT_ENABLED_STATE_ENABLED, - PackageManager.DONT_KILL_APP); - } - - static public void disableActionReceiver(Context context) { - PackageManager pm = context.getPackageManager(); - ComponentName compName = new ComponentName(context.getApplicationContext(), ConnectivityActionReceiver.class); - pm.setComponentEnabledSetting(compName, PackageManager.COMPONENT_ENABLED_STATE_DISABLED, - PackageManager.DONT_KILL_APP); - } - - */ -} \ No newline at end of file diff --git a/src/main/java/com/owncloud/android/files/services/FileUploader.java b/src/main/java/com/owncloud/android/files/services/FileUploader.java index e98e71d2466f..78f9ee5c96fd 100644 --- a/src/main/java/com/owncloud/android/files/services/FileUploader.java +++ b/src/main/java/com/owncloud/android/files/services/FileUploader.java @@ -34,7 +34,6 @@ import android.content.Context; import android.content.Intent; import android.os.Binder; -import android.os.Build; import android.os.Handler; import android.os.HandlerThread; import android.os.IBinder; @@ -691,7 +690,7 @@ private void cancel(String accountName, String remotePath) { // need to update now table in mUploadsStorageManager, // since the operation will not get to be run by FileUploader#uploadFile mUploadsStorageManager.removeUpload(accountName, remotePath); - } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + } else { // try to cancel job in jobScheduler mUploadsStorageManager.cancelPendingJob(accountName, remotePath); } diff --git a/src/main/java/com/owncloud/android/ui/activity/DrawerActivity.java b/src/main/java/com/owncloud/android/ui/activity/DrawerActivity.java index 2391ad12cdbb..4099185b97e8 100644 --- a/src/main/java/com/owncloud/android/ui/activity/DrawerActivity.java +++ b/src/main/java/com/owncloud/android/ui/activity/DrawerActivity.java @@ -211,12 +211,6 @@ protected void setupDrawer() { setupDrawerMenu(mNavigationView); setupQuotaElement(); - - // show folder sync menu item only for Android 6+ - if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M && - mNavigationView.getMenu().findItem(R.id.nav_folder_sync) != null) { - mNavigationView.getMenu().removeItem(R.id.nav_folder_sync); - } } setupDrawerToggle(); diff --git a/src/main/java/com/owncloud/android/ui/activity/FileDisplayActivity.java b/src/main/java/com/owncloud/android/ui/activity/FileDisplayActivity.java index c543e795b77c..c891127358f7 100644 --- a/src/main/java/com/owncloud/android/ui/activity/FileDisplayActivity.java +++ b/src/main/java/com/owncloud/android/ui/activity/FileDisplayActivity.java @@ -282,9 +282,8 @@ public void onClick(View v) { */ private void upgradeNotificationForInstantUpload() { // check for Android 6+ if legacy instant upload is activated --> disable + show info - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && - (PreferenceManager.instantPictureUploadEnabled(this) || - PreferenceManager.instantPictureUploadEnabled(this))) { + if (PreferenceManager.instantPictureUploadEnabled(this) || + PreferenceManager.instantPictureUploadEnabled(this)) { // remove legacy shared preferences SharedPreferences.Editor editor = PreferenceManager.getDefaultSharedPreferences(this).edit(); From cdcbd4bf79edda04a363ab97333478cb5e177640 Mon Sep 17 00:00:00 2001 From: Mario Danic Date: Fri, 5 May 2017 00:05:12 +0200 Subject: [PATCH 019/206] Add migration procedures Signed-off-by: Mario Danic --- .../android/datamodel/SyncedFolder.java | 11 +++++- .../datamodel/SyncedFolderProvider.java | 37 +++++++++++------- .../providers/FileContentProvider.java | 39 ++++++++++++++++++- 3 files changed, 72 insertions(+), 15 deletions(-) diff --git a/src/main/java/com/owncloud/android/datamodel/SyncedFolder.java b/src/main/java/com/owncloud/android/datamodel/SyncedFolder.java index 7cac28af5541..3b6ea18ac6fa 100644 --- a/src/main/java/com/owncloud/android/datamodel/SyncedFolder.java +++ b/src/main/java/com/owncloud/android/datamodel/SyncedFolder.java @@ -26,7 +26,7 @@ /** * Synced folder entity containing all information per synced folder. */ -public class SyncedFolder implements Serializable { +public class SyncedFolder implements Serializable, Cloneable { public static final long UNPERSISTED_ID = Long.MIN_VALUE; private static final long serialVersionUID = -793476118299906429L; private long id = UNPERSISTED_ID; @@ -94,6 +94,15 @@ public SyncedFolder(String localPath, String remotePath, Boolean wifiOnly, Boole this.type = type; } + public Object clone() { + try { + return super.clone(); + } catch( CloneNotSupportedException e) { + return null; + } + } + + public long getId() { return id; } diff --git a/src/main/java/com/owncloud/android/datamodel/SyncedFolderProvider.java b/src/main/java/com/owncloud/android/datamodel/SyncedFolderProvider.java index 2b71a92fff51..baef8c3c1d12 100644 --- a/src/main/java/com/owncloud/android/datamodel/SyncedFolderProvider.java +++ b/src/main/java/com/owncloud/android/datamodel/SyncedFolderProvider.java @@ -1,21 +1,21 @@ /** - * Nextcloud Android client application + * Nextcloud Android client application * - * Copyright (C) 2016 Andy Scherzinger - * Copyright (C) 2016 Nextcloud. + * Copyright (C) 2016 Andy Scherzinger + * Copyright (C) 2016 Nextcloud. * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE - * License as published by the Free Software Foundation; either - * version 3 of the License, or any later version. + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE + * License as published by the Free Software Foundation; either + * version 3 of the License, or 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. + * 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 . + * You should have received a copy of the GNU Affero General Public + * License along with this program. If not, see . */ package com.owncloud.android.datamodel; @@ -270,6 +270,17 @@ public int deleteSyncedFoldersNotInList(Context context, ArrayList ids) { return result; } + /** + * delete any records of synchronized folders that are withing the given list of ids. + */ + public int deleteSyncedFoldersInList(ArrayList ids) { + return mContentResolver.delete( + ProviderMeta.ProviderTableMeta.CONTENT_URI_SYNCED_FOLDERS, + ProviderMeta.ProviderTableMeta._ID + " IN (?)", + new String[]{String.valueOf(ids)} + ); + } + /** * update given synced folder. * diff --git a/src/main/java/com/owncloud/android/providers/FileContentProvider.java b/src/main/java/com/owncloud/android/providers/FileContentProvider.java index ad48e0b71550..7860dc4e75bd 100644 --- a/src/main/java/com/owncloud/android/providers/FileContentProvider.java +++ b/src/main/java/com/owncloud/android/providers/FileContentProvider.java @@ -27,6 +27,7 @@ import android.content.ContentProvider; import android.content.ContentProviderOperation; import android.content.ContentProviderResult; +import android.content.ContentResolver; import android.content.ContentUris; import android.content.ContentValues; import android.content.Context; @@ -42,7 +43,11 @@ import com.owncloud.android.MainApp; import com.owncloud.android.R; +import com.owncloud.android.datamodel.MediaFolder; +import com.owncloud.android.datamodel.MediaProvider; import com.owncloud.android.datamodel.OCFile; +import com.owncloud.android.datamodel.SyncedFolder; +import com.owncloud.android.datamodel.SyncedFolderProvider; import com.owncloud.android.datamodel.UploadsStorageManager; import com.owncloud.android.db.ProviderMeta; import com.owncloud.android.db.ProviderMeta.ProviderTableMeta; @@ -54,6 +59,7 @@ import java.io.File; import java.util.ArrayList; +import java.util.List; import java.util.Locale; /** @@ -982,11 +988,42 @@ public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { Log_OC.i(SQL, "Entering in the #20 ADD in onUpgrade"); db.beginTransaction(); try { - // add type column default being IMAGE(0) + // add type column default being LEGACY (3) db.execSQL(ALTER_TABLE + ProviderTableMeta.SYNCED_FOLDERS_TABLE_NAME + ADD_COLUMN + ProviderTableMeta.SYNCED_FOLDER_TYPE + " INTEGER " + " DEFAULT 3"); + ContentResolver contentResolver = getContext().getContentResolver(); + + SyncedFolderProvider syncedFolderProvider = new SyncedFolderProvider(contentResolver); + + final List imageMediaFolders = MediaProvider.getImageFolders(contentResolver, 0); + final List videoMediaFolders = MediaProvider.getVideoFolders(contentResolver, 0); + + ArrayList idsToDelete = new ArrayList<>(); + for (SyncedFolder syncedFolder : syncedFolderProvider.getSyncedFolders()) { + idsToDelete.add(syncedFolder.getId()); + for (int i = 0; i < imageMediaFolders.size(); i++) { + if (imageMediaFolders.get(i).absolutePath.equals(syncedFolder.getLocalPath())) { + SyncedFolder imageSyncedFolder = (SyncedFolder) syncedFolder.clone(); + imageSyncedFolder.setType(MediaFolder.IMAGE); + syncedFolderProvider.storeFolderSync(imageSyncedFolder); + break; + } + } + + for (int j = 0; j < videoMediaFolders.size(); j++) { + if (videoMediaFolders.get(j).absolutePath.equals(syncedFolder.getLocalPath())) { + SyncedFolder videoSyncedFolder = (SyncedFolder) syncedFolder.clone(); + videoSyncedFolder.setType(MediaFolder.VIDEO); + syncedFolderProvider.storeFolderSync(videoSyncedFolder); + break; + } + } + } + + syncedFolderProvider.deleteSyncedFoldersInList(idsToDelete); + upgraded = true; db.setTransactionSuccessful(); } finally { From d48d57e1b173d00d80a95ee01ed89879c1a6753b Mon Sep 17 00:00:00 2001 From: Mario Danic Date: Fri, 5 May 2017 13:50:18 +0200 Subject: [PATCH 020/206] Clean up DB upgrade Signed-off-by: Mario Danic --- .../com/owncloud/android/datamodel/MediaFolder.java | 1 - .../android/providers/FileContentProvider.java | 12 +++++++++--- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/src/main/java/com/owncloud/android/datamodel/MediaFolder.java b/src/main/java/com/owncloud/android/datamodel/MediaFolder.java index 71e3fcc12982..3bd4229b8a7a 100644 --- a/src/main/java/com/owncloud/android/datamodel/MediaFolder.java +++ b/src/main/java/com/owncloud/android/datamodel/MediaFolder.java @@ -30,7 +30,6 @@ public class MediaFolder { public static final Integer CUSTOM = 0; public static final Integer IMAGE = 1; public static final Integer VIDEO = 2; - public static final Integer LEGACY = 3; /** name of the folder. */ public String folderName; diff --git a/src/main/java/com/owncloud/android/providers/FileContentProvider.java b/src/main/java/com/owncloud/android/providers/FileContentProvider.java index 7860dc4e75bd..e2653815b410 100644 --- a/src/main/java/com/owncloud/android/providers/FileContentProvider.java +++ b/src/main/java/com/owncloud/android/providers/FileContentProvider.java @@ -988,11 +988,16 @@ public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { Log_OC.i(SQL, "Entering in the #20 ADD in onUpgrade"); db.beginTransaction(); try { - // add type column default being LEGACY (3) + // add type column default being CUSTOM (0) db.execSQL(ALTER_TABLE + ProviderTableMeta.SYNCED_FOLDERS_TABLE_NAME + ADD_COLUMN + ProviderTableMeta.SYNCED_FOLDER_TYPE + - " INTEGER " + " DEFAULT 3"); + " INTEGER " + " DEFAULT 0"); + + // create arbitrary data table + createArbitraryData(db); + // magic to split out existing synced folders in two when needed + // otherwise, we migrate them to their proper type (image or video) ContentResolver contentResolver = getContext().getContentResolver(); SyncedFolderProvider syncedFolderProvider = new SyncedFolderProvider(contentResolver); @@ -1001,7 +1006,8 @@ public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { final List videoMediaFolders = MediaProvider.getVideoFolders(contentResolver, 0); ArrayList idsToDelete = new ArrayList<>(); - for (SyncedFolder syncedFolder : syncedFolderProvider.getSyncedFolders()) { + List syncedFolders = syncedFolderProvider.getSyncedFolders(); + for (SyncedFolder syncedFolder : syncedFolders) { idsToDelete.add(syncedFolder.getId()); for (int i = 0; i < imageMediaFolders.size(); i++) { if (imageMediaFolders.get(i).absolutePath.equals(syncedFolder.getLocalPath())) { From 2bd5cdc7d470b8b8ce7875988edb2be258012e14 Mon Sep 17 00:00:00 2001 From: Mario Danic Date: Fri, 5 May 2017 13:53:18 +0200 Subject: [PATCH 021/206] Clean up in file content provider Signed-off-by: Mario Danic --- .../owncloud/android/providers/FileContentProvider.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/main/java/com/owncloud/android/providers/FileContentProvider.java b/src/main/java/com/owncloud/android/providers/FileContentProvider.java index e2653815b410..113ebe6f6aa5 100644 --- a/src/main/java/com/owncloud/android/providers/FileContentProvider.java +++ b/src/main/java/com/owncloud/android/providers/FileContentProvider.java @@ -1221,8 +1221,8 @@ private void createSyncedFoldersTable(SQLiteDatabase db) { + ProviderTableMeta.SYNCED_FOLDER_ENABLED + " INTEGER, " // enabled + ProviderTableMeta.SYNCED_FOLDER_SUBFOLDER_BY_DATE + " INTEGER, " // subfolder by date + ProviderTableMeta.SYNCED_FOLDER_ACCOUNT + " TEXT, " // account - + ProviderTableMeta.SYNCED_FOLDER_TYPE + " INTEGER, " // type - + ProviderTableMeta.SYNCED_FOLDER_UPLOAD_ACTION + " INTEGER );" // upload action + + ProviderTableMeta.SYNCED_FOLDER_UPLOAD_ACTION + " INTEGER, " // upload action + + ProviderTableMeta.SYNCED_FOLDER_TYPE + " INTEGER );" // type ); } @@ -1233,7 +1233,7 @@ private void createExternalLinksTable(SQLiteDatabase db) { + ProviderTableMeta.EXTERNAL_LINKS_LANGUAGE + " TEXT, " // language + ProviderTableMeta.EXTERNAL_LINKS_TYPE + " INTEGER, " // type + ProviderTableMeta.EXTERNAL_LINKS_NAME + " TEXT, " // name - + ProviderTableMeta.EXTERNAL_LINKS_URL + " TEXT )" // url + + ProviderTableMeta.EXTERNAL_LINKS_URL + " TEXT );" // url ); } @@ -1242,7 +1242,7 @@ private void createArbitraryData(SQLiteDatabase db) { + ProviderTableMeta._ID + " INTEGER PRIMARY KEY, " // id + ProviderTableMeta.ARBITRARY_DATA_CLOUD_ID + " TEXT, " // cloud id (account name + FQDN) + ProviderTableMeta.ARBITRARY_DATA_KEY + " TEXT, " // key - + ProviderTableMeta.ARBITRARY_DATA_VALUE + " TEXT) " // value + + ProviderTableMeta.ARBITRARY_DATA_VALUE + " TEXT );" // value ); } From 93c19a7eb773967345c9e13d4c7dfc372486e80d Mon Sep 17 00:00:00 2001 From: AndyScherzinger Date: Fri, 5 May 2017 17:33:41 +0200 Subject: [PATCH 022/206] more logging while migrating data. --- .../providers/FileContentProvider.java | 21 +++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/src/main/java/com/owncloud/android/providers/FileContentProvider.java b/src/main/java/com/owncloud/android/providers/FileContentProvider.java index 113ebe6f6aa5..28574fe6e201 100644 --- a/src/main/java/com/owncloud/android/providers/FileContentProvider.java +++ b/src/main/java/com/owncloud/android/providers/FileContentProvider.java @@ -989,15 +989,18 @@ public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { db.beginTransaction(); try { // add type column default being CUSTOM (0) + Log_OC.i(SQL, "Add type column and default value 0 (CUSTOM) to synced_folders table"); db.execSQL(ALTER_TABLE + ProviderTableMeta.SYNCED_FOLDERS_TABLE_NAME + ADD_COLUMN + ProviderTableMeta.SYNCED_FOLDER_TYPE + " INTEGER " + " DEFAULT 0"); // create arbitrary data table + Log_OC.i(SQL, "Create arbitrary_data table"); createArbitraryData(db); // magic to split out existing synced folders in two when needed // otherwise, we migrate them to their proper type (image or video) + Log_OC.i(SQL, "Migrate synced_folders records for image/video split"); ContentResolver contentResolver = getContext().getContentResolver(); SyncedFolderProvider syncedFolderProvider = new SyncedFolderProvider(contentResolver); @@ -1007,22 +1010,28 @@ public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { ArrayList idsToDelete = new ArrayList<>(); List syncedFolders = syncedFolderProvider.getSyncedFolders(); + long primaryKey; + SyncedFolder newSyncedFolder; for (SyncedFolder syncedFolder : syncedFolders) { idsToDelete.add(syncedFolder.getId()); for (int i = 0; i < imageMediaFolders.size(); i++) { if (imageMediaFolders.get(i).absolutePath.equals(syncedFolder.getLocalPath())) { - SyncedFolder imageSyncedFolder = (SyncedFolder) syncedFolder.clone(); - imageSyncedFolder.setType(MediaFolder.IMAGE); - syncedFolderProvider.storeFolderSync(imageSyncedFolder); + newSyncedFolder = (SyncedFolder) syncedFolder.clone(); + newSyncedFolder.setType(MediaFolder.IMAGE); + primaryKey = syncedFolderProvider.storeFolderSync(newSyncedFolder); + Log_OC.i(SQL, "Migrated image synced_folders record: " + + primaryKey + " - " + newSyncedFolder.getLocalPath()); break; } } for (int j = 0; j < videoMediaFolders.size(); j++) { if (videoMediaFolders.get(j).absolutePath.equals(syncedFolder.getLocalPath())) { - SyncedFolder videoSyncedFolder = (SyncedFolder) syncedFolder.clone(); - videoSyncedFolder.setType(MediaFolder.VIDEO); - syncedFolderProvider.storeFolderSync(videoSyncedFolder); + newSyncedFolder = (SyncedFolder) syncedFolder.clone(); + newSyncedFolder.setType(MediaFolder.VIDEO); + primaryKey = syncedFolderProvider.storeFolderSync(newSyncedFolder); + Log_OC.i(SQL, "Migrated video synced_folders record: " + + primaryKey + " - " + newSyncedFolder.getLocalPath()); break; } } From 3d5634f6b13793e93ba23e9dd6c76e20c8a4c7b6 Mon Sep 17 00:00:00 2001 From: AndyScherzinger Date: Fri, 5 May 2017 18:01:27 +0200 Subject: [PATCH 023/206] db v20 is also on master, v21 now has to be used so development doesn't break --- .../providers/FileContentProvider.java | 22 ++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/src/main/java/com/owncloud/android/providers/FileContentProvider.java b/src/main/java/com/owncloud/android/providers/FileContentProvider.java index 28574fe6e201..80dda33f7bbe 100644 --- a/src/main/java/com/owncloud/android/providers/FileContentProvider.java +++ b/src/main/java/com/owncloud/android/providers/FileContentProvider.java @@ -985,7 +985,23 @@ public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { } if (oldVersion < 20 && newVersion >= 20) { - Log_OC.i(SQL, "Entering in the #20 ADD in onUpgrade"); + Log_OC.i(SQL, "Adding arbitrary data table"); + db.beginTransaction(); + try { + createArbitraryData(db); + upgraded = true; + db.setTransactionSuccessful(); + } finally { + db.endTransaction(); + } + } + + if (!upgraded) { + Log_OC.i(SQL, String.format(Locale.ENGLISH, UPGRADE_VERSION_MSG, oldVersion, newVersion)); + } + + if (oldVersion < 21 && newVersion >= 21) { + Log_OC.i(SQL, "Entering in the #21 ADD in onUpgrade"); db.beginTransaction(); try { // add type column default being CUSTOM (0) @@ -994,10 +1010,6 @@ public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { ADD_COLUMN + ProviderTableMeta.SYNCED_FOLDER_TYPE + " INTEGER " + " DEFAULT 0"); - // create arbitrary data table - Log_OC.i(SQL, "Create arbitrary_data table"); - createArbitraryData(db); - // magic to split out existing synced folders in two when needed // otherwise, we migrate them to their proper type (image or video) Log_OC.i(SQL, "Migrate synced_folders records for image/video split"); From 598aea6f982e8466cc0abaaf25aba5bedb262f58 Mon Sep 17 00:00:00 2001 From: AndyScherzinger Date: Fri, 5 May 2017 18:40:07 +0200 Subject: [PATCH 024/206] fix limit variable to prevent NPE --- .../android/providers/FileContentProvider.java | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/src/main/java/com/owncloud/android/providers/FileContentProvider.java b/src/main/java/com/owncloud/android/providers/FileContentProvider.java index 80dda33f7bbe..d6e717455b1c 100644 --- a/src/main/java/com/owncloud/android/providers/FileContentProvider.java +++ b/src/main/java/com/owncloud/android/providers/FileContentProvider.java @@ -675,14 +675,14 @@ public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { Log_OC.i(SQL, "Entering in onUpgrade"); boolean upgraded = false; if (oldVersion == 1 && newVersion >= 2) { - Log_OC.i(SQL, "Entering in the #1 ADD in onUpgrade"); + Log_OC.i(SQL, "Entering in the #2 ADD in onUpgrade"); db.execSQL(ALTER_TABLE + ProviderTableMeta.FILE_TABLE_NAME + ADD_COLUMN + ProviderTableMeta.FILE_KEEP_IN_SYNC + " INTEGER " + " DEFAULT 0"); upgraded = true; } if (oldVersion < 3 && newVersion >= 3) { - Log_OC.i(SQL, "Entering in the #2 ADD in onUpgrade"); + Log_OC.i(SQL, "Entering in the #3 ADD in onUpgrade"); db.beginTransaction(); try { db.execSQL(ALTER_TABLE + ProviderTableMeta.FILE_TABLE_NAME + @@ -702,7 +702,7 @@ public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { } } if (oldVersion < 4 && newVersion >= 4) { - Log_OC.i(SQL, "Entering in the #3 ADD in onUpgrade"); + Log_OC.i(SQL, "Entering in the #4 ADD in onUpgrade"); db.beginTransaction(); try { db.execSQL(ALTER_TABLE + ProviderTableMeta.FILE_TABLE_NAME + @@ -726,7 +726,7 @@ public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { } if (oldVersion < 5 && newVersion >= 5) { - Log_OC.i(SQL, "Entering in the #4 ADD in onUpgrade"); + Log_OC.i(SQL, "Entering in the #5 ADD in onUpgrade"); db.beginTransaction(); try { db.execSQL(ALTER_TABLE + ProviderTableMeta.FILE_TABLE_NAME + @@ -744,7 +744,7 @@ public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { } if (oldVersion < 6 && newVersion >= 6) { - Log_OC.i(SQL, "Entering in the #5 ADD in onUpgrade"); + Log_OC.i(SQL, "Entering in the #6 ADD in onUpgrade"); db.beginTransaction(); try { db.execSQL(ALTER_TABLE + ProviderTableMeta.FILE_TABLE_NAME + @@ -985,7 +985,7 @@ public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { } if (oldVersion < 20 && newVersion >= 20) { - Log_OC.i(SQL, "Adding arbitrary data table"); + Log_OC.i(SQL, "Entering in the #20 Adding arbitrary data table"); db.beginTransaction(); try { createArbitraryData(db); @@ -1017,8 +1017,8 @@ public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { SyncedFolderProvider syncedFolderProvider = new SyncedFolderProvider(contentResolver); - final List imageMediaFolders = MediaProvider.getImageFolders(contentResolver, 0); - final List videoMediaFolders = MediaProvider.getVideoFolders(contentResolver, 0); + final List imageMediaFolders = MediaProvider.getImageFolders(contentResolver, 1); + final List videoMediaFolders = MediaProvider.getVideoFolders(contentResolver, 1); ArrayList idsToDelete = new ArrayList<>(); List syncedFolders = syncedFolderProvider.getSyncedFolders(); @@ -1053,6 +1053,8 @@ public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { upgraded = true; db.setTransactionSuccessful(); + } catch (Throwable t) { + Log_OC.e(TAG, "ERROR!", t); } finally { db.endTransaction(); } From 4461118362b69371542c55ef52addb6e353ad375 Mon Sep 17 00:00:00 2001 From: Mario Danic Date: Fri, 5 May 2017 18:37:28 +0200 Subject: [PATCH 025/206] Some work on a new job for AU Signed-off-by: Mario Danic --- build.gradle | 1 + .../java/com/owncloud/android/MainApp.java | 2 +- .../datamodel/ArbitraryDataProvider.java | 18 +-- .../android/datamodel/FileSystemDataSet.java | 93 +++++++++++++ .../datamodel/FilesystemDataProvider.java | 129 ++++++++++++++++++ .../datamodel/UploadsStorageManager.java | 2 +- .../com/owncloud/android/db/ProviderMeta.java | 12 ++ .../{services => jobs}/AutoUploadJob.java | 2 +- .../{services => jobs}/ContactsBackupJob.java | 5 +- .../{services => jobs}/ContactsImportJob.java | 2 +- .../{services => jobs}/NCJobCreator.java | 4 +- .../android/jobs/NewAutoUploadJob.java | 112 +++++++++++++++ .../providers/FileContentProvider.java | 20 +++ .../AdvancedFileAlterationListener.java | 1 + .../activity/ContactsPreferenceActivity.java | 2 +- .../android/ui/activity/UserInfoActivity.java | 4 +- .../contactsbackup/ContactListFragment.java | 2 +- .../ContactsBackupFragment.java | 2 +- 18 files changed, 392 insertions(+), 21 deletions(-) create mode 100644 src/main/java/com/owncloud/android/datamodel/FileSystemDataSet.java create mode 100644 src/main/java/com/owncloud/android/datamodel/FilesystemDataProvider.java rename src/main/java/com/owncloud/android/{services => jobs}/AutoUploadJob.java (98%) rename src/main/java/com/owncloud/android/{services => jobs}/ContactsBackupJob.java (98%) rename src/main/java/com/owncloud/android/{services => jobs}/ContactsImportJob.java (99%) rename src/main/java/com/owncloud/android/{services => jobs}/NCJobCreator.java (92%) create mode 100644 src/main/java/com/owncloud/android/jobs/NewAutoUploadJob.java diff --git a/build.gradle b/build.gradle index 772940d538ee..7773b1edb9b8 100644 --- a/build.gradle +++ b/build.gradle @@ -193,6 +193,7 @@ dependencies { annotationProcessor 'com.jakewharton:butterknife-compiler:8.4.0' compile 'org.greenrobot:eventbus:3.0.0' compile 'com.googlecode.ez-vcard:ez-vcard:0.10.2' + compile 'org.lukhnos:nnio:0.2' // uncomment for gplay, modified // compile "com.google.firebase:firebase-messaging:${googleLibraryVersion}" diff --git a/src/main/java/com/owncloud/android/MainApp.java b/src/main/java/com/owncloud/android/MainApp.java index 21883a144584..d7846c5088bc 100644 --- a/src/main/java/com/owncloud/android/MainApp.java +++ b/src/main/java/com/owncloud/android/MainApp.java @@ -42,7 +42,7 @@ import com.owncloud.android.lib.common.OwnCloudClientManagerFactory; import com.owncloud.android.lib.common.OwnCloudClientManagerFactory.Policy; import com.owncloud.android.lib.common.utils.Log_OC; -import com.owncloud.android.services.NCJobCreator; +import com.owncloud.android.jobs.NCJobCreator; import com.owncloud.android.services.observer.SyncedFolderObserverService; import com.owncloud.android.ui.activity.Preferences; import com.owncloud.android.ui.activity.WhatsNewActivity; diff --git a/src/main/java/com/owncloud/android/datamodel/ArbitraryDataProvider.java b/src/main/java/com/owncloud/android/datamodel/ArbitraryDataProvider.java index b2f54a8f0dfe..b5e25f9586fe 100644 --- a/src/main/java/com/owncloud/android/datamodel/ArbitraryDataProvider.java +++ b/src/main/java/com/owncloud/android/datamodel/ArbitraryDataProvider.java @@ -59,24 +59,24 @@ public int deleteKeyForAccount(Account account, String key) { } - public void storeOrUpdateKeyValue(Account account, String key, String newValue) { - ArbitraryDataSet data = getArbitraryDataSet(account, key); + public void storeOrUpdateKeyValue(String accountName, String key, String newValue) { + ArbitraryDataSet data = getArbitraryDataSet(accountName, key); if (data == null) { - Log_OC.v(TAG, "Adding arbitrary data with cloud id: " + account.name + " key: " + key + Log_OC.v(TAG, "Adding arbitrary data with cloud id: " + accountName + " key: " + key + " value: " + newValue); ContentValues cv = new ContentValues(); - cv.put(ProviderMeta.ProviderTableMeta.ARBITRARY_DATA_CLOUD_ID, account.name); + cv.put(ProviderMeta.ProviderTableMeta.ARBITRARY_DATA_CLOUD_ID, accountName); cv.put(ProviderMeta.ProviderTableMeta.ARBITRARY_DATA_KEY, key); cv.put(ProviderMeta.ProviderTableMeta.ARBITRARY_DATA_VALUE, newValue); Uri result = contentResolver.insert(ProviderMeta.ProviderTableMeta.CONTENT_URI_ARBITRARY_DATA, cv); if (result == null) { - Log_OC.v(TAG, "Failed to store arbitrary data with cloud id: " + account.name + " key: " + key + Log_OC.v(TAG, "Failed to store arbitrary data with cloud id: " + accountName + " key: " + key + " value: " + newValue); } } else { - Log_OC.v(TAG, "Updating arbitrary data with cloud id: " + account.name + " key: " + key + Log_OC.v(TAG, "Updating arbitrary data with cloud id: " + accountName + " key: " + key + " value: " + newValue); ContentValues cv = new ContentValues(); cv.put(ProviderMeta.ProviderTableMeta.ARBITRARY_DATA_CLOUD_ID, data.getCloudId()); @@ -91,7 +91,7 @@ public void storeOrUpdateKeyValue(Account account, String key, String newValue) ); if (result == 0) { - Log_OC.v(TAG, "Failed to update arbitrary data with cloud id: " + account.name + " key: " + key + Log_OC.v(TAG, "Failed to update arbitrary data with cloud id: " + accountName + " key: " + key + " value: " + newValue); } } @@ -215,13 +215,13 @@ public String getValue(String accountName, String key) { return ""; } - private ArbitraryDataSet getArbitraryDataSet(Account account, String key) { + private ArbitraryDataSet getArbitraryDataSet(String accountName, String key) { Cursor cursor = contentResolver.query( ProviderMeta.ProviderTableMeta.CONTENT_URI_ARBITRARY_DATA, null, ProviderMeta.ProviderTableMeta.ARBITRARY_DATA_CLOUD_ID + " = ? and " + ProviderMeta.ProviderTableMeta.ARBITRARY_DATA_KEY + " = ?", - new String[]{account.name, key}, + new String[]{accountName, key}, null ); diff --git a/src/main/java/com/owncloud/android/datamodel/FileSystemDataSet.java b/src/main/java/com/owncloud/android/datamodel/FileSystemDataSet.java new file mode 100644 index 000000000000..999658dbedb5 --- /dev/null +++ b/src/main/java/com/owncloud/android/datamodel/FileSystemDataSet.java @@ -0,0 +1,93 @@ +/** + * Nextcloud Android client application + * + * @author Mario Danic + * Copyright (C) 2017 Mario Danic + * Copyright (C) 2017 Nextcloud + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE + * License as published by the Free Software Foundation; either + * version 3 of the License, or 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 . + */ +package com.owncloud.android.datamodel; + + +public class FileSystemDataSet { + + private int id; + private String localPath; + private long modifiedAt; + private boolean isFolder; + private boolean isSentForUpload; + private long foundAt; + + public FileSystemDataSet() { + } + + public FileSystemDataSet(int id, String localPath, long modifiedAt, boolean isFolder, boolean isSentForUpload, long foundAt) { + this.id = id; + this.localPath = localPath; + this.modifiedAt = modifiedAt; + this.isFolder = isFolder; + this.isSentForUpload = isSentForUpload; + this.foundAt = foundAt; + } + + public int getId() { + return id; + } + + public void setId(int id) { + this.id = id; + } + + public String getLocalPath() { + return localPath; + } + + public void setLocalPath(String localPath) { + this.localPath = localPath; + } + + public long getModifiedAt() { + return modifiedAt; + } + + public void setModifiedAt(long modifiedAt) { + this.modifiedAt = modifiedAt; + } + + public boolean isFolder() { + return isFolder; + } + + public void setFolder(boolean folder) { + isFolder = folder; + } + + public long getFoundAt() { + return foundAt; + } + + public void setFoundAt(long foundAt) { + this.foundAt = foundAt; + } + + public boolean isSentForUpload() { + return isSentForUpload; + } + + public void setSentForUpload(boolean sentForUpload) { + isSentForUpload = sentForUpload; + } + +} diff --git a/src/main/java/com/owncloud/android/datamodel/FilesystemDataProvider.java b/src/main/java/com/owncloud/android/datamodel/FilesystemDataProvider.java new file mode 100644 index 000000000000..e275498e8e50 --- /dev/null +++ b/src/main/java/com/owncloud/android/datamodel/FilesystemDataProvider.java @@ -0,0 +1,129 @@ +/** + * Nextcloud Android client application + * + * Copyright (C) 2017 Mario Danic + * Copyright (C) 2017 Nextcloud. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE + * License as published by the Free Software Foundation; either + * version 3 of the License, or 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 . + */ +package com.owncloud.android.datamodel; + +import android.content.ContentResolver; +import android.content.ContentValues; +import android.database.Cursor; +import android.net.Uri; + +import com.owncloud.android.db.ProviderMeta; +import com.owncloud.android.lib.common.utils.Log_OC; + +public class FilesystemDataProvider { + + static private final String TAG = FilesystemDataProvider.class.getSimpleName(); + + private ContentResolver contentResolver; + + public FilesystemDataProvider(ContentResolver contentResolver) { + if (contentResolver == null) { + throw new IllegalArgumentException("Cannot create an instance with a NULL contentResolver"); + } + this.contentResolver = contentResolver; + } + + public void storeOrUpdateFileValue(String localPath, long modifiedAt, boolean isFolder, boolean sentForUpload) { + FileSystemDataSet data = getFilesystemDataSet(localPath); + + int isFolderValue = 0; + if (isFolder) { + isFolderValue = 1; + } + + int isSentForUpload = 0; + if (sentForUpload) { + isSentForUpload = 1; + } + + ContentValues cv = new ContentValues(); + cv.put(ProviderMeta.ProviderTableMeta.FILESYSTEM_FILE_LOCAL_PATH, localPath); + cv.put(ProviderMeta.ProviderTableMeta.FILESYSTEM_FILE_MODIFIED, modifiedAt); + cv.put(ProviderMeta.ProviderTableMeta.FILESYSTEM_FILE_IS_FOLDER, isFolderValue); + cv.put(ProviderMeta.ProviderTableMeta.FILESYSTEM_FILE_FOUND_RECENTLY, System.currentTimeMillis()); + cv.put(ProviderMeta.ProviderTableMeta.FILESYSTEM_FILE_SENT_FOR_UPLOAD, isSentForUpload); + + if (data == null) { + + Uri result = contentResolver.insert(ProviderMeta.ProviderTableMeta.CONTENT_URI_FILESYSTEM, cv); + + if (result == null) { + Log_OC.v(TAG, "Failed to insert filesystem data with local path: " + localPath); + } + } else { + + int result = contentResolver.update( + ProviderMeta.ProviderTableMeta.CONTENT_URI_FILESYSTEM, + cv, + ProviderMeta.ProviderTableMeta._ID + "=?", + new String[]{String.valueOf(data.getId())} + ); + + if (result == 0) { + Log_OC.v(TAG, "Failed to update filesystem data with local path: " + localPath); + } + } + } + + private FileSystemDataSet getFilesystemDataSet(String localPathParam) { + Cursor cursor = contentResolver.query( + ProviderMeta.ProviderTableMeta.CONTENT_URI_FILESYSTEM, + null, + ProviderMeta.ProviderTableMeta.FILESYSTEM_FILE_LOCAL_PATH + " = ?", + new String[]{localPathParam}, + null + ); + + FileSystemDataSet dataSet = null; + if (cursor != null) { + if (cursor.moveToFirst()) { + int id = cursor.getInt(cursor.getColumnIndex(ProviderMeta.ProviderTableMeta._ID)); + String localPath = cursor.getString(cursor.getColumnIndex( + ProviderMeta.ProviderTableMeta.FILESYSTEM_FILE_LOCAL_PATH)); + long modifiedAt = cursor.getLong(cursor.getColumnIndex( + ProviderMeta.ProviderTableMeta.FILESYSTEM_FILE_MODIFIED)); + boolean isFolder = false; + if (cursor.getInt(cursor.getColumnIndex( + ProviderMeta.ProviderTableMeta.FILESYSTEM_FILE_MODIFIED)) != 0) { + isFolder = true; + } + long foundAt = cursor.getLong(cursor.getColumnIndex(ProviderMeta. + ProviderTableMeta.FILESYSTEM_FILE_FOUND_RECENTLY)); + + boolean isSentForUpload = false; + if (cursor.getInt(cursor.getColumnIndex( + ProviderMeta.ProviderTableMeta.FILESYSTEM_FILE_SENT_FOR_UPLOAD)) != 0) { + isSentForUpload = true; + } + + if (id == -1) { + Log_OC.e(TAG, "Arbitrary value could not be created from cursor"); + } else { + dataSet = new FileSystemDataSet(id, localPath, modifiedAt, isFolder, isSentForUpload, foundAt); + } + } + cursor.close(); + } else { + Log_OC.e(TAG, "DB error restoring arbitrary values."); + } + + return dataSet; + } +} diff --git a/src/main/java/com/owncloud/android/datamodel/UploadsStorageManager.java b/src/main/java/com/owncloud/android/datamodel/UploadsStorageManager.java index 2808a93abdfa..afc2e20044a5 100644 --- a/src/main/java/com/owncloud/android/datamodel/UploadsStorageManager.java +++ b/src/main/java/com/owncloud/android/datamodel/UploadsStorageManager.java @@ -37,7 +37,7 @@ import com.owncloud.android.lib.common.operations.RemoteOperationResult; import com.owncloud.android.lib.common.utils.Log_OC; import com.owncloud.android.operations.UploadFileOperation; -import com.owncloud.android.services.AutoUploadJob; +import com.owncloud.android.jobs.AutoUploadJob; import java.util.ArrayList; import java.util.Calendar; diff --git a/src/main/java/com/owncloud/android/db/ProviderMeta.java b/src/main/java/com/owncloud/android/db/ProviderMeta.java index cc10fe13867b..299da9a95eb7 100644 --- a/src/main/java/com/owncloud/android/db/ProviderMeta.java +++ b/src/main/java/com/owncloud/android/db/ProviderMeta.java @@ -46,6 +46,7 @@ static public class ProviderTableMeta implements BaseColumns { public static final String EXTERNAL_LINKS_TABLE_NAME = "external_links"; public static final String ARBITRARY_DATA_TABLE_NAME = "arbitrary_data"; public static final String VIRTUAL_TABLE_NAME = "virtual"; + public static final String FILESYSTEM_TABLE_NAME = "filesystem"; private static final String CONTENT_PREFIX = "content://"; @@ -68,6 +69,9 @@ static public class ProviderTableMeta implements BaseColumns { public static final Uri CONTENT_URI_ARBITRARY_DATA = Uri.parse(CONTENT_PREFIX + MainApp.getAuthority() + "/arbitrary_data"); public static final Uri CONTENT_URI_VIRTUAL = Uri.parse(CONTENT_PREFIX + MainApp.getAuthority() + "/virtual"); + public static final Uri CONTENT_URI_FILESYSTEM = Uri.parse(CONTENT_PREFIX + + MainApp.getAuthority() + "/filesystem"); + public static final String CONTENT_TYPE = "vnd.android.cursor.dir/vnd.owncloud.file"; public static final String CONTENT_TYPE_ITEM = "vnd.android.cursor.item/vnd.owncloud.file"; @@ -193,8 +197,16 @@ static public class ProviderTableMeta implements BaseColumns { public static final String ARBITRARY_DATA_KEY = "key"; public static final String ARBITRARY_DATA_VALUE = "value"; + // Columns of virtual public static final String VIRTUAL_TYPE = "type"; public static final String VIRTUAL_OCFILE_ID = "ocfile_id"; + + // Columns of filesystem data table + public static final String FILESYSTEM_FILE_LOCAL_PATH = "local_path"; + public static final String FILESYSTEM_FILE_MODIFIED = "modified_at"; + public static final String FILESYSTEM_FILE_IS_FOLDER = "is_folder"; + public static final String FILESYSTEM_FILE_FOUND_RECENTLY = "found_at"; + public static final String FILESYSTEM_FILE_SENT_FOR_UPLOAD = "upload_triggered"; } } \ No newline at end of file diff --git a/src/main/java/com/owncloud/android/services/AutoUploadJob.java b/src/main/java/com/owncloud/android/jobs/AutoUploadJob.java similarity index 98% rename from src/main/java/com/owncloud/android/services/AutoUploadJob.java rename to src/main/java/com/owncloud/android/jobs/AutoUploadJob.java index 7ae6502f6e4e..4faa1133f41d 100644 --- a/src/main/java/com/owncloud/android/services/AutoUploadJob.java +++ b/src/main/java/com/owncloud/android/jobs/AutoUploadJob.java @@ -20,7 +20,7 @@ * License along with this program. If not, see . */ -package com.owncloud.android.services; +package com.owncloud.android.jobs; import android.accounts.Account; import android.content.Context; diff --git a/src/main/java/com/owncloud/android/services/ContactsBackupJob.java b/src/main/java/com/owncloud/android/jobs/ContactsBackupJob.java similarity index 98% rename from src/main/java/com/owncloud/android/services/ContactsBackupJob.java rename to src/main/java/com/owncloud/android/jobs/ContactsBackupJob.java index a81140d0ee2f..643ce75432e2 100644 --- a/src/main/java/com/owncloud/android/services/ContactsBackupJob.java +++ b/src/main/java/com/owncloud/android/jobs/ContactsBackupJob.java @@ -19,7 +19,7 @@ * along with this program. If not, see . */ -package com.owncloud.android.services; +package com.owncloud.android.jobs; import android.accounts.Account; import android.content.ComponentName; @@ -44,6 +44,7 @@ import com.owncloud.android.files.services.FileUploader; import com.owncloud.android.lib.common.utils.Log_OC; import com.owncloud.android.operations.UploadFileOperation; +import com.owncloud.android.services.OperationsService; import com.owncloud.android.ui.activity.ContactsPreferenceActivity; import java.io.File; @@ -97,7 +98,7 @@ protected Result onRunJob(Params params) { OperationsService.BIND_AUTO_CREATE); // store execution date - arbitraryDataProvider.storeOrUpdateKeyValue(account, + arbitraryDataProvider.storeOrUpdateKeyValue(account.name, ContactsPreferenceActivity.PREFERENCE_CONTACTS_LAST_BACKUP, String.valueOf(Calendar.getInstance().getTimeInMillis())); } else { diff --git a/src/main/java/com/owncloud/android/services/ContactsImportJob.java b/src/main/java/com/owncloud/android/jobs/ContactsImportJob.java similarity index 99% rename from src/main/java/com/owncloud/android/services/ContactsImportJob.java rename to src/main/java/com/owncloud/android/jobs/ContactsImportJob.java index 3380d34d6290..247902711588 100644 --- a/src/main/java/com/owncloud/android/services/ContactsImportJob.java +++ b/src/main/java/com/owncloud/android/jobs/ContactsImportJob.java @@ -19,7 +19,7 @@ * along with this program. If not, see . */ -package com.owncloud.android.services; +package com.owncloud.android.jobs; import android.database.Cursor; import android.net.Uri; diff --git a/src/main/java/com/owncloud/android/services/NCJobCreator.java b/src/main/java/com/owncloud/android/jobs/NCJobCreator.java similarity index 92% rename from src/main/java/com/owncloud/android/services/NCJobCreator.java rename to src/main/java/com/owncloud/android/jobs/NCJobCreator.java index c3d2cd418256..0d6af75f67b4 100644 --- a/src/main/java/com/owncloud/android/services/NCJobCreator.java +++ b/src/main/java/com/owncloud/android/jobs/NCJobCreator.java @@ -18,7 +18,7 @@ * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ -package com.owncloud.android.services; +package com.owncloud.android.jobs; import com.evernote.android.job.Job; import com.evernote.android.job.JobCreator; @@ -39,6 +39,8 @@ public Job create(String tag) { return new ContactsImportJob(); case AccountRemovalJob.TAG: return new AccountRemovalJob(); + case NewAutoUploadJob.TAG: + return new NewAutoUploadJob(); default: return null; } diff --git a/src/main/java/com/owncloud/android/jobs/NewAutoUploadJob.java b/src/main/java/com/owncloud/android/jobs/NewAutoUploadJob.java new file mode 100644 index 000000000000..e22f44aa66b7 --- /dev/null +++ b/src/main/java/com/owncloud/android/jobs/NewAutoUploadJob.java @@ -0,0 +1,112 @@ +/** + * Nextcloud Android client application + * + * @author Mario Danic + * Copyright (C) 2017 Mario Danic + * Copyright (C) 2017 Nextcloud + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE + * License as published by the Free Software Foundation; either + * version 3 of the License, or 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 . + */ + +package com.owncloud.android.jobs; + +import android.content.ContentResolver; +import android.content.Context; +import android.support.annotation.NonNull; +import android.util.Log; + +import com.evernote.android.job.Job; +import com.owncloud.android.MainApp; +import com.owncloud.android.datamodel.ArbitraryDataProvider; +import com.owncloud.android.datamodel.FilesystemDataProvider; +import com.owncloud.android.datamodel.SyncedFolder; +import com.owncloud.android.datamodel.SyncedFolderProvider; + +import org.lukhnos.nnio.file.FileVisitResult; +import org.lukhnos.nnio.file.Files; +import org.lukhnos.nnio.file.Path; +import org.lukhnos.nnio.file.Paths; +import org.lukhnos.nnio.file.SimpleFileVisitor; +import org.lukhnos.nnio.file.attribute.BasicFileAttributes; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; + +public class NewAutoUploadJob extends Job { + public static final String TAG = "NewAutoUploadJob"; + + private static final String GLOBAL = "global"; + private static final String LAST_AUTOUPLOAD_JOB_RUN = "last_autoupload_job_run"; + + + @NonNull + @Override + protected Result onRunJob(Params params) { + final Context context = MainApp.getAppContext(); + final ContentResolver contentResolver = context.getContentResolver(); + + // Create all the providers we'll need + ArbitraryDataProvider arbitraryDataProvider = new ArbitraryDataProvider(contentResolver); + final FilesystemDataProvider filesystemDataProvider = new FilesystemDataProvider(contentResolver); + SyncedFolderProvider syncedFolderProvider = new SyncedFolderProvider(contentResolver); + + // Store when we started doing this + arbitraryDataProvider.storeOrUpdateKeyValue(GLOBAL, LAST_AUTOUPLOAD_JOB_RUN, + Long.toString(System.currentTimeMillis())); + + List syncedFolders = syncedFolderProvider.getSyncedFolders(); + List syncedFoldersToDelete = new ArrayList<>(); + + for (SyncedFolder syncedFolder : syncedFolders) { + for (SyncedFolder secondarySyncedFolder : syncedFolders) { + if (syncedFolder != secondarySyncedFolder) { + if (secondarySyncedFolder.getLocalPath().startsWith(syncedFolder.getLocalPath()) && + !syncedFoldersToDelete.contains(secondarySyncedFolder)) { + syncedFoldersToDelete.add(secondarySyncedFolder); + } + } + } + } + + // delete all the folders from the list that we won't traverse + syncedFolders.removeAll(syncedFoldersToDelete); + + for (int i = 0; i < syncedFolders.size(); i++) { + Path path = Paths.get(syncedFolders.get(i).getLocalPath()); + + try { + Files.walkFileTree(path, new SimpleFileVisitor() { + @Override + public FileVisitResult visitFile(Path path, BasicFileAttributes attrs) throws IOException { + + filesystemDataProvider.storeOrUpdateFileValue(path.toAbsolutePath().toString(), + attrs.lastModifiedTime().toMillis(), path.toFile().isDirectory(), false); + + return FileVisitResult.CONTINUE; + } + + @Override + public FileVisitResult visitFileFailed(Path file, IOException exc) { + return FileVisitResult.CONTINUE; + } + + }); + } catch (IOException e) { + Log.d(TAG, "Something went wrong while indexing files for auto upload"); + } + } + return Result.SUCCESS; + } +} diff --git a/src/main/java/com/owncloud/android/providers/FileContentProvider.java b/src/main/java/com/owncloud/android/providers/FileContentProvider.java index d6e717455b1c..05d1445cf19a 100644 --- a/src/main/java/com/owncloud/android/providers/FileContentProvider.java +++ b/src/main/java/com/owncloud/android/providers/FileContentProvider.java @@ -668,6 +668,9 @@ public void onCreate(SQLiteDatabase db) { // Create virtual table createVirtualTable(db); + + // Create filesystem table + createFileSystemTable(db); } @Override @@ -1010,6 +1013,11 @@ public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { ADD_COLUMN + ProviderTableMeta.SYNCED_FOLDER_TYPE + " INTEGER " + " DEFAULT 0"); + + // create Filesystem table + Log_OC.i(SQL, "Create filesystem table"); + createFileSystemTable(db); + // magic to split out existing synced folders in two when needed // otherwise, we migrate them to their proper type (image or video) Log_OC.i(SQL, "Migrate synced_folders records for image/video split"); @@ -1277,6 +1285,18 @@ private void createVirtualTable(SQLiteDatabase db) { ); } + private void createFileSystemTable(SQLiteDatabase db) { + db.execSQL("CREATE TABLE " + ProviderTableMeta.FILESYSTEM_TABLE_NAME + "(" + + ProviderTableMeta._ID + " INTEGER PRIMARY KEY, " // id + + ProviderTableMeta.FILESYSTEM_FILE_LOCAL_PATH + " TEXT, " + + ProviderTableMeta.FILESYSTEM_FILE_IS_FOLDER + " INTEGER, " + + ProviderTableMeta.FILESYSTEM_FILE_FOUND_RECENTLY + " LONG, " + + ProviderTableMeta.FILESYSTEM_FILE_SENT_FOR_UPLOAD + " INTEGER, " + + ProviderTableMeta.FILESYSTEM_FILE_MODIFIED + " LONG );" + ); + } + + /** * Version 10 of database does not modify its scheme. It coincides with the upgrade of the ownCloud account names * structure to include in it the path to the server instance. Updating the account names and path to local files diff --git a/src/main/java/com/owncloud/android/services/AdvancedFileAlterationListener.java b/src/main/java/com/owncloud/android/services/AdvancedFileAlterationListener.java index 9c8fa7fcc571..9c1f88610543 100644 --- a/src/main/java/com/owncloud/android/services/AdvancedFileAlterationListener.java +++ b/src/main/java/com/owncloud/android/services/AdvancedFileAlterationListener.java @@ -32,6 +32,7 @@ import com.owncloud.android.datamodel.ArbitraryDataProvider; import com.owncloud.android.datamodel.OCFile; import com.owncloud.android.datamodel.SyncedFolder; +import com.owncloud.android.jobs.AutoUploadJob; import com.owncloud.android.files.services.FileUploader; import com.owncloud.android.lib.common.utils.Log_OC; import com.owncloud.android.ui.activity.Preferences; diff --git a/src/main/java/com/owncloud/android/ui/activity/ContactsPreferenceActivity.java b/src/main/java/com/owncloud/android/ui/activity/ContactsPreferenceActivity.java index 01ebfd8a8f12..3a52a18124f6 100644 --- a/src/main/java/com/owncloud/android/ui/activity/ContactsPreferenceActivity.java +++ b/src/main/java/com/owncloud/android/ui/activity/ContactsPreferenceActivity.java @@ -37,7 +37,7 @@ import com.owncloud.android.R; import com.owncloud.android.datamodel.OCFile; import com.owncloud.android.lib.common.utils.Log_OC; -import com.owncloud.android.services.ContactsBackupJob; +import com.owncloud.android.jobs.ContactsBackupJob; import com.owncloud.android.ui.fragment.FileFragment; import com.owncloud.android.ui.fragment.contactsbackup.ContactListFragment; import com.owncloud.android.ui.fragment.contactsbackup.ContactsBackupFragment; diff --git a/src/main/java/com/owncloud/android/ui/activity/UserInfoActivity.java b/src/main/java/com/owncloud/android/ui/activity/UserInfoActivity.java index 3f18ad26a037..b0da2edcbb64 100644 --- a/src/main/java/com/owncloud/android/ui/activity/UserInfoActivity.java +++ b/src/main/java/com/owncloud/android/ui/activity/UserInfoActivity.java @@ -419,7 +419,7 @@ public void onClick(DialogInterface dialogInterface, int i) { ArbitraryDataProvider arbitraryDataProvider = new ArbitraryDataProvider( contentResolver); - arbitraryDataProvider.storeOrUpdateKeyValue(account, + arbitraryDataProvider.storeOrUpdateKeyValue(account.name, ContactsPreferenceActivity.PREFERENCE_CONTACTS_AUTOMATIC_BACKUP, "false"); @@ -433,7 +433,7 @@ public void onClick(DialogInterface dialogInterface, int i) { PushConfigurationState pushArbitraryData = gson.fromJson(arbitraryDataPushString, PushConfigurationState.class); pushArbitraryData.setShouldBeDeleted(true); - arbitraryDataProvider.storeOrUpdateKeyValue(account, PushUtils.KEY_PUSH, + arbitraryDataProvider.storeOrUpdateKeyValue(account.name, PushUtils.KEY_PUSH, gson.toJson(pushArbitraryData)); EventBus.getDefault().post(new TokenPushEvent()); } diff --git a/src/main/java/com/owncloud/android/ui/fragment/contactsbackup/ContactListFragment.java b/src/main/java/com/owncloud/android/ui/fragment/contactsbackup/ContactListFragment.java index 2b8c16d150c0..dcda8f6b8420 100644 --- a/src/main/java/com/owncloud/android/ui/fragment/contactsbackup/ContactListFragment.java +++ b/src/main/java/com/owncloud/android/ui/fragment/contactsbackup/ContactListFragment.java @@ -67,7 +67,7 @@ import com.owncloud.android.datamodel.OCFile; import com.owncloud.android.files.services.FileDownloader; import com.owncloud.android.lib.common.utils.Log_OC; -import com.owncloud.android.services.ContactsImportJob; +import com.owncloud.android.jobs.ContactsImportJob; import com.owncloud.android.ui.TextDrawable; import com.owncloud.android.ui.activity.ContactsPreferenceActivity; import com.owncloud.android.ui.events.VCardToggleEvent; diff --git a/src/main/java/com/owncloud/android/ui/fragment/contactsbackup/ContactsBackupFragment.java b/src/main/java/com/owncloud/android/ui/fragment/contactsbackup/ContactsBackupFragment.java index 959d87de91d0..0d91671d5dcc 100644 --- a/src/main/java/com/owncloud/android/ui/fragment/contactsbackup/ContactsBackupFragment.java +++ b/src/main/java/com/owncloud/android/ui/fragment/contactsbackup/ContactsBackupFragment.java @@ -338,7 +338,7 @@ private void setAutomaticBackup(final boolean bool) { contactsPreferenceActivity.getAccount()); } - arbitraryDataProvider.storeOrUpdateKeyValue(account, PREFERENCE_CONTACTS_AUTOMATIC_BACKUP, + arbitraryDataProvider.storeOrUpdateKeyValue(account.name, PREFERENCE_CONTACTS_AUTOMATIC_BACKUP, String.valueOf(bool)); } From f8462024f6bbdea50dfe5fe6016f490523a39d9c Mon Sep 17 00:00:00 2001 From: Mario Danic Date: Fri, 5 May 2017 18:56:27 +0200 Subject: [PATCH 026/206] Add counting magic Signed-off-by: Mario Danic --- .../datamodel/FilesystemDataProvider.java | 21 +++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/src/main/java/com/owncloud/android/datamodel/FilesystemDataProvider.java b/src/main/java/com/owncloud/android/datamodel/FilesystemDataProvider.java index e275498e8e50..2688ee6ede51 100644 --- a/src/main/java/com/owncloud/android/datamodel/FilesystemDataProvider.java +++ b/src/main/java/com/owncloud/android/datamodel/FilesystemDataProvider.java @@ -40,6 +40,27 @@ public FilesystemDataProvider(ContentResolver contentResolver) { this.contentResolver = contentResolver; } + public long countFilesThatNeedUploadInFolder(String localPath) { + String likeParam = localPath + "%"; + + Cursor cursor = contentResolver.query( + ProviderMeta.ProviderTableMeta.CONTENT_URI_FILESYSTEM, + new String[] {"count(*)"}, + ProviderMeta.ProviderTableMeta.FILESYSTEM_FILE_LOCAL_PATH + " LIKE ?", + new String[]{likeParam}, + null); + + if (cursor.getCount() == 0) { + cursor.close(); + return 0; + } else { + cursor.moveToFirst(); + int result = cursor.getInt(0); + cursor.close(); + return result; + } + } + public void storeOrUpdateFileValue(String localPath, long modifiedAt, boolean isFolder, boolean sentForUpload) { FileSystemDataSet data = getFilesystemDataSet(localPath); From 6ea762a59cb8e11fac8e1f1af0f623e68aba217f Mon Sep 17 00:00:00 2001 From: Mario Danic Date: Fri, 5 May 2017 18:57:40 +0200 Subject: [PATCH 027/206] Change to 1.8 Signed-off-by: Mario Danic --- build.gradle | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/build.gradle b/build.gradle index 7773b1edb9b8..4fd795cf75e8 100644 --- a/build.gradle +++ b/build.gradle @@ -112,8 +112,8 @@ android { } compileOptions { - sourceCompatibility JavaVersion.VERSION_1_7 - targetCompatibility JavaVersion.VERSION_1_7 + sourceCompatibility JavaVersion.VERSION_1_8 + targetCompatibility JavaVersion.VERSION_1_8 } packagingOptions { From 460024174ca57643652961edcb1dda00384116db Mon Sep 17 00:00:00 2001 From: Mario Danic Date: Fri, 5 May 2017 19:17:02 +0200 Subject: [PATCH 028/206] Fix build Signed-off-by: Mario Danic --- build.gradle | 9 ++------- gradle/wrapper/gradle-wrapper.properties | 2 +- 2 files changed, 3 insertions(+), 8 deletions(-) diff --git a/build.gradle b/build.gradle index 4fd795cf75e8..ccdc5bf993d4 100644 --- a/build.gradle +++ b/build.gradle @@ -13,7 +13,7 @@ buildscript { } } dependencies { - classpath 'com.android.tools.build:gradle:2.3.2' + classpath 'com.android.tools.build:gradle:2.4.0-alpha7' classpath 'com.google.gms:google-services:3.0.0' } } @@ -63,7 +63,7 @@ android { } compileSdkVersion 25 - buildToolsVersion '25.0.0' + buildToolsVersion '25.0.2' defaultConfig { testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" @@ -111,11 +111,6 @@ android { preDexLibraries = preDexEnabled && !travisBuild } - compileOptions { - sourceCompatibility JavaVersion.VERSION_1_8 - targetCompatibility JavaVersion.VERSION_1_8 - } - packagingOptions { exclude 'META-INF/LICENSE.txt' exclude 'META-INF/LICENSE' diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 41b4d0e9c9cb..6c2b37cddb23 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,4 +1,4 @@ -#Wed Mar 15 00:10:20 CET 2017 +#Fri May 05 19:09:31 CEST 2017 distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME From 5ea5f169af5e1e08b28969d8ffbde2e57f4fcac1 Mon Sep 17 00:00:00 2001 From: Mario Danic Date: Fri, 5 May 2017 19:24:42 +0200 Subject: [PATCH 029/206] Fix issues Signed-off-by: Mario Danic --- build.gradle | 2 +- .../java/com/owncloud/android/jobs/NewAutoUploadJob.java | 9 ++++----- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/build.gradle b/build.gradle index ccdc5bf993d4..2b6befb615ed 100644 --- a/build.gradle +++ b/build.gradle @@ -63,7 +63,7 @@ android { } compileSdkVersion 25 - buildToolsVersion '25.0.2' + buildToolsVersion '25.0.0' defaultConfig { testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" diff --git a/src/main/java/com/owncloud/android/jobs/NewAutoUploadJob.java b/src/main/java/com/owncloud/android/jobs/NewAutoUploadJob.java index e22f44aa66b7..277cee0bfd38 100644 --- a/src/main/java/com/owncloud/android/jobs/NewAutoUploadJob.java +++ b/src/main/java/com/owncloud/android/jobs/NewAutoUploadJob.java @@ -71,11 +71,10 @@ protected Result onRunJob(Params params) { for (SyncedFolder syncedFolder : syncedFolders) { for (SyncedFolder secondarySyncedFolder : syncedFolders) { - if (syncedFolder != secondarySyncedFolder) { - if (secondarySyncedFolder.getLocalPath().startsWith(syncedFolder.getLocalPath()) && - !syncedFoldersToDelete.contains(secondarySyncedFolder)) { - syncedFoldersToDelete.add(secondarySyncedFolder); - } + if (!syncedFolder.equals(secondarySyncedFolder) && + secondarySyncedFolder.getLocalPath().startsWith(syncedFolder.getLocalPath()) && + !syncedFoldersToDelete.contains(secondarySyncedFolder)) { + syncedFoldersToDelete.add(secondarySyncedFolder); } } } From 9178e23bf7333f1f19935faee3887a70f4a98cc9 Mon Sep 17 00:00:00 2001 From: Mario Danic Date: Fri, 5 May 2017 20:00:52 +0200 Subject: [PATCH 030/206] Attempt to fix build Signed-off-by: Mario Danic --- build.gradle | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/build.gradle b/build.gradle index 2b6befb615ed..e5e64615bc20 100644 --- a/build.gradle +++ b/build.gradle @@ -167,6 +167,10 @@ android { } check.dependsOn 'checkstyle', 'findbugs', 'pmd', 'lint' + compileOptions { + sourceCompatibility JavaVersion.VERSION_1_8 + targetCompatibility JavaVersion.VERSION_1_8 + } } dependencies { From aa06b069662d776523e77bf37ac621a0e7e3092d Mon Sep 17 00:00:00 2001 From: Mario Danic Date: Fri, 5 May 2017 20:38:49 +0200 Subject: [PATCH 031/206] Clean up code Signed-off-by: Mario Danic --- src/main/AndroidManifest.xml | 2 +- .../java/com/owncloud/android/MainApp.java | 39 +- .../datamodel/SyncedFolderProvider.java | 18 - .../AdvancedFileAlterationListener.java | 238 ----------- .../AdvancedFileAlterationObserver.java | 402 ------------------ .../observer/SyncedFolderObserverService.java | 180 -------- 6 files changed, 2 insertions(+), 877 deletions(-) delete mode 100644 src/main/java/com/owncloud/android/services/AdvancedFileAlterationListener.java delete mode 100644 src/main/java/com/owncloud/android/services/observer/AdvancedFileAlterationObserver.java delete mode 100644 src/main/java/com/owncloud/android/services/observer/SyncedFolderObserverService.java diff --git a/src/main/AndroidManifest.xml b/src/main/AndroidManifest.xml index 660bdfd93f0a..d25b0660214e 100644 --- a/src/main/AndroidManifest.xml +++ b/src/main/AndroidManifest.xml @@ -131,7 +131,7 @@ android:name="android.accounts.AccountAuthenticator" android:resource="@xml/authenticator" /> - + diff --git a/src/main/java/com/owncloud/android/MainApp.java b/src/main/java/com/owncloud/android/MainApp.java index d7846c5088bc..39567c078fa2 100644 --- a/src/main/java/com/owncloud/android/MainApp.java +++ b/src/main/java/com/owncloud/android/MainApp.java @@ -20,16 +20,12 @@ package com.owncloud.android; import android.app.Activity; -import android.content.ComponentName; import android.content.Context; -import android.content.Intent; -import android.content.ServiceConnection; import android.content.SharedPreferences; import android.content.pm.PackageInfo; import android.content.pm.PackageManager; import android.os.Bundle; import android.os.Environment; -import android.os.IBinder; import android.support.multidex.MultiDexApplication; import android.support.v4.util.Pair; @@ -39,11 +35,10 @@ import com.owncloud.android.datamodel.SyncedFolderProvider; import com.owncloud.android.datamodel.ThumbnailsCacheManager; import com.owncloud.android.db.PreferenceManager; +import com.owncloud.android.jobs.NCJobCreator; import com.owncloud.android.lib.common.OwnCloudClientManagerFactory; import com.owncloud.android.lib.common.OwnCloudClientManagerFactory.Policy; import com.owncloud.android.lib.common.utils.Log_OC; -import com.owncloud.android.jobs.NCJobCreator; -import com.owncloud.android.services.observer.SyncedFolderObserverService; import com.owncloud.android.ui.activity.Preferences; import com.owncloud.android.ui.activity.WhatsNewActivity; import com.owncloud.android.utils.AnalyticsUtils; @@ -79,8 +74,6 @@ public class MainApp extends MultiDexApplication { private static boolean mOnlyOnDevice = false; - private static SyncedFolderObserverService mObserverService; - @SuppressWarnings("unused") private boolean mBound; @@ -121,12 +114,6 @@ public void onCreate() { cleanOldEntries(); updateAutoUploadEntries(); - Log_OC.d("SyncedFolderObserverService", "Start service SyncedFolderObserverService"); - Intent i = new Intent(this, SyncedFolderObserverService.class); - startService(i); - bindService(i, syncedFolderObserverServiceConnection, Context.BIND_AUTO_CREATE); - - // register global protection with pass code registerActivityLifecycleCallbacks(new ActivityLifecycleCallbacks() { @@ -245,10 +232,6 @@ public static boolean isOnlyOnDevice() { return mOnlyOnDevice; } - public static SyncedFolderObserverService getSyncedFolderObserverService() { - return mObserverService; - } - // user agent public static String getUserAgent() { String appString = getAppContext().getResources().getString(R.string.user_agent); @@ -313,24 +296,4 @@ private void cleanOldEntries() { } } } - - /** - * Defines callbacks for service binding, passed to bindService() - */ - private ServiceConnection syncedFolderObserverServiceConnection = new ServiceConnection() { - - @Override - public void onServiceConnected(ComponentName className, IBinder service) { - SyncedFolderObserverService.SyncedFolderObserverBinder binder = - (SyncedFolderObserverService.SyncedFolderObserverBinder) service; - mObserverService = binder.getService(); - mBound = true; - } - - @Override - public void onServiceDisconnected(ComponentName arg0) { - mBound = false; - } - }; - } diff --git a/src/main/java/com/owncloud/android/datamodel/SyncedFolderProvider.java b/src/main/java/com/owncloud/android/datamodel/SyncedFolderProvider.java index baef8c3c1d12..53b871d5bc9b 100644 --- a/src/main/java/com/owncloud/android/datamodel/SyncedFolderProvider.java +++ b/src/main/java/com/owncloud/android/datamodel/SyncedFolderProvider.java @@ -27,7 +27,6 @@ import android.net.Uri; import android.support.annotation.NonNull; -import com.owncloud.android.MainApp; import com.owncloud.android.db.PreferenceManager; import com.owncloud.android.db.ProviderMeta; import com.owncloud.android.lib.common.utils.Log_OC; @@ -71,7 +70,6 @@ public long storeFolderSync(SyncedFolder syncedFolder) { Uri result = mContentResolver.insert(ProviderMeta.ProviderTableMeta.CONTENT_URI_SYNCED_FOLDERS, cv); if (result != null) { - notifyFolderSyncObservers(syncedFolder); return Long.parseLong(result.getPathSegments().get(1)); } else { Log_OC.e(TAG, "Failed to insert item " + syncedFolder.getLocalPath() + " into folder sync db."); @@ -299,10 +297,6 @@ public int updateSyncFolder(SyncedFolder syncedFolder) { new String[]{String.valueOf(syncedFolder.getId())} ); - if (result > 0) { - notifyFolderSyncObservers(syncedFolder); - } - return result; } @@ -362,16 +356,4 @@ private ContentValues createContentValuesFromSyncedFolder(SyncedFolder syncedFol return cv; } - - /** - * Inform all observers about data change. - * - * @param syncedFolder changed, synchronized folder - */ - private void notifyFolderSyncObservers(SyncedFolder syncedFolder) { - if (syncedFolder != null) { - MainApp.getSyncedFolderObserverService().restartObserver(syncedFolder); - Log_OC.d(TAG, "notifying folder sync data observers for changed/added: " + syncedFolder.getLocalPath()); - } - } } diff --git a/src/main/java/com/owncloud/android/services/AdvancedFileAlterationListener.java b/src/main/java/com/owncloud/android/services/AdvancedFileAlterationListener.java deleted file mode 100644 index 9c1f88610543..000000000000 --- a/src/main/java/com/owncloud/android/services/AdvancedFileAlterationListener.java +++ /dev/null @@ -1,238 +0,0 @@ -/** - * Nextcloud Android client application - * - * @author Mario Danic - * Copyright (C) 2017 Mario Danic - *

- * 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 . - */ -package com.owncloud.android.services; - -import android.content.Context; -import android.content.res.Resources; -import android.media.ExifInterface; -import android.os.Handler; -import android.text.TextUtils; - -import com.evernote.android.job.JobRequest; -import com.evernote.android.job.util.support.PersistableBundleCompat; -import com.owncloud.android.MainApp; -import com.owncloud.android.R; -import com.owncloud.android.datamodel.ArbitraryDataProvider; -import com.owncloud.android.datamodel.OCFile; -import com.owncloud.android.datamodel.SyncedFolder; -import com.owncloud.android.jobs.AutoUploadJob; -import com.owncloud.android.files.services.FileUploader; -import com.owncloud.android.lib.common.utils.Log_OC; -import com.owncloud.android.ui.activity.Preferences; -import com.owncloud.android.utils.FileStorageUtils; - -import org.apache.commons.io.monitor.FileAlterationListener; -import org.apache.commons.io.monitor.FileAlterationObserver; - -import java.io.File; -import java.io.IOException; -import java.text.ParsePosition; -import java.text.SimpleDateFormat; -import java.util.Date; -import java.util.HashMap; -import java.util.Locale; -import java.util.Map; -import java.util.TimeZone; - -/** - * Magical file alteration listener - */ - -public class AdvancedFileAlterationListener implements FileAlterationListener { - - public static final String TAG = "AdvancedFileAlterationListener"; - public static final int DELAY_INVOCATION_MS = 2500; - private Context context; - private boolean lightVersion; - - private SyncedFolder syncedFolder; - - private Map uploadMap = new HashMap<>(); - private Handler handler = new Handler(); - - public AdvancedFileAlterationListener(SyncedFolder syncedFolder, boolean lightVersion) { - super(); - - context = MainApp.getAppContext(); - this.lightVersion = lightVersion; - this.syncedFolder = syncedFolder; - } - - @Override - public void onStart(FileAlterationObserver observer) { - // This method is intentionally empty - } - - @Override - public void onDirectoryCreate(File directory) { - // This method is intentionally empty - } - - @Override - public void onDirectoryChange(File directory) { - // This method is intentionally empty - } - - @Override - public void onDirectoryDelete(File directory) { - // This method is intentionally empty - } - - @Override - public void onFileCreate(final File file) { - onFileCreate(file, DELAY_INVOCATION_MS); - } - - public void onFileCreate(final File file, int delay) { - if (file != null) { - uploadMap.put(file.getAbsolutePath(), null); - - String mimetypeString = FileStorageUtils.getMimeTypeFromName(file.getAbsolutePath()); - Long lastModificationTime = file.lastModified(); - final Locale currentLocale = context.getResources().getConfiguration().locale; - - if ("image/jpeg".equalsIgnoreCase(mimetypeString) || "image/tiff".equalsIgnoreCase(mimetypeString)) { - try { - ExifInterface exifInterface = new ExifInterface(file.getAbsolutePath()); - String exifDate = exifInterface.getAttribute(ExifInterface.TAG_DATETIME); - if (!TextUtils.isEmpty(exifDate)) { - ParsePosition pos = new ParsePosition(0); - SimpleDateFormat sFormatter = new SimpleDateFormat("yyyy:MM:dd HH:mm:ss", currentLocale); - sFormatter.setTimeZone(TimeZone.getTimeZone(TimeZone.getDefault().getID())); - Date dateTime = sFormatter.parse(exifDate, pos); - lastModificationTime = dateTime.getTime(); - } - - } catch (IOException e) { - Log_OC.d(TAG, "Failed to get the proper time " + e.getLocalizedMessage()); - } - } - - - final Long finalLastModificationTime = lastModificationTime; - - Runnable runnable = new Runnable() { - @Override - public void run() { - - String remotePath; - boolean subfolderByDate; - boolean chargingOnly; - boolean wifiOnly; - Integer uploadAction; - String accountName = syncedFolder.getAccount(); - - if (lightVersion) { - ArbitraryDataProvider arbitraryDataProvider = new ArbitraryDataProvider(context - .getContentResolver()); - - Resources resources = MainApp.getAppContext().getResources(); - - remotePath = resources.getString(R.string.syncedFolder_remote_folder) + OCFile.PATH_SEPARATOR + - new File(syncedFolder.getLocalPath()).getName() + OCFile.PATH_SEPARATOR; - subfolderByDate = resources.getBoolean(R.bool.syncedFolder_light_use_subfolders); - chargingOnly = resources.getBoolean(R.bool.syncedFolder_light_on_charging); - wifiOnly = arbitraryDataProvider.getBooleanValue(accountName, - Preferences.SYNCED_FOLDER_LIGHT_UPLOAD_ON_WIFI); - String uploadActionString = resources.getString(R.string.syncedFolder_light_upload_behaviour); - uploadAction = getUploadAction(uploadActionString); - } else { - remotePath = syncedFolder.getRemotePath(); - subfolderByDate = syncedFolder.getSubfolderByDate(); - chargingOnly = syncedFolder.getChargingOnly(); - wifiOnly = syncedFolder.getWifiOnly(); - uploadAction = syncedFolder.getUploadAction(); - } - - PersistableBundleCompat bundle = new PersistableBundleCompat(); - bundle.putString(AutoUploadJob.LOCAL_PATH, file.getAbsolutePath()); - bundle.putString(AutoUploadJob.REMOTE_PATH, FileStorageUtils.getInstantUploadFilePath( - currentLocale, - remotePath, file.getName(), - finalLastModificationTime, - subfolderByDate)); - bundle.putString(AutoUploadJob.ACCOUNT, accountName); - bundle.putInt(AutoUploadJob.UPLOAD_BEHAVIOUR, uploadAction); - - new JobRequest.Builder(AutoUploadJob.TAG) - .setExecutionWindow(30_000L, 80_000L) - .setRequiresCharging(chargingOnly) - .setRequiredNetworkType(wifiOnly ? JobRequest.NetworkType.UNMETERED : - JobRequest.NetworkType.ANY) - .setExtras(bundle) - .setPersisted(false) - .setRequirementsEnforced(true) - .setUpdateCurrent(false) - .build() - .schedule(); - - uploadMap.remove(file.getAbsolutePath()); - } - }; - - uploadMap.put(file.getAbsolutePath(), runnable); - handler.postDelayed(runnable, delay); - } - } - - private Integer getUploadAction(String action) { - switch (action) { - case "LOCAL_BEHAVIOUR_FORGET": - return FileUploader.LOCAL_BEHAVIOUR_FORGET; - case "LOCAL_BEHAVIOUR_MOVE": - return FileUploader.LOCAL_BEHAVIOUR_MOVE; - case "LOCAL_BEHAVIOUR_DELETE": - return FileUploader.LOCAL_BEHAVIOUR_DELETE; - default: - return FileUploader.LOCAL_BEHAVIOUR_FORGET; - } - } - - @Override - public void onFileChange(File file) { - onFileChange(file, 2500); - } - - public void onFileChange(File file, int delay) { - Runnable runnable; - if ((runnable = uploadMap.get(file.getAbsolutePath())) != null) { - handler.removeCallbacks(runnable); - handler.postDelayed(runnable, delay); - } - } - - @Override - public void onFileDelete(File file) { - Runnable runnable; - if ((runnable = uploadMap.get(file.getAbsolutePath())) != null) { - handler.removeCallbacks(runnable); - uploadMap.remove(file.getAbsolutePath()); - } - } - - @Override - public void onStop(FileAlterationObserver observer) { - // This method is intentionally empty - } - - public int getActiveTasksCount() { - return uploadMap.size(); - } -} diff --git a/src/main/java/com/owncloud/android/services/observer/AdvancedFileAlterationObserver.java b/src/main/java/com/owncloud/android/services/observer/AdvancedFileAlterationObserver.java deleted file mode 100644 index 86046b94596f..000000000000 --- a/src/main/java/com/owncloud/android/services/observer/AdvancedFileAlterationObserver.java +++ /dev/null @@ -1,402 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You 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. - * - * Original source code: - * https://github.com/apache/commons-io/blob/master/src/main/java/org/apache/commons/io/monitor/FileAlterationObserver.java - * - * Modified by Mario Danic - * Changes are Copyright (C) 2017 Mario Danic - * Copyright (C) 2017 Nextcloud GmbH - * - * All changes are under the same licence as the original. - * - */ -package com.owncloud.android.services.observer; - -import android.os.SystemClock; - -import com.owncloud.android.datamodel.SyncedFolder; -import com.owncloud.android.lib.common.utils.Log_OC; -import com.owncloud.android.services.AdvancedFileAlterationListener; - -import org.apache.commons.io.FileUtils; -import org.apache.commons.io.comparator.NameFileComparator; -import org.apache.commons.io.monitor.FileAlterationObserver; -import org.apache.commons.io.monitor.FileEntry; - -import java.io.File; -import java.io.FileFilter; -import java.io.Serializable; -import java.util.Arrays; -import java.util.Comparator; -import java.util.Iterator; -import java.util.List; -import java.util.concurrent.CopyOnWriteArrayList; - -public class AdvancedFileAlterationObserver extends FileAlterationObserver implements Serializable { - - private static final long serialVersionUID = 1185122225658782848L; - private static final int DELAY_INVOCATION_MS = 2500; - private final List listeners = new CopyOnWriteArrayList<>(); - private FileEntry rootEntry; - private FileFilter fileFilter; - private Comparator comparator; - private SyncedFolder syncedFolder; - - private static final FileEntry[] EMPTY_ENTRIES = new FileEntry[0]; - - public AdvancedFileAlterationObserver(SyncedFolder syncedFolder, FileFilter fileFilter) { - super(syncedFolder.getLocalPath(), fileFilter); - - this.rootEntry = new FileEntry(new File(syncedFolder.getLocalPath())); - this.fileFilter = fileFilter; - this.syncedFolder = syncedFolder; - comparator = NameFileComparator.NAME_SYSTEM_COMPARATOR; - } - - public long getSyncedFolderID() { - return syncedFolder.getId(); - } - - public SyncedFolder getSyncedFolder() { - return syncedFolder; - } - - /** - * Return the directory being observed. - * - * @return the directory being observed - */ - public File getDirectory() { - return rootEntry.getFile(); - } - - /** - * Return the fileFilter. - * - * @return the fileFilter - * @since 2.1 - */ - public FileFilter getFileFilter() { - return fileFilter; - } - - public FileEntry getRootEntry() { - return rootEntry; - } - - public void setRootEntry(FileEntry rootEntry) { - this.rootEntry = rootEntry; - } - - /** - * Add a file system listener. - * - * @param listener The file system listener - */ - public void addListener(final AdvancedFileAlterationListener listener) { - if (listener != null) { - listeners.add(listener); - } - } - - /** - * Remove a file system listener. - * - * @param listener The file system listener - */ - public void removeListener(final AdvancedFileAlterationListener listener) { - if (listener != null) { - while (listeners.remove(listener)) { - } - } - } - - /** - * Returns the set of registered file system listeners. - * - * @return The file system listeners - */ - public Iterable getMagicListeners() { - return listeners; - } - - /** - * Does nothing - hack for the monitor - * - * - */ - public void initialize() { - // does nothing - hack the monitor - } - - - /** - * Initializes everything - * - * @throws Exception if an error occurs - */ - public void init() throws Exception { - rootEntry.refresh(rootEntry.getFile()); - final FileEntry[] children = doListFiles(rootEntry.getFile(), rootEntry); - rootEntry.setChildren(children); - } - - - /** - * Final processing. - * - * @throws Exception if an error occurs - */ - public void destroy() throws Exception { - Iterator iterator = getMagicListeners().iterator(); - while (iterator.hasNext()) { - AdvancedFileAlterationListener AdvancedFileAlterationListener = (AdvancedFileAlterationListener) iterator.next(); - while (AdvancedFileAlterationListener.getActiveTasksCount() > 0) { - SystemClock.sleep(250); - } - } - } - - public void checkAndNotifyNow() { - /* fire onStart() */ - for (final AdvancedFileAlterationListener listener : listeners) { - listener.onStart(this); - } - - /* fire directory/file events */ - final File rootFile = rootEntry.getFile(); - if (rootFile.exists()) { - checkAndNotify(rootEntry, rootEntry.getChildren(), listFiles(rootFile), 0); - } else if (rootEntry.isExists()) { - try { - // try to init once more - init(); - if (rootEntry.getFile().exists()) { - checkAndNotify(rootEntry, rootEntry.getChildren(), listFiles(rootEntry.getFile()), 0); - } else { - checkAndNotify(rootEntry, rootEntry.getChildren(), FileUtils.EMPTY_FILE_ARRAY, 0); - } - } catch (Exception e) { - Log_OC.d("AdvancedFileAlterationObserver", "Failed getting an observer to intialize " + e); - checkAndNotify(rootEntry, rootEntry.getChildren(), FileUtils.EMPTY_FILE_ARRAY, 0); - } - } // else didn't exist and still doesn't - - /* fire onStop() */ - for (final AdvancedFileAlterationListener listener : listeners) { - listener.onStop(this); - } - } - - /** - * Check whether the file and its children have been created, modified or deleted. - */ - public void checkAndNotify() { - - /* fire onStart() */ - for (final AdvancedFileAlterationListener listener : listeners) { - listener.onStart(this); - } - - /* fire directory/file events */ - final File rootFile = rootEntry.getFile(); - if (rootFile.exists()) { - checkAndNotify(rootEntry, rootEntry.getChildren(), listFiles(rootFile), DELAY_INVOCATION_MS); - } else if (rootEntry.isExists()) { - try { - // try to init once more - init(); - if (rootEntry.getFile().exists()) { - checkAndNotify(rootEntry, rootEntry.getChildren(), listFiles(rootEntry.getFile()), - DELAY_INVOCATION_MS); - } else { - checkAndNotify(rootEntry, rootEntry.getChildren(), FileUtils.EMPTY_FILE_ARRAY, DELAY_INVOCATION_MS); - } - } catch (Exception e) { - Log_OC.d("AdvancedFileAlterationObserver", "Failed getting an observer to intialize " + e); - checkAndNotify(rootEntry, rootEntry.getChildren(), FileUtils.EMPTY_FILE_ARRAY, DELAY_INVOCATION_MS); - } - } // else didn't exist and still doesn't - - /* fire onStop() */ - for (final AdvancedFileAlterationListener listener : listeners) { - listener.onStop(this); - } - } - - /** - * Compare two file lists for files which have been created, modified or deleted. - * - * @param parent The parent entry - * @param previous The original list of files - * @param files The current list of files - */ - private void checkAndNotify(final FileEntry parent, final FileEntry[] previous, final File[] files, int delay) { - if (files != null && files.length > 0) { - int c = 0; - final FileEntry[] current = files.length > 0 ? new FileEntry[files.length] : EMPTY_ENTRIES; - for (final FileEntry entry : previous) { - while (c < files.length && comparator.compare(entry.getFile(), files[c]) > 0) { - current[c] = createFileEntry(parent, files[c]); - doCreate(current[c], delay); - c++; - } - if (c < files.length && comparator.compare(entry.getFile(), files[c]) == 0) { - doMatch(entry, files[c], delay); - checkAndNotify(entry, entry.getChildren(), listFiles(files[c]), delay); - current[c] = entry; - c++; - } else { - checkAndNotify(entry, entry.getChildren(), FileUtils.EMPTY_FILE_ARRAY, delay); - doDelete(entry); - } - } - for (; c < files.length; c++) { - current[c] = createFileEntry(parent, files[c]); - doCreate(current[c], delay); - } - parent.setChildren(current); - } - } - - /** - * Create a new file entry for the specified file. - * - * @param parent The parent file entry - * @param file The file to create an entry for - * @return A new file entry - */ - private FileEntry createFileEntry(final FileEntry parent, final File file) { - final FileEntry entry = parent.newChildInstance(file); - entry.refresh(file); - final FileEntry[] children = doListFiles(file, entry); - entry.setChildren(children); - return entry; - } - - /** - * List the files - * - * @param file The file to list files for - * @param entry the parent entry - * @return The child files - */ - private FileEntry[] doListFiles(File file, FileEntry entry) { - final File[] files = listFiles(file); - final FileEntry[] children = files.length > 0 ? new FileEntry[files.length] : EMPTY_ENTRIES; - for (int i = 0; i < files.length; i++) { - children[i] = createFileEntry(entry, files[i]); - } - return children; - } - - /** - * Fire directory/file created events to the registered listeners. - * - * @param entry The file entry - */ - private void doCreate(final FileEntry entry, int delay) { - for (final AdvancedFileAlterationListener listener : listeners) { - if (entry.isDirectory()) { - listener.onDirectoryCreate(entry.getFile()); - } else { - listener.onFileCreate(entry.getFile(), delay); - } - } - final FileEntry[] children = entry.getChildren(); - for (final FileEntry aChildren : children) { - doCreate(aChildren, delay); - } - } - - /** - * Fire directory/file change events to the registered listeners. - * - * @param entry The previous file system entry - * @param file The current file - */ - private void doMatch(final FileEntry entry, final File file, int delay) { - if (entry.refresh(file)) { - for (final AdvancedFileAlterationListener listener : listeners) { - if (entry.isDirectory()) { - listener.onDirectoryChange(file); - } else { - listener.onFileChange(file, delay); - } - } - } - } - - /** - * Fire directory/file delete events to the registered listeners. - * - * @param entry The file entry - */ - private void doDelete(final FileEntry entry) { - for (final AdvancedFileAlterationListener listener : listeners) { - if (entry.isDirectory()) { - listener.onDirectoryDelete(entry.getFile()); - } else { - listener.onFileDelete(entry.getFile()); - } - } - } - - /** - * List the contents of a directory - * - * @param file The file to list the contents of - * @return the directory contents or a zero length array if - * the empty or the file is not a directory - */ - private File[] listFiles(final File file) { - File[] children = null; - if (file.isDirectory()) { - children = fileFilter == null ? file.listFiles() : file.listFiles(fileFilter); - } - if (children == null) { - children = FileUtils.EMPTY_FILE_ARRAY; - } - if (comparator != null && children.length > 1) { - Arrays.sort(children, comparator); - } - return children; - } - - /** - * Provide a String representation of this observer. - * - * @return a String representation of this observer - */ - @Override - public String toString() { - final StringBuilder builder = new StringBuilder(); - builder.append(getClass().getSimpleName()); - builder.append("[file='"); - builder.append(getDirectory().getPath()); - builder.append('\''); - if (fileFilter != null) { - builder.append(", "); - builder.append(fileFilter.toString()); - } - builder.append(", listeners="); - builder.append(listeners.size()); - builder.append("]"); - return builder.toString(); - } - -} \ No newline at end of file diff --git a/src/main/java/com/owncloud/android/services/observer/SyncedFolderObserverService.java b/src/main/java/com/owncloud/android/services/observer/SyncedFolderObserverService.java deleted file mode 100644 index a6bd52821820..000000000000 --- a/src/main/java/com/owncloud/android/services/observer/SyncedFolderObserverService.java +++ /dev/null @@ -1,180 +0,0 @@ -/** - * Nextcloud Android client application - * - * @author Tobias Kaminsky - * @author Andy Scherzinger - * @author Mario Danic - * Copyright (C) 2016 Tobias Kaminsky, Andy Scherzinger - * Copyright (C) 2017 Mario Danic - *

- * 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 . - */ -package com.owncloud.android.services.observer; - -import android.app.Service; -import android.content.Intent; -import android.os.Binder; -import android.os.IBinder; - -import com.owncloud.android.MainApp; -import com.owncloud.android.R; -import com.owncloud.android.datamodel.SyncedFolder; -import com.owncloud.android.datamodel.SyncedFolderProvider; -import com.owncloud.android.lib.common.utils.Log_OC; -import com.owncloud.android.services.AdvancedFileAlterationListener; - -import org.apache.commons.io.monitor.FileAlterationMonitor; -import org.apache.commons.io.monitor.FileAlterationObserver; - -import java.io.File; -import java.io.FileFilter; - -public class SyncedFolderObserverService extends Service { - private static final String TAG = "SyncedFolderObserverService"; - - private static final int MONITOR_SCAN_INTERVAL = 1000; - - private final IBinder mBinder = new SyncedFolderObserverBinder(); - private FileAlterationMonitor monitor; - private FileFilter fileFilter; - - @Override - public void onCreate() { - SyncedFolderProvider syncedFolderProvider = new SyncedFolderProvider(MainApp.getAppContext(). - getContentResolver()); - monitor = new FileAlterationMonitor(MONITOR_SCAN_INTERVAL); - - fileFilter = new FileFilter() { - @Override - public boolean accept(File pathname) { - return !pathname.getName().startsWith(".") && !pathname.getName().endsWith(".tmp") && - !pathname.getName().endsWith(".temp") && !pathname.getName().endsWith(".thumbnail"); - } - }; - - - for (SyncedFolder syncedFolder : syncedFolderProvider.getSyncedFolders()) { - if (syncedFolder.isEnabled()) { - AdvancedFileAlterationObserver observer = new AdvancedFileAlterationObserver(syncedFolder, fileFilter); - - try { - observer.init(); - observer.addListener(new AdvancedFileAlterationListener(syncedFolder, - getResources().getBoolean(R.bool.syncedFolder_light))); - monitor.addObserver(observer); - } catch (Exception e) { - Log_OC.d(TAG, "Failed getting an observer to initialize " + e); - } - - } - } - - - try { - monitor.start(); - } catch (Exception e) { - Log_OC.d(TAG, "Something went very wrong at onStartCommand"); - } - - } - - @Override - public void onDestroy() { - - super.onDestroy(); - for (FileAlterationObserver fileAlterationObserver : monitor.getObservers()) { - AdvancedFileAlterationObserver advancedFileAlterationObserver = (AdvancedFileAlterationObserver) - fileAlterationObserver; - try { - monitor.removeObserver(advancedFileAlterationObserver); - advancedFileAlterationObserver.checkAndNotifyNow(); - advancedFileAlterationObserver.destroy(); - } catch (Exception e) { - Log_OC.d(TAG, "Something went very wrong on trying to destroy observers"); - } - } - } - - @Override - public int onStartCommand(Intent intent, int flags, int startId) { - return Service.START_NOT_STICKY; - } - - /** - * Restart oberver if it is enabled - * If syncedFolder exists already, use it, otherwise create new observer - * - * @param syncedFolder - */ - - public void restartObserver(SyncedFolder syncedFolder) { - boolean found = false; - AdvancedFileAlterationObserver advancedFileAlterationObserver; - for (FileAlterationObserver fileAlterationObserver : monitor.getObservers()) { - advancedFileAlterationObserver = - (AdvancedFileAlterationObserver) fileAlterationObserver; - if (advancedFileAlterationObserver.getSyncedFolderID() == syncedFolder.getId()) { - monitor.removeObserver(fileAlterationObserver); - advancedFileAlterationObserver.checkAndNotifyNow(); - try { - advancedFileAlterationObserver.destroy(); - } catch (Exception e) { - Log_OC.d(TAG, "Failed to destroy the observer in restart"); - } - - if (syncedFolder.isEnabled()) { - try { - advancedFileAlterationObserver = new AdvancedFileAlterationObserver(syncedFolder, fileFilter); - advancedFileAlterationObserver.init(); - advancedFileAlterationObserver.addListener(new AdvancedFileAlterationListener(syncedFolder, - getResources().getBoolean(R.bool.syncedFolder_light))); - monitor.addObserver(advancedFileAlterationObserver); - } catch (Exception e) { - Log_OC.d(TAG, "Failed getting an observer to initialize"); - } - } else { - monitor.removeObserver(fileAlterationObserver); - } - found = true; - break; - } - } - - if (!found && syncedFolder.isEnabled()) { - try { - advancedFileAlterationObserver = new AdvancedFileAlterationObserver(syncedFolder, fileFilter); - advancedFileAlterationObserver.init(); - advancedFileAlterationObserver.addListener(new AdvancedFileAlterationListener(syncedFolder, - getResources().getBoolean(R.bool.syncedFolder_light))); - monitor.addObserver(advancedFileAlterationObserver); - } catch (Exception e) { - Log_OC.d(TAG, "Failed getting an observer to initialize"); - } - - } - - } - - @Override - public IBinder onBind(Intent arg0) { - return mBinder; - } - - public class SyncedFolderObserverBinder extends Binder { - public SyncedFolderObserverService getService() { - return SyncedFolderObserverService.this; - } - } - -} From a088658c5f2ef5d46102de2a315f7439638bd534 Mon Sep 17 00:00:00 2001 From: Mario Danic Date: Fri, 5 May 2017 20:55:11 +0200 Subject: [PATCH 032/206] Document new auto upload job Signed-off-by: Mario Danic --- .../java/com/owncloud/android/jobs/NewAutoUploadJob.java | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/main/java/com/owncloud/android/jobs/NewAutoUploadJob.java b/src/main/java/com/owncloud/android/jobs/NewAutoUploadJob.java index 277cee0bfd38..63562d7e39e3 100644 --- a/src/main/java/com/owncloud/android/jobs/NewAutoUploadJob.java +++ b/src/main/java/com/owncloud/android/jobs/NewAutoUploadJob.java @@ -44,6 +44,14 @@ import java.util.ArrayList; import java.util.List; +/* + This job is meant to run periodically every half an hour, and has the following burden on it's shoulders: + - looks through all relevant folders and stores files & folders into a database + - cleans files from DB that we haven't encountered in a while (TODO) + - store newly created files to be uploaded into uploads table (TODO) + - initiate uploads from the uploads table (TODO) + - restart uploads that have previously failed (TODO +*/ public class NewAutoUploadJob extends Job { public static final String TAG = "NewAutoUploadJob"; From 40c7ab5cbe3371cd97a191b40f03ae9d3c867e06 Mon Sep 17 00:00:00 2001 From: Mario Danic Date: Sat, 6 May 2017 19:10:27 +0200 Subject: [PATCH 033/206] Delete shutdown receiver Signed-off-by: Mario Danic --- src/main/AndroidManifest.xml | 6 --- .../android/services/ShutdownReceiver.java | 40 ------------------- 2 files changed, 46 deletions(-) delete mode 100644 src/main/java/com/owncloud/android/services/ShutdownReceiver.java diff --git a/src/main/AndroidManifest.xml b/src/main/AndroidManifest.xml index d25b0660214e..2ebc6236ae49 100644 --- a/src/main/AndroidManifest.xml +++ b/src/main/AndroidManifest.xml @@ -232,12 +232,6 @@ - - - - - - diff --git a/src/main/java/com/owncloud/android/services/ShutdownReceiver.java b/src/main/java/com/owncloud/android/services/ShutdownReceiver.java deleted file mode 100644 index 940dd1e45ed5..000000000000 --- a/src/main/java/com/owncloud/android/services/ShutdownReceiver.java +++ /dev/null @@ -1,40 +0,0 @@ -/** - * Nextcloud Android client application - * - * @author Mario Danic - * Copyright (C) 2017 Mario Danic - * Copyright (C) 2017 Nextcloud GmbH - * - * 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 . - */ -package com.owncloud.android.services; - -import android.content.BroadcastReceiver; -import android.content.Context; -import android.content.Intent; - -import com.owncloud.android.MainApp; - -/** - * Handles shutdown procedure - basically just waits a little bit for all jobs to finish - */ - -public class ShutdownReceiver extends BroadcastReceiver { - @Override - public void onReceive(final Context context, final Intent intent) { - if (MainApp.getSyncedFolderObserverService() != null) { - MainApp.getSyncedFolderObserverService().onDestroy(); - } - } -} From 010718d75cd65b6488235c4608af454ae71592d7 Mon Sep 17 00:00:00 2001 From: Mario Danic Date: Wed, 10 May 2017 20:13:52 +0200 Subject: [PATCH 034/206] Try to fix update Signed-off-by: Mario Danic --- .../com/owncloud/android/providers/FileContentProvider.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/main/java/com/owncloud/android/providers/FileContentProvider.java b/src/main/java/com/owncloud/android/providers/FileContentProvider.java index 05d1445cf19a..e6df3a2fa352 100644 --- a/src/main/java/com/owncloud/android/providers/FileContentProvider.java +++ b/src/main/java/com/owncloud/android/providers/FileContentProvider.java @@ -1018,6 +1018,9 @@ public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { Log_OC.i(SQL, "Create filesystem table"); createFileSystemTable(db); + upgraded = true; + db.setTransactionSuccessful(); + // magic to split out existing synced folders in two when needed // otherwise, we migrate them to their proper type (image or video) Log_OC.i(SQL, "Migrate synced_folders records for image/video split"); @@ -1059,8 +1062,6 @@ public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { syncedFolderProvider.deleteSyncedFoldersInList(idsToDelete); - upgraded = true; - db.setTransactionSuccessful(); } catch (Throwable t) { Log_OC.e(TAG, "ERROR!", t); } finally { From bf42b538bed48ee64e1511d7cb0d8058373154ad Mon Sep 17 00:00:00 2001 From: Mario Danic Date: Wed, 10 May 2017 20:35:44 +0200 Subject: [PATCH 035/206] Try to fix it Signed-off-by: Mario Danic --- .../java/com/owncloud/android/MainApp.java | 51 +++++++++++++++++++ .../android/db/PreferenceManager.java | 22 ++++++++ .../providers/FileContentProvider.java | 42 --------------- 3 files changed, 73 insertions(+), 42 deletions(-) diff --git a/src/main/java/com/owncloud/android/MainApp.java b/src/main/java/com/owncloud/android/MainApp.java index 39567c078fa2..2e195dc9779e 100644 --- a/src/main/java/com/owncloud/android/MainApp.java +++ b/src/main/java/com/owncloud/android/MainApp.java @@ -20,6 +20,7 @@ package com.owncloud.android; import android.app.Activity; +import android.content.ContentResolver; import android.content.Context; import android.content.SharedPreferences; import android.content.pm.PackageInfo; @@ -31,6 +32,8 @@ import com.evernote.android.job.JobManager; import com.owncloud.android.authentication.PassCodeManager; +import com.owncloud.android.datamodel.MediaFolder; +import com.owncloud.android.datamodel.MediaProvider; import com.owncloud.android.datamodel.SyncedFolder; import com.owncloud.android.datamodel.SyncedFolderProvider; import com.owncloud.android.datamodel.ThumbnailsCacheManager; @@ -263,6 +266,54 @@ private void updateAutoUploadEntries() { } } + private void splitOutAutoUploadEntries() { + if (!PreferenceManager.getAutoUploadSplitEntries(this)) { + // magic to split out existing synced folders in two when needed + // otherwise, we migrate them to their proper type (image or video) + Log_OC.i(TAG, "Migrate synced_folders records for image/video split"); + ContentResolver contentResolver = this.getContentResolver(); + + SyncedFolderProvider syncedFolderProvider = new SyncedFolderProvider(contentResolver); + + final List imageMediaFolders = MediaProvider.getImageFolders(contentResolver, 1); + final List videoMediaFolders = MediaProvider.getVideoFolders(contentResolver, 1); + + ArrayList idsToDelete = new ArrayList<>(); + List syncedFolders = syncedFolderProvider.getSyncedFolders(); + long primaryKey; + SyncedFolder newSyncedFolder; + for (SyncedFolder syncedFolder : syncedFolders) { + idsToDelete.add(syncedFolder.getId()); + for (int i = 0; i < imageMediaFolders.size(); i++) { + if (imageMediaFolders.get(i).absolutePath.equals(syncedFolder.getLocalPath())) { + newSyncedFolder = (SyncedFolder) syncedFolder.clone(); + newSyncedFolder.setType(MediaFolder.IMAGE); + primaryKey = syncedFolderProvider.storeFolderSync(newSyncedFolder); + Log_OC.i(TAG, "Migrated image synced_folders record: " + + primaryKey + " - " + newSyncedFolder.getLocalPath()); + break; + } + } + + for (int j = 0; j < videoMediaFolders.size(); j++) { + if (videoMediaFolders.get(j).absolutePath.equals(syncedFolder.getLocalPath())) { + newSyncedFolder = (SyncedFolder) syncedFolder.clone(); + newSyncedFolder.setType(MediaFolder.VIDEO); + primaryKey = syncedFolderProvider.storeFolderSync(newSyncedFolder); + Log_OC.i(TAG, "Migrated video synced_folders record: " + + primaryKey + " - " + newSyncedFolder.getLocalPath()); + break; + } + } + } + + syncedFolderProvider.deleteSyncedFoldersInList(idsToDelete); + + PreferenceManager.setAutoUploadSplitEntries(this, true); + + } + } + private void cleanOldEntries() { // previous versions of application created broken entries in the SyncedFolderProvider // database, and this cleans all that and leaves 1 (newest) entry per synced folder diff --git a/src/main/java/com/owncloud/android/db/PreferenceManager.java b/src/main/java/com/owncloud/android/db/PreferenceManager.java index daff294f5913..4b052e363dc4 100644 --- a/src/main/java/com/owncloud/android/db/PreferenceManager.java +++ b/src/main/java/com/owncloud/android/db/PreferenceManager.java @@ -48,6 +48,7 @@ public abstract class PreferenceManager { private static final String PREF__LEGACY_CLEAN = "legacyClean"; private static final String PREF__AUTO_UPLOAD_UPDATE_PATH = "autoUploadPathUpdate"; private static final String PREF__PUSH_TOKEN = "pushToken"; + private static final String PREF__AUTO_UPLOAD_SPLIT_OUT = "autoUploadEntriesSplitOut"; public static void setPushToken(Context context, String pushToken) { saveStringPreferenceNow(context, PREF__PUSH_TOKEN, pushToken); @@ -218,6 +219,16 @@ public static boolean getAutoUploadPathsUpdate(Context context) { return getDefaultSharedPreferences(context).getBoolean(PREF__AUTO_UPLOAD_UPDATE_PATH, false); } + /** + * Gets the auto upload split out flag last set. + * + * @param context Caller {@link Context}, used to access to shared preferences manager. + * @return ascending order the legacy cleaning flag, default is false + */ + public static boolean getAutoUploadSplitEntries(Context context) { + return getDefaultSharedPreferences(context).getBoolean(PREF__AUTO_UPLOAD_SPLIT_OUT, false); + } + /** * Saves the legacy cleaning flag which the user has set last. @@ -239,6 +250,17 @@ public static void setAutoUploadPathsUpdate(Context context, boolean pathUpdate) saveBooleanPreference(context, PREF__AUTO_UPLOAD_UPDATE_PATH, pathUpdate); } + /** + * Saves the flag for split entries magic + * + * @param context Caller {@link Context}, used to access to shared preferences manager. + * @param splitOut flag if it is a auto upload path update + */ + public static void setAutoUploadSplitEntries(Context context, boolean splitOut) { + saveBooleanPreference(context, PREF__AUTO_UPLOAD_SPLIT_OUT, splitOut); + } + + /** * Gets the uploader behavior which the user has set last. diff --git a/src/main/java/com/owncloud/android/providers/FileContentProvider.java b/src/main/java/com/owncloud/android/providers/FileContentProvider.java index e6df3a2fa352..c568df17f5a1 100644 --- a/src/main/java/com/owncloud/android/providers/FileContentProvider.java +++ b/src/main/java/com/owncloud/android/providers/FileContentProvider.java @@ -1021,53 +1021,11 @@ public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { upgraded = true; db.setTransactionSuccessful(); - // magic to split out existing synced folders in two when needed - // otherwise, we migrate them to their proper type (image or video) - Log_OC.i(SQL, "Migrate synced_folders records for image/video split"); - ContentResolver contentResolver = getContext().getContentResolver(); - - SyncedFolderProvider syncedFolderProvider = new SyncedFolderProvider(contentResolver); - - final List imageMediaFolders = MediaProvider.getImageFolders(contentResolver, 1); - final List videoMediaFolders = MediaProvider.getVideoFolders(contentResolver, 1); - - ArrayList idsToDelete = new ArrayList<>(); - List syncedFolders = syncedFolderProvider.getSyncedFolders(); - long primaryKey; - SyncedFolder newSyncedFolder; - for (SyncedFolder syncedFolder : syncedFolders) { - idsToDelete.add(syncedFolder.getId()); - for (int i = 0; i < imageMediaFolders.size(); i++) { - if (imageMediaFolders.get(i).absolutePath.equals(syncedFolder.getLocalPath())) { - newSyncedFolder = (SyncedFolder) syncedFolder.clone(); - newSyncedFolder.setType(MediaFolder.IMAGE); - primaryKey = syncedFolderProvider.storeFolderSync(newSyncedFolder); - Log_OC.i(SQL, "Migrated image synced_folders record: " - + primaryKey + " - " + newSyncedFolder.getLocalPath()); - break; - } - } - - for (int j = 0; j < videoMediaFolders.size(); j++) { - if (videoMediaFolders.get(j).absolutePath.equals(syncedFolder.getLocalPath())) { - newSyncedFolder = (SyncedFolder) syncedFolder.clone(); - newSyncedFolder.setType(MediaFolder.VIDEO); - primaryKey = syncedFolderProvider.storeFolderSync(newSyncedFolder); - Log_OC.i(SQL, "Migrated video synced_folders record: " - + primaryKey + " - " + newSyncedFolder.getLocalPath()); - break; - } - } - } - - syncedFolderProvider.deleteSyncedFoldersInList(idsToDelete); - } catch (Throwable t) { Log_OC.e(TAG, "ERROR!", t); } finally { db.endTransaction(); } - } if (!upgraded) { Log_OC.i(SQL, String.format(Locale.ENGLISH, UPGRADE_VERSION_MSG, oldVersion, newVersion)); From 890685cdffff9a3606412752b4aae23e612def46 Mon Sep 17 00:00:00 2001 From: Mario Danic Date: Wed, 10 May 2017 20:56:59 +0200 Subject: [PATCH 036/206] Fix two smaller issues Signed-off-by: Mario Danic --- src/main/java/com/owncloud/android/MainApp.java | 1 + .../android/providers/FileContentProvider.java | 12 ++++-------- 2 files changed, 5 insertions(+), 8 deletions(-) diff --git a/src/main/java/com/owncloud/android/MainApp.java b/src/main/java/com/owncloud/android/MainApp.java index 2e195dc9779e..67fdd291b289 100644 --- a/src/main/java/com/owncloud/android/MainApp.java +++ b/src/main/java/com/owncloud/android/MainApp.java @@ -116,6 +116,7 @@ public void onCreate() { cleanOldEntries(); updateAutoUploadEntries(); + splitOutAutoUploadEntries(); // register global protection with pass code registerActivityLifecycleCallbacks(new ActivityLifecycleCallbacks() { diff --git a/src/main/java/com/owncloud/android/providers/FileContentProvider.java b/src/main/java/com/owncloud/android/providers/FileContentProvider.java index c568df17f5a1..e624c4a05774 100644 --- a/src/main/java/com/owncloud/android/providers/FileContentProvider.java +++ b/src/main/java/com/owncloud/android/providers/FileContentProvider.java @@ -27,7 +27,6 @@ import android.content.ContentProvider; import android.content.ContentProviderOperation; import android.content.ContentProviderResult; -import android.content.ContentResolver; import android.content.ContentUris; import android.content.ContentValues; import android.content.Context; @@ -43,11 +42,7 @@ import com.owncloud.android.MainApp; import com.owncloud.android.R; -import com.owncloud.android.datamodel.MediaFolder; -import com.owncloud.android.datamodel.MediaProvider; import com.owncloud.android.datamodel.OCFile; -import com.owncloud.android.datamodel.SyncedFolder; -import com.owncloud.android.datamodel.SyncedFolderProvider; import com.owncloud.android.datamodel.UploadsStorageManager; import com.owncloud.android.db.ProviderMeta; import com.owncloud.android.db.ProviderMeta.ProviderTableMeta; @@ -59,7 +54,6 @@ import java.io.File; import java.util.ArrayList; -import java.util.List; import java.util.Locale; /** @@ -852,6 +846,7 @@ public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { db.endTransaction(); } } + if (!upgraded) { Log_OC.i(SQL, String.format(Locale.ENGLISH, UPGRADE_VERSION_MSG, oldVersion, newVersion)); } @@ -1027,8 +1022,9 @@ public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { db.endTransaction(); } - if (!upgraded) { - Log_OC.i(SQL, String.format(Locale.ENGLISH, UPGRADE_VERSION_MSG, oldVersion, newVersion)); + if (!upgraded) { + Log_OC.i(SQL, String.format(Locale.ENGLISH, UPGRADE_VERSION_MSG, oldVersion, newVersion)); + } } if (oldVersion < 20 && newVersion >= 20) { From f02657d5a64a4737244ccb7ddd54c3b3387d7a18 Mon Sep 17 00:00:00 2001 From: Mario Danic Date: Wed, 10 May 2017 21:37:43 +0200 Subject: [PATCH 037/206] Acquire wakelocks to make sure all is good Signed-off-by: Mario Danic --- .../java/com/owncloud/android/jobs/AutoUploadJob.java | 9 +++++++++ .../com/owncloud/android/jobs/NewAutoUploadJob.java | 10 ++++++++++ 2 files changed, 19 insertions(+) diff --git a/src/main/java/com/owncloud/android/jobs/AutoUploadJob.java b/src/main/java/com/owncloud/android/jobs/AutoUploadJob.java index 4faa1133f41d..f1f12f1d3706 100644 --- a/src/main/java/com/owncloud/android/jobs/AutoUploadJob.java +++ b/src/main/java/com/owncloud/android/jobs/AutoUploadJob.java @@ -24,6 +24,7 @@ import android.accounts.Account; import android.content.Context; +import android.os.PowerManager; import android.support.annotation.NonNull; import com.evernote.android.job.Job; @@ -47,7 +48,14 @@ public class AutoUploadJob extends Job { @NonNull @Override protected Result onRunJob(Params params) { + final Context context = MainApp.getAppContext(); + + PowerManager powerManager = (PowerManager) context.getSystemService(context.POWER_SERVICE); + PowerManager.WakeLock wakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, + TAG); + wakeLock.acquire(); + PersistableBundleCompat bundle = params.getExtras(); final String filePath = bundle.getString(LOCAL_PATH, ""); final String remotePath = bundle.getString(REMOTE_PATH, ""); @@ -76,6 +84,7 @@ protected Result onRunJob(Params params) { } + wakeLock.release(); return Result.SUCCESS; } } diff --git a/src/main/java/com/owncloud/android/jobs/NewAutoUploadJob.java b/src/main/java/com/owncloud/android/jobs/NewAutoUploadJob.java index 63562d7e39e3..69dd677e0468 100644 --- a/src/main/java/com/owncloud/android/jobs/NewAutoUploadJob.java +++ b/src/main/java/com/owncloud/android/jobs/NewAutoUploadJob.java @@ -23,6 +23,7 @@ import android.content.ContentResolver; import android.content.Context; +import android.os.PowerManager; import android.support.annotation.NonNull; import android.util.Log; @@ -62,9 +63,16 @@ public class NewAutoUploadJob extends Job { @NonNull @Override protected Result onRunJob(Params params) { + final Context context = MainApp.getAppContext(); final ContentResolver contentResolver = context.getContentResolver(); + PowerManager powerManager = (PowerManager) context.getSystemService(context.POWER_SERVICE); + PowerManager.WakeLock wakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, + TAG); + wakeLock.acquire(); + + // Create all the providers we'll need ArbitraryDataProvider arbitraryDataProvider = new ArbitraryDataProvider(contentResolver); final FilesystemDataProvider filesystemDataProvider = new FilesystemDataProvider(contentResolver); @@ -114,6 +122,8 @@ public FileVisitResult visitFileFailed(Path file, IOException exc) { Log.d(TAG, "Something went wrong while indexing files for auto upload"); } } + + wakeLock.release(); return Result.SUCCESS; } } From f5935480aa5535473e7ff153182fcfedd7fc632f Mon Sep 17 00:00:00 2001 From: Mario Danic Date: Wed, 10 May 2017 22:07:44 +0200 Subject: [PATCH 038/206] Work a bit on locking Signed-off-by: Mario Danic --- build.gradle | 2 +- .../android/datamodel/FileSystemDataSet.java | 12 ++++++- .../datamodel/FilesystemDataProvider.java | 33 ++++++++++++++----- .../com/owncloud/android/db/ProviderMeta.java | 1 + .../android/jobs/NewAutoUploadJob.java | 22 +++++++++++-- .../providers/FileContentProvider.java | 1 + 6 files changed, 58 insertions(+), 13 deletions(-) diff --git a/build.gradle b/build.gradle index e5e64615bc20..94cfa1dedc65 100644 --- a/build.gradle +++ b/build.gradle @@ -187,7 +187,7 @@ dependencies { compile 'com.github.tobiasKaminsky:android-floating-action-button:1.10.2' compile 'com.google.code.findbugs:annotations:2.0.1' compile group: 'commons-io', name: 'commons-io', version: '2.4' - compile 'com.github.evernote:android-job:v1.1.9' + compile 'com.github.evernote:android-job:v1.1.10' compile 'com.jakewharton:butterknife:8.4.0' annotationProcessor 'com.jakewharton:butterknife-compiler:8.4.0' compile 'org.greenrobot:eventbus:3.0.0' diff --git a/src/main/java/com/owncloud/android/datamodel/FileSystemDataSet.java b/src/main/java/com/owncloud/android/datamodel/FileSystemDataSet.java index 999658dbedb5..f49d38588236 100644 --- a/src/main/java/com/owncloud/android/datamodel/FileSystemDataSet.java +++ b/src/main/java/com/owncloud/android/datamodel/FileSystemDataSet.java @@ -29,17 +29,20 @@ public class FileSystemDataSet { private boolean isFolder; private boolean isSentForUpload; private long foundAt; + private boolean isFileModified; public FileSystemDataSet() { } - public FileSystemDataSet(int id, String localPath, long modifiedAt, boolean isFolder, boolean isSentForUpload, long foundAt) { + public FileSystemDataSet(int id, String localPath, long modifiedAt, boolean isFolder, + boolean isSentForUpload, long foundAt, boolean isFileModified) { this.id = id; this.localPath = localPath; this.modifiedAt = modifiedAt; this.isFolder = isFolder; this.isSentForUpload = isSentForUpload; this.foundAt = foundAt; + this.isFileModified = isFileModified; } public int getId() { @@ -90,4 +93,11 @@ public void setSentForUpload(boolean sentForUpload) { isSentForUpload = sentForUpload; } + public boolean isFileModified() { + return isFileModified; + } + + public void setFileModified(boolean fileModified) { + isFileModified = fileModified; + } } diff --git a/src/main/java/com/owncloud/android/datamodel/FilesystemDataProvider.java b/src/main/java/com/owncloud/android/datamodel/FilesystemDataProvider.java index 2688ee6ede51..21455db828a0 100644 --- a/src/main/java/com/owncloud/android/datamodel/FilesystemDataProvider.java +++ b/src/main/java/com/owncloud/android/datamodel/FilesystemDataProvider.java @@ -61,7 +61,7 @@ public long countFilesThatNeedUploadInFolder(String localPath) { } } - public void storeOrUpdateFileValue(String localPath, long modifiedAt, boolean isFolder, boolean sentForUpload) { + public void storeOrUpdateFileValue(String localPath, long modifiedAt, boolean isFolder, boolean fileBeingModified) { FileSystemDataSet data = getFilesystemDataSet(localPath); int isFolderValue = 0; @@ -69,20 +69,23 @@ public void storeOrUpdateFileValue(String localPath, long modifiedAt, boolean is isFolderValue = 1; } - int isSentForUpload = 0; - if (sentForUpload) { - isSentForUpload = 1; + int isBeingModified = 0; + if (fileBeingModified) { + isBeingModified = 1; } ContentValues cv = new ContentValues(); - cv.put(ProviderMeta.ProviderTableMeta.FILESYSTEM_FILE_LOCAL_PATH, localPath); - cv.put(ProviderMeta.ProviderTableMeta.FILESYSTEM_FILE_MODIFIED, modifiedAt); - cv.put(ProviderMeta.ProviderTableMeta.FILESYSTEM_FILE_IS_FOLDER, isFolderValue); cv.put(ProviderMeta.ProviderTableMeta.FILESYSTEM_FILE_FOUND_RECENTLY, System.currentTimeMillis()); - cv.put(ProviderMeta.ProviderTableMeta.FILESYSTEM_FILE_SENT_FOR_UPLOAD, isSentForUpload); + cv.put(ProviderMeta.ProviderTableMeta.FILESYSTEM_FILE_BEING_MODIFIED, isBeingModified); if (data == null) { + cv.put(ProviderMeta.ProviderTableMeta.FILESYSTEM_FILE_LOCAL_PATH, localPath); + cv.put(ProviderMeta.ProviderTableMeta.FILESYSTEM_FILE_MODIFIED, modifiedAt); + cv.put(ProviderMeta.ProviderTableMeta.FILESYSTEM_FILE_IS_FOLDER, isFolderValue); + cv.put(ProviderMeta.ProviderTableMeta.FILESYSTEM_FILE_SENT_FOR_UPLOAD, 0); + + Uri result = contentResolver.insert(ProviderMeta.ProviderTableMeta.CONTENT_URI_FILESYSTEM, cv); if (result == null) { @@ -90,6 +93,10 @@ public void storeOrUpdateFileValue(String localPath, long modifiedAt, boolean is } } else { + if (data.getModifiedAt() != modifiedAt) { + cv.put(ProviderMeta.ProviderTableMeta.FILESYSTEM_FILE_SENT_FOR_UPLOAD, 0); + } + int result = contentResolver.update( ProviderMeta.ProviderTableMeta.CONTENT_URI_FILESYSTEM, cv, @@ -134,10 +141,18 @@ private FileSystemDataSet getFilesystemDataSet(String localPathParam) { isSentForUpload = true; } + boolean isBeingModified = false; + if (cursor.getInt(cursor.getColumnIndex( + ProviderMeta.ProviderTableMeta.FILESYSTEM_FILE_BEING_MODIFIED)) != 0) { + isBeingModified = true; + } + + if (id == -1) { Log_OC.e(TAG, "Arbitrary value could not be created from cursor"); } else { - dataSet = new FileSystemDataSet(id, localPath, modifiedAt, isFolder, isSentForUpload, foundAt); + dataSet = new FileSystemDataSet(id, localPath, modifiedAt, isFolder, isSentForUpload, foundAt, + isBeingModified); } } cursor.close(); diff --git a/src/main/java/com/owncloud/android/db/ProviderMeta.java b/src/main/java/com/owncloud/android/db/ProviderMeta.java index 299da9a95eb7..9871dd1ccf16 100644 --- a/src/main/java/com/owncloud/android/db/ProviderMeta.java +++ b/src/main/java/com/owncloud/android/db/ProviderMeta.java @@ -208,5 +208,6 @@ static public class ProviderTableMeta implements BaseColumns { public static final String FILESYSTEM_FILE_IS_FOLDER = "is_folder"; public static final String FILESYSTEM_FILE_FOUND_RECENTLY = "found_at"; public static final String FILESYSTEM_FILE_SENT_FOR_UPLOAD = "upload_triggered"; + public static final String FILESYSTEM_FILE_BEING_MODIFIED = "file_being_modified"; } } \ No newline at end of file diff --git a/src/main/java/com/owncloud/android/jobs/NewAutoUploadJob.java b/src/main/java/com/owncloud/android/jobs/NewAutoUploadJob.java index 69dd677e0468..a77152812e62 100644 --- a/src/main/java/com/owncloud/android/jobs/NewAutoUploadJob.java +++ b/src/main/java/com/owncloud/android/jobs/NewAutoUploadJob.java @@ -41,7 +41,12 @@ import org.lukhnos.nnio.file.SimpleFileVisitor; import org.lukhnos.nnio.file.attribute.BasicFileAttributes; +import java.io.File; import java.io.IOException; +import java.io.RandomAccessFile; +import java.nio.channels.FileChannel; +import java.nio.channels.FileLock; +import java.nio.channels.OverlappingFileLockException; import java.util.ArrayList; import java.util.List; @@ -85,6 +90,7 @@ protected Result onRunJob(Params params) { List syncedFolders = syncedFolderProvider.getSyncedFolders(); List syncedFoldersToDelete = new ArrayList<>(); + // be smart, and only traverse folders once instead of multiple times for (SyncedFolder syncedFolder : syncedFolders) { for (SyncedFolder secondarySyncedFolder : syncedFolders) { if (!syncedFolder.equals(secondarySyncedFolder) && @@ -98,6 +104,7 @@ protected Result onRunJob(Params params) { // delete all the folders from the list that we won't traverse syncedFolders.removeAll(syncedFoldersToDelete); + // store all files from the filesystem for (int i = 0; i < syncedFolders.size(); i++) { Path path = Paths.get(syncedFolders.get(i).getLocalPath()); @@ -106,8 +113,19 @@ protected Result onRunJob(Params params) { @Override public FileVisitResult visitFile(Path path, BasicFileAttributes attrs) throws IOException { - filesystemDataProvider.storeOrUpdateFileValue(path.toAbsolutePath().toString(), - attrs.lastModifiedTime().toMillis(), path.toFile().isDirectory(), false); + File file = path.toFile(); + FileChannel channel = new RandomAccessFile(file, "rw").getChannel(); + FileLock lock = channel.lock(); + try { + lock = channel.tryLock(); + filesystemDataProvider.storeOrUpdateFileValue(path.toAbsolutePath().toString(), + attrs.lastModifiedTime().toMillis(), file.isDirectory(), false); + } catch (OverlappingFileLockException e) { + filesystemDataProvider.storeOrUpdateFileValue(path.toAbsolutePath().toString(), + attrs.lastModifiedTime().toMillis(), file.isDirectory(), true); + } finally { + lock.release(); + } return FileVisitResult.CONTINUE; } diff --git a/src/main/java/com/owncloud/android/providers/FileContentProvider.java b/src/main/java/com/owncloud/android/providers/FileContentProvider.java index e624c4a05774..dd89be80469a 100644 --- a/src/main/java/com/owncloud/android/providers/FileContentProvider.java +++ b/src/main/java/com/owncloud/android/providers/FileContentProvider.java @@ -1247,6 +1247,7 @@ private void createFileSystemTable(SQLiteDatabase db) { + ProviderTableMeta.FILESYSTEM_FILE_IS_FOLDER + " INTEGER, " + ProviderTableMeta.FILESYSTEM_FILE_FOUND_RECENTLY + " LONG, " + ProviderTableMeta.FILESYSTEM_FILE_SENT_FOR_UPLOAD + " INTEGER, " + + ProviderTableMeta.FILESYSTEM_FILE_BEING_MODIFIED + " INTEGER, " + ProviderTableMeta.FILESYSTEM_FILE_MODIFIED + " LONG );" ); } From bd6d8e1b7d1d924ad52844d03a39722d1b721924 Mon Sep 17 00:00:00 2001 From: Mario Danic Date: Thu, 11 May 2017 21:30:51 +0200 Subject: [PATCH 039/206] Work a bit more on stuff Signed-off-by: Mario Danic --- src/main/AndroidManifest.xml | 2 +- .../java/com/owncloud/android/MainApp.java | 17 ++++- .../datamodel/FilesystemDataProvider.java | 58 ++++++++++++++- .../android/jobs/NewAutoUploadJob.java | 74 +++++++++++++++++++ .../providers/DocumentsStorageProvider.java | 2 +- .../providers/FileContentProvider.java | 27 +++++++ 6 files changed, 176 insertions(+), 4 deletions(-) rename src/main/java/{org/nextcloud => com/owncloud/android}/providers/DocumentsStorageProvider.java (99%) diff --git a/src/main/AndroidManifest.xml b/src/main/AndroidManifest.xml index 2ebc6236ae49..ec1c163fc443 100644 --- a/src/main/AndroidManifest.xml +++ b/src/main/AndroidManifest.xml @@ -160,7 +160,7 @@ android:label="@string/search_users_and_groups_hint" /> localPathsToUpload = new HashSet<>(); + + String likeParam = localPath + "%"; + + Cursor cursor = contentResolver.query( + ProviderMeta.ProviderTableMeta.CONTENT_URI_FILESYSTEM, + null, + ProviderMeta.ProviderTableMeta.FILESYSTEM_FILE_LOCAL_PATH + " LIKE ? and " + + ProviderMeta.ProviderTableMeta.FILESYSTEM_FILE_BEING_MODIFIED + " = ? and " + + ProviderMeta.ProviderTableMeta.FILESYSTEM_FILE_SENT_FOR_UPLOAD + " = ? and " + + ProviderMeta.ProviderTableMeta.FILESYSTEM_FILE_IS_FOLDER + " = ?", + new String[]{likeParam, "0", "0", "0"}, + null); + + if (cursor.moveToFirst()) { + do { + String value = cursor.getString(cursor.getColumnIndex( + ProviderMeta.ProviderTableMeta.FILESYSTEM_FILE_LOCAL_PATH)); + if (value == null) { + Log_OC.e(TAG, "Cannot get local path"); + } else { + localPathsToUpload.add(value); + } + } while (cursor.moveToNext()); + } + + + cursor.close(); + return localPathsToUpload.toArray(); + + } + public long countFilesThatNeedUploadInFolder(String localPath) { String likeParam = localPath + "%"; Cursor cursor = contentResolver.query( ProviderMeta.ProviderTableMeta.CONTENT_URI_FILESYSTEM, - new String[] {"count(*)"}, + new String[]{"count(*)"}, ProviderMeta.ProviderTableMeta.FILESYSTEM_FILE_LOCAL_PATH + " LIKE ?", new String[]{likeParam}, null); diff --git a/src/main/java/com/owncloud/android/jobs/NewAutoUploadJob.java b/src/main/java/com/owncloud/android/jobs/NewAutoUploadJob.java index a77152812e62..a886d9596863 100644 --- a/src/main/java/com/owncloud/android/jobs/NewAutoUploadJob.java +++ b/src/main/java/com/owncloud/android/jobs/NewAutoUploadJob.java @@ -23,16 +23,22 @@ import android.content.ContentResolver; import android.content.Context; +import android.media.ExifInterface; import android.os.PowerManager; import android.support.annotation.NonNull; +import android.text.TextUtils; import android.util.Log; import com.evernote.android.job.Job; +import com.evernote.android.job.JobRequest; +import com.evernote.android.job.util.support.PersistableBundleCompat; import com.owncloud.android.MainApp; import com.owncloud.android.datamodel.ArbitraryDataProvider; import com.owncloud.android.datamodel.FilesystemDataProvider; import com.owncloud.android.datamodel.SyncedFolder; import com.owncloud.android.datamodel.SyncedFolderProvider; +import com.owncloud.android.lib.common.utils.Log_OC; +import com.owncloud.android.utils.FileStorageUtils; import org.lukhnos.nnio.file.FileVisitResult; import org.lukhnos.nnio.file.Files; @@ -47,8 +53,15 @@ import java.nio.channels.FileChannel; import java.nio.channels.FileLock; import java.nio.channels.OverlappingFileLockException; +import java.text.ParsePosition; +import java.text.SimpleDateFormat; import java.util.ArrayList; +import java.util.Date; +import java.util.HashSet; import java.util.List; +import java.util.Locale; +import java.util.Set; +import java.util.TimeZone; /* This job is meant to run periodically every half an hour, and has the following burden on it's shoulders: @@ -88,6 +101,7 @@ protected Result onRunJob(Params params) { Long.toString(System.currentTimeMillis())); List syncedFolders = syncedFolderProvider.getSyncedFolders(); + List syncedFoldersOriginalList = syncedFolderProvider.getSyncedFolders(); List syncedFoldersToDelete = new ArrayList<>(); // be smart, and only traverse folders once instead of multiple times @@ -141,6 +155,66 @@ public FileVisitResult visitFileFailed(Path file, IOException exc) { } } + Set pathsToSet = new HashSet<>(); + + // get all files that we want to upload + for (SyncedFolder syncedFolder : syncedFoldersOriginalList) { + Object[] pathsToUpload = filesystemDataProvider.getFilesToUploadForPath(syncedFolder.getLocalPath()); + + for (Object pathToUpload : pathsToUpload) { + File file = new File((String) pathToUpload); + + String mimetypeString = FileStorageUtils.getMimeTypeFromName(file.getAbsolutePath()); + Long lastModificationTime = file.lastModified(); + final Locale currentLocale = context.getResources().getConfiguration().locale; + + if ("image/jpeg".equalsIgnoreCase(mimetypeString) || "image/tiff".equalsIgnoreCase(mimetypeString)) { + try { + ExifInterface exifInterface = new ExifInterface(file.getAbsolutePath()); + String exifDate = exifInterface.getAttribute(ExifInterface.TAG_DATETIME); + if (!TextUtils.isEmpty(exifDate)) { + ParsePosition pos = new ParsePosition(0); + SimpleDateFormat sFormatter = new SimpleDateFormat("yyyy:MM:dd HH:mm:ss", + currentLocale); + sFormatter.setTimeZone(TimeZone.getTimeZone(TimeZone.getDefault().getID())); + Date dateTime = sFormatter.parse(exifDate, pos); + lastModificationTime = dateTime.getTime(); + } + + } catch (IOException e) { + Log_OC.d(TAG, "Failed to get the proper time " + e.getLocalizedMessage()); + } + } + + PersistableBundleCompat bundle = new PersistableBundleCompat(); + bundle.putString(AutoUploadJob.LOCAL_PATH, file.getAbsolutePath()); + bundle.putString(AutoUploadJob.REMOTE_PATH, FileStorageUtils.getInstantUploadFilePath( + currentLocale, + syncedFolder.getRemotePath(), file.getName(), + lastModificationTime, + syncedFolder.getSubfolderByDate())); + bundle.putString(AutoUploadJob.ACCOUNT, syncedFolder.getAccount()); + bundle.putInt(AutoUploadJob.UPLOAD_BEHAVIOUR, syncedFolder.getUploadAction()); + + pathsToSet.add((String) pathToUpload); + + new JobRequest.Builder(AutoUploadJob.TAG) + .setExecutionWindow(30_000L, 80_000L) + .setRequiresCharging(syncedFolder.getChargingOnly()) + .setRequiredNetworkType(syncedFolder.getWifiOnly() ? JobRequest.NetworkType.UNMETERED : + JobRequest.NetworkType.ANY) + .setExtras(bundle) + .setPersisted(false) + .setRequirementsEnforced(true) + .setUpdateCurrent(false) + .build() + .schedule(); + } + } + + // set them as sent for upload + filesystemDataProvider.updateFilesInList(pathsToSet.toArray()); + wakeLock.release(); return Result.SUCCESS; } diff --git a/src/main/java/org/nextcloud/providers/DocumentsStorageProvider.java b/src/main/java/com/owncloud/android/providers/DocumentsStorageProvider.java similarity index 99% rename from src/main/java/org/nextcloud/providers/DocumentsStorageProvider.java rename to src/main/java/com/owncloud/android/providers/DocumentsStorageProvider.java index ca94302ec1e7..07dd5a93d93e 100644 --- a/src/main/java/org/nextcloud/providers/DocumentsStorageProvider.java +++ b/src/main/java/com/owncloud/android/providers/DocumentsStorageProvider.java @@ -18,7 +18,7 @@ * */ -package org.nextcloud.providers; +package com.owncloud.android.providers; import android.accounts.Account; import android.annotation.TargetApi; diff --git a/src/main/java/com/owncloud/android/providers/FileContentProvider.java b/src/main/java/com/owncloud/android/providers/FileContentProvider.java index dd89be80469a..c071593fbff5 100644 --- a/src/main/java/com/owncloud/android/providers/FileContentProvider.java +++ b/src/main/java/com/owncloud/android/providers/FileContentProvider.java @@ -74,6 +74,7 @@ public class FileContentProvider extends ContentProvider { private static final int EXTERNAL_LINKS = 8; private static final int ARBITRARY_DATA = 9; private static final int VIRTUAL = 10; + private static final int FILESYSTEM = 11; private static final String TAG = FileContentProvider.class.getSimpleName(); @@ -209,6 +210,9 @@ private int delete(SQLiteDatabase db, Uri uri, String where, String[] whereArgs) case VIRTUAL: count = db.delete(ProviderTableMeta.VIRTUAL_TABLE_NAME, where, whereArgs); break; + case FILESYSTEM: + count = db.delete(ProviderTableMeta.FILESYSTEM_TABLE_NAME, where, whereArgs); + break; default: //Log_OC.e(TAG, "Unknown uri " + uri); throw new IllegalArgumentException("Unknown uri: " + uri.toString()); @@ -365,6 +369,18 @@ private Uri insert(SQLiteDatabase db, Uri uri, ContentValues values) { } return insertedVirtualUri; + case FILESYSTEM: + Uri insertedFilesystemUri = null; + long filesystedId = db.insert(ProviderTableMeta.FILESYSTEM_TABLE_NAME, null, values); + if (filesystedId > 0) { + insertedFilesystemUri = + ContentUris.withAppendedId(ProviderTableMeta.CONTENT_URI_FILESYSTEM, filesystedId); + } else { + throw new SQLException("ERROR " + uri); + + } + return insertedFilesystemUri; + default: throw new IllegalArgumentException("Unknown uri id: " + uri); } @@ -417,6 +433,7 @@ public boolean onCreate() { mUriMatcher.addURI(authority, "external_links", EXTERNAL_LINKS); mUriMatcher.addURI(authority, "arbitrary_data", ARBITRARY_DATA); mUriMatcher.addURI(authority, "virtual", VIRTUAL); + mUriMatcher.addURI(authority, "filesystem", FILESYSTEM); return true; } @@ -518,6 +535,13 @@ private Cursor query( sqlQuery.appendWhere(ProviderTableMeta._ID + "=" + uri.getPathSegments().get(1)); } break; + case FILESYSTEM: + sqlQuery.setTables(ProviderTableMeta.FILESYSTEM_TABLE_NAME); + if (uri.getPathSegments().size() > 1) { + sqlQuery.appendWhere(ProviderTableMeta._ID + "=" + + uri.getPathSegments().get(1)); + } + break; default: throw new IllegalArgumentException("Unknown uri id: " + uri); } @@ -549,6 +573,9 @@ private Cursor query( default: // Files order = ProviderTableMeta.FILE_DEFAULT_SORT_ORDER; break; + case FILESYSTEM: + order = ProviderTableMeta.FILESYSTEM_FILE_LOCAL_PATH; + break; } } else { order = sortOrder; From 50b100f936cb9d901dd2674872ee32293e6219e6 Mon Sep 17 00:00:00 2001 From: Mario Danic Date: Mon, 15 May 2017 12:51:03 +0200 Subject: [PATCH 040/206] Add onClick Signed-off-by: Mario Danic --- .../datamodel/FilesystemDataProvider.java | 2 +- .../ui/activity/FolderSyncActivity.java | 13 ++++++++++- .../android/ui/adapter/FolderSyncAdapter.java | 11 ++++++++- .../android/ui/events/CustomFolderEvent.java | 23 +++++++++++++++++++ 4 files changed, 46 insertions(+), 3 deletions(-) create mode 100644 src/main/java/com/owncloud/android/ui/events/CustomFolderEvent.java diff --git a/src/main/java/com/owncloud/android/datamodel/FilesystemDataProvider.java b/src/main/java/com/owncloud/android/datamodel/FilesystemDataProvider.java index 085325f2a29e..139704de733b 100644 --- a/src/main/java/com/owncloud/android/datamodel/FilesystemDataProvider.java +++ b/src/main/java/com/owncloud/android/datamodel/FilesystemDataProvider.java @@ -55,7 +55,7 @@ public int updateFilesInList(Object[] paths) { int result = contentResolver.update( ProviderMeta.ProviderTableMeta.CONTENT_URI_FILESYSTEM, cv, - ProviderMeta.ProviderTableMeta.FILESYSTEM_FILE_LOCAL_PATH + "IN (?)", + ProviderMeta.ProviderTableMeta.FILESYSTEM_FILE_LOCAL_PATH + " IN (?)", stringPaths ); diff --git a/src/main/java/com/owncloud/android/ui/activity/FolderSyncActivity.java b/src/main/java/com/owncloud/android/ui/activity/FolderSyncActivity.java index 1969d07c58a1..7334aedef1c2 100644 --- a/src/main/java/com/owncloud/android/ui/activity/FolderSyncActivity.java +++ b/src/main/java/com/owncloud/android/ui/activity/FolderSyncActivity.java @@ -34,6 +34,7 @@ import android.support.v7.app.ActionBar; import android.support.v7.widget.GridLayoutManager; import android.support.v7.widget.RecyclerView; +import android.util.Log; import android.view.MenuItem; import android.view.View; import android.widget.LinearLayout; @@ -48,17 +49,21 @@ import com.owncloud.android.datamodel.SyncedFolder; import com.owncloud.android.datamodel.SyncedFolderDisplayItem; import com.owncloud.android.datamodel.SyncedFolderProvider; -import com.owncloud.android.lib.common.utils.Log_OC; import com.owncloud.android.files.services.FileUploader; +import com.owncloud.android.lib.common.utils.Log_OC; import com.owncloud.android.ui.adapter.FolderSyncAdapter; import com.owncloud.android.ui.decoration.MediaGridItemDecoration; import com.owncloud.android.ui.dialog.SyncedFolderPreferencesDialogFragment; import com.owncloud.android.ui.dialog.parcel.SyncedFolderParcelable; +import com.owncloud.android.ui.events.CustomFolderEvent; import com.owncloud.android.utils.AnalyticsUtils; import com.owncloud.android.utils.DisplayUtils; import com.owncloud.android.utils.PermissionUtil; import com.owncloud.android.utils.ThemeUtils; +import org.greenrobot.eventbus.Subscribe; +import org.greenrobot.eventbus.ThreadMode; + import java.io.File; import java.util.ArrayList; import java.util.Collections; @@ -539,4 +544,10 @@ public void onRequestPermissionsResult(int requestCode, @NonNull String permissi super.onRequestPermissionsResult(requestCode, permissions, grantResults); } } + + @Subscribe(threadMode = ThreadMode.MAIN) + public void onMessageEvent(CustomFolderEvent event) { + Log.d(TAG, "Show custom folder magic here"); + }; + } diff --git a/src/main/java/com/owncloud/android/ui/adapter/FolderSyncAdapter.java b/src/main/java/com/owncloud/android/ui/adapter/FolderSyncAdapter.java index f82dc397485f..c2d793c18535 100644 --- a/src/main/java/com/owncloud/android/ui/adapter/FolderSyncAdapter.java +++ b/src/main/java/com/owncloud/android/ui/adapter/FolderSyncAdapter.java @@ -37,8 +37,12 @@ import com.owncloud.android.datamodel.MediaFolder; import com.owncloud.android.datamodel.SyncedFolderDisplayItem; import com.owncloud.android.datamodel.ThumbnailsCacheManager; +import com.owncloud.android.ui.events.CustomFolderEvent; +import com.owncloud.android.utils.DisplayUtils; import com.owncloud.android.utils.ThemeUtils; +import org.greenrobot.eventbus.EventBus; + import java.io.File; import java.util.ArrayList; import java.util.List; @@ -150,7 +154,12 @@ public void onClick(View v) { } else { holder.mainHeaderContainer.setVisibility(View.GONE); holder.customFolderHeaderContainer.setVisibility(View.VISIBLE); - + holder.customFolderHeaderContainer.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + EventBus.getDefault().post(new CustomFolderEvent()); + } + }); } } diff --git a/src/main/java/com/owncloud/android/ui/events/CustomFolderEvent.java b/src/main/java/com/owncloud/android/ui/events/CustomFolderEvent.java new file mode 100644 index 000000000000..6d85081e204c --- /dev/null +++ b/src/main/java/com/owncloud/android/ui/events/CustomFolderEvent.java @@ -0,0 +1,23 @@ +/** + * Nextcloud Android client application + * + * @author Mario Danic + * Copyright (C) 2017 Mario Danic + * + * 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 . + */ +package com.owncloud.android.ui.events; + +public class CustomFolderEvent { +} From 62b61eccbf8433986986ec673ff9efc0250df202 Mon Sep 17 00:00:00 2001 From: Mario Danic Date: Mon, 15 May 2017 14:43:54 +0200 Subject: [PATCH 041/206] Revert "Maybe fix modified build" This reverts commit 8dee57a90e3a8d0f7e7282bb0a9e045a0bfd55af. --- src/gplay/AndroidManifest.xml | 4 ++++ src/modified/AndroidManifest.xml | 9 +++++++++ 2 files changed, 13 insertions(+) diff --git a/src/gplay/AndroidManifest.xml b/src/gplay/AndroidManifest.xml index 8e252d1de17c..bc5d14f61cee 100644 --- a/src/gplay/AndroidManifest.xml +++ b/src/gplay/AndroidManifest.xml @@ -59,6 +59,10 @@ + + + + + + Date: Mon, 15 May 2017 18:25:42 +0200 Subject: [PATCH 042/206] open folder sync preference for new, custom folder (work in progress) --- .../ui/activity/FolderSyncActivity.java | 5 + ...SyncedFolderPreferencesDialogFragment.java | 44 ++++-- .../dialog/parcel/SyncedFolderParcelable.java | 12 ++ .../res/layout/folder_sync_item_header.xml | 7 +- .../layout/folder_sync_settings_layout.xml | 133 ++++++++++++++---- src/main/res/values/strings.xml | 3 +- 6 files changed, 169 insertions(+), 35 deletions(-) diff --git a/src/main/java/com/owncloud/android/ui/activity/FolderSyncActivity.java b/src/main/java/com/owncloud/android/ui/activity/FolderSyncActivity.java index 7334aedef1c2..96a43f562b21 100644 --- a/src/main/java/com/owncloud/android/ui/activity/FolderSyncActivity.java +++ b/src/main/java/com/owncloud/android/ui/activity/FolderSyncActivity.java @@ -548,6 +548,11 @@ public void onRequestPermissionsResult(int requestCode, @NonNull String permissi @Subscribe(threadMode = ThreadMode.MAIN) public void onMessageEvent(CustomFolderEvent event) { Log.d(TAG, "Show custom folder magic here"); + SyncedFolderDisplayItem emptyCustomFolder = new SyncedFolderDisplayItem( + SyncedFolder.UNPERSISTED_ID, null, null, true, false, + false, AccountUtils.getCurrentOwnCloudAccount(this).name, + FileUploader.LOCAL_BEHAVIOUR_FORGET, false, null, MediaFolder.CUSTOM); + onSyncFolderSettingsClick(0, emptyCustomFolder); }; } diff --git a/src/main/java/com/owncloud/android/ui/dialog/SyncedFolderPreferencesDialogFragment.java b/src/main/java/com/owncloud/android/ui/dialog/SyncedFolderPreferencesDialogFragment.java index 772746f86ac1..37f630468f06 100644 --- a/src/main/java/com/owncloud/android/ui/dialog/SyncedFolderPreferencesDialogFragment.java +++ b/src/main/java/com/owncloud/android/ui/dialog/SyncedFolderPreferencesDialogFragment.java @@ -41,6 +41,7 @@ import android.widget.TextView; import com.owncloud.android.R; +import com.owncloud.android.datamodel.MediaFolder; import com.owncloud.android.datamodel.SyncedFolderDisplayItem; import com.owncloud.android.lib.common.utils.Log_OC; import com.owncloud.android.ui.activity.FolderPickerActivity; @@ -57,6 +58,7 @@ public class SyncedFolderPreferencesDialogFragment extends DialogFragment { public static final String SYNCED_FOLDER_PARCELABLE = "SyncedFolderParcelable"; private static final String BEHAVIOUR_DIALOG_STATE = "BEHAVIOUR_DIALOG_STATE"; public static final int REQUEST_CODE__SELECT_REMOTE_FOLDER = 0; + public static final int REQUEST_CODE__SELECT_LOCAL_FOLDER = 1; private CharSequence[] mUploadBehaviorItemStrings; @@ -67,6 +69,7 @@ public class SyncedFolderPreferencesDialogFragment extends DialogFragment { private AppCompatCheckBox mUploadUseSubfoldersCheckbox; private TextView mUploadBehaviorSummary; private TextView mLocalFolderPath; + private TextView mLocalFolderSummary; private TextView mRemoteFolderSummary; private SyncedFolderParcelable mSyncedFolder; @@ -132,12 +135,17 @@ public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle sa private void setupDialogElements(View view) { int accentColor = ThemeUtils.primaryAccentColor(); + if (mSyncedFolder.getType() > MediaFolder.CUSTOM) { + view.findViewById(R.id.local_folder_container).setVisibility(View.GONE); + } + // find/saves UI elements mEnabledSwitch = (SwitchCompat) view.findViewById(R.id.sync_enabled); ThemeUtils.tintSwitch(mEnabledSwitch, accentColor); mLocalFolderPath = (TextView) view.findViewById(R.id.folder_sync_settings_local_folder_path); + mLocalFolderSummary = (TextView) view.findViewById(R.id.local_folder_summary); mRemoteFolderSummary = (TextView) view.findViewById(R.id.remote_folder_summary); mUploadOnWifiCheckbox = (AppCompatCheckBox) view.findViewById(R.id.setting_instant_upload_on_wifi_checkbox); @@ -161,15 +169,25 @@ private void setupDialogElements(View view) { // Set values setEnabled(mSyncedFolder.getEnabled()); - mLocalFolderPath.setText( - DisplayUtils.createTextWithSpan( - String.format( - getString(R.string.folder_sync_preferences_folder_path), - mSyncedFolder.getLocalPath()), - mSyncedFolder.getFolderName(), - new StyleSpan(Typeface.BOLD))); - mRemoteFolderSummary.setText(mSyncedFolder.getRemotePath()); + if(mSyncedFolder.getLocalPath() != null && mSyncedFolder.getLocalPath().length() > 0) { + mLocalFolderPath.setText( + DisplayUtils.createTextWithSpan( + String.format( + getString(R.string.folder_sync_preferences_folder_path), + mSyncedFolder.getLocalPath()), + mSyncedFolder.getFolderName(), + new StyleSpan(Typeface.BOLD))); + mLocalFolderSummary.setText(mSyncedFolder.getLocalPath()); + } else { + mLocalFolderSummary.setText(R.string.choose_local_folder); + } + + if(mSyncedFolder.getLocalPath() != null && mSyncedFolder.getLocalPath().length() > 0) { + mRemoteFolderSummary.setText(mSyncedFolder.getRemotePath()); + } else { + mRemoteFolderSummary.setText(R.string.choose_remote_folder); + } mUploadOnWifiCheckbox.setChecked(mSyncedFolder.getWifiOnly()); mUploadOnChargingCheckbox.setChecked(mSyncedFolder.getChargingOnly()); @@ -246,6 +264,16 @@ public void onClick(View v) { } }); + view.findViewById(R.id.local_folder_container).setOnClickListener(new OnClickListener() { + @Override + public void onClick(View v) { + Intent action = new Intent(getActivity(), FolderPickerActivity.class); + action.putExtra( + FolderPickerActivity.EXTRA_ACTION, getResources().getText(R.string.choose_remote_folder)); + getActivity().startActivityForResult(action, REQUEST_CODE__SELECT_LOCAL_FOLDER); + } + }); + view.findViewById(R.id.sync_enabled).setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { diff --git a/src/main/java/com/owncloud/android/ui/dialog/parcel/SyncedFolderParcelable.java b/src/main/java/com/owncloud/android/ui/dialog/parcel/SyncedFolderParcelable.java index f5d9f499ba57..0d18e4e063a8 100644 --- a/src/main/java/com/owncloud/android/ui/dialog/parcel/SyncedFolderParcelable.java +++ b/src/main/java/com/owncloud/android/ui/dialog/parcel/SyncedFolderParcelable.java @@ -38,6 +38,7 @@ public class SyncedFolderParcelable implements Parcelable { private Boolean mEnabled = false; private Boolean mSubfolderByDate = false; private Integer mUploadAction; + private int mType; private long mId; private String mAccount; private int mSection; @@ -54,6 +55,7 @@ public SyncedFolderParcelable(SyncedFolderDisplayItem syncedFolderDisplayItem, i mChargingOnly = syncedFolderDisplayItem.getChargingOnly(); mEnabled = syncedFolderDisplayItem.isEnabled(); mSubfolderByDate = syncedFolderDisplayItem.getSubfolderByDate(); + mType = syncedFolderDisplayItem.getType(); mAccount = syncedFolderDisplayItem.getAccount(); mUploadAction = syncedFolderDisplayItem.getUploadAction(); mSection = section; @@ -68,6 +70,7 @@ private SyncedFolderParcelable(Parcel read) { mChargingOnly = read.readInt() != 0; mEnabled = read.readInt() != 0; mSubfolderByDate = read.readInt() != 0; + mType = read.readInt(); mAccount = read.readString(); mUploadAction = read.readInt(); mSection = read.readInt(); @@ -83,6 +86,7 @@ public void writeToParcel(Parcel dest, int flags) { dest.writeInt(mChargingOnly ? 1 : 0); dest.writeInt(mEnabled ? 1 : 0); dest.writeInt(mSubfolderByDate ? 1 : 0); + dest.writeInt(mType); dest.writeString(mAccount); dest.writeInt(mUploadAction); dest.writeInt(mSection); @@ -163,6 +167,14 @@ public void setSubfolderByDate(Boolean mSubfolderByDate) { this.mSubfolderByDate = mSubfolderByDate; } + public int getType() { + return mType; + } + + public void setType(int mType) { + this.mType = mType; + } + public Integer getUploadAction() { return mUploadAction; } diff --git a/src/main/res/layout/folder_sync_item_header.xml b/src/main/res/layout/folder_sync_item_header.xml index 2bd055162019..dd0d73113448 100644 --- a/src/main/res/layout/folder_sync_item_header.xml +++ b/src/main/res/layout/folder_sync_item_header.xml @@ -94,13 +94,15 @@ android:layout_height="wrap_content" android:minHeight="48dp"> - diff --git a/src/main/res/layout/folder_sync_settings_layout.xml b/src/main/res/layout/folder_sync_settings_layout.xml index a709bcef1dbd..6411526f839a 100644 --- a/src/main/res/layout/folder_sync_settings_layout.xml +++ b/src/main/res/layout/folder_sync_settings_layout.xml @@ -18,8 +18,8 @@ You should have received a copy of the GNU Affero General Public License along with this program. If not, see . --> - + android:paddingTop="@dimen/standard_padding"> @@ -62,8 +61,8 @@ android:layout_height="match_parent" android:gravity="end|top" android:orientation="vertical" - android:paddingTop="@dimen/standard_padding" - android:paddingLeft="@dimen/standard_padding"> + android:paddingLeft="@dimen/standard_padding" + android:paddingTop="@dimen/standard_padding"> + android:minHeight="?attr/listPreferredItemHeightSmall"> - + android:layout_weight="1" + android:paddingBottom="@dimen/standard_padding" + android:paddingTop="@dimen/standard_padding"> - + + + + + + + + + + + + + + + + + + android:layout_weight="1" + android:paddingBottom="@dimen/standard_padding" + android:paddingTop="@dimen/standard_padding"> + + + + + + + + + + + + + diff --git a/src/main/res/values/strings.xml b/src/main/res/values/strings.xml index e4c7a6848a63..5f4df5274386 100644 --- a/src/main/res/values/strings.xml +++ b/src/main/res/values/strings.xml @@ -582,7 +582,8 @@ <font color=\"%1$s\"><a href=\"%2$s\">CONTRIBUTING.md</a></font> Move to… Copy to… - Choose folder… + Choose remote folder… + Choose local folder… Loading folders… No media folders found. Preferences for auto uploading From 8e6c80c96afbe501069253edffe8dd6d4ceb20bc Mon Sep 17 00:00:00 2001 From: AndyScherzinger Date: Wed, 24 May 2017 15:38:11 +0200 Subject: [PATCH 043/206] folder open icon --- src/main/res/drawable-hdpi/ic_folder_open.png | Bin 0 -> 367 bytes src/main/res/drawable-mdpi/ic_folder_open.png | Bin 0 -> 278 bytes src/main/res/drawable-xhdpi/ic_folder_open.png | Bin 0 -> 289 bytes src/main/res/drawable-xxhdpi/ic_folder_open.png | Bin 0 -> 442 bytes 4 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 src/main/res/drawable-hdpi/ic_folder_open.png create mode 100644 src/main/res/drawable-mdpi/ic_folder_open.png create mode 100644 src/main/res/drawable-xhdpi/ic_folder_open.png create mode 100644 src/main/res/drawable-xxhdpi/ic_folder_open.png diff --git a/src/main/res/drawable-hdpi/ic_folder_open.png b/src/main/res/drawable-hdpi/ic_folder_open.png new file mode 100644 index 0000000000000000000000000000000000000000..b5e1e0dc66efc5fed44444a98852998263e292d8 GIT binary patch literal 367 zcmeAS@N?(olHy`uVBq!ia0vp^Dj>|k1|%Oc%$NbB*pj^6U4S$Y{B+)352QE?JR*yM zvQ&rUPlPeufHm>3$&Rm#2$kNW|f{(+#r@I|#V`k8d~W=1-XKJayHb z!w2`Zq<9GKX$$^b=>eMCxzqlF8c)+Z%^!sbO@ZXzqPR7 zob9*T|9(ZR36*aX1q*X;?|UjT>*zv_F8)c;44(s+E6zW)$>n-!dyJDB=dm^Qw;GnL zUX?Z7TjOU+X{`6UW6BmxKek-kXZ8Ht!XE|;4@^*PSo{4S(*sqJvv2=?awrmhp09qz zgm?Y+-E%)wcbrgKcfZ!jImDwCh~D*bt-P>b*y|eqq~#muwb3(_PFVdQ&MBb@0QB3I A)&Kwi literal 0 HcmV?d00001 diff --git a/src/main/res/drawable-mdpi/ic_folder_open.png b/src/main/res/drawable-mdpi/ic_folder_open.png new file mode 100644 index 0000000000000000000000000000000000000000..0a53eea0dfc6297eecf708389ad009ed27aeea6f GIT binary patch literal 278 zcmeAS@N?(olHy`uVBq!ia0vp^5+KaM1|%Pp+x`GjY)RhkE)4%caKYZ?lYt_f1s;*b z3=G`DAk4@xYYs>cdx@v7EBixMAs!hfrTFdNfI^EsT^vI^j=v2($jfBNvsqBOM7C^4 z>Ll~l^x26C9LHYrJS=u)mkIL!QulRrs8V#jABwyEU7Tp@jxKP_3qun*Sy~j2}m$GCw!djDOS#Jn#Xx}Nf?e-q?rZm$d4E{W=qg;f?X;VTi_fMR3t#xIedVaIM0vs{ zWfqPtIe89ZJ6T-G@yGywoMXI(3)EF2VS{N990fib~ zFff!FFfhDIU|_JC!N4G1FlSew4N!t9$=lt9;eUJonf*W>XMsm#F#`j)FbFd;%$g&? zz`$ta>EalYaqsOddoN~3k>elVyLfU`FsX`nxQIA*n`}F%ePd~7>!f2 zCwOD)I*fa&zc0AE^m%QZnh_cYqBuI%5zm-~7L z7jMaoZKXf7KUCC(UweMso=GwlWVw(>Zy=Zg5gHp#+x?xPH|OWkdpwVy@bH>Xnv!Vr zB2{_8^OOt9@%Jp}+o(1#Gm)rx7GvZcovM5;zopr0QZKgkpKVy literal 0 HcmV?d00001 From 831a498dc5d41efa2d98f0dddc266d2b72afacab Mon Sep 17 00:00:00 2001 From: AndyScherzinger Date: Thu, 25 May 2017 11:49:33 +0200 Subject: [PATCH 044/206] fix import --- .../ui/fragment/contactsbackup/ContactsBackupFragment.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/com/owncloud/android/ui/fragment/contactsbackup/ContactsBackupFragment.java b/src/main/java/com/owncloud/android/ui/fragment/contactsbackup/ContactsBackupFragment.java index 0d91671d5dcc..49832fa71dc2 100644 --- a/src/main/java/com/owncloud/android/ui/fragment/contactsbackup/ContactsBackupFragment.java +++ b/src/main/java/com/owncloud/android/ui/fragment/contactsbackup/ContactsBackupFragment.java @@ -49,9 +49,9 @@ import com.owncloud.android.datamodel.ArbitraryDataProvider; import com.owncloud.android.datamodel.FileDataStorageManager; import com.owncloud.android.datamodel.OCFile; +import com.owncloud.android.jobs.ContactsBackupJob; import com.owncloud.android.lib.common.operations.RemoteOperationResult; import com.owncloud.android.operations.RefreshFolderOperation; -import com.owncloud.android.services.ContactsBackupJob; import com.owncloud.android.ui.activity.ContactsPreferenceActivity; import com.owncloud.android.ui.activity.Preferences; import com.owncloud.android.ui.fragment.FileFragment; From b62ea93140088fab545547856a45d8f950d6f420 Mon Sep 17 00:00:00 2001 From: AndyScherzinger Date: Thu, 25 May 2017 12:13:18 +0200 Subject: [PATCH 045/206] fix type --- src/gplay/java/com/owncloud/android/utils/PushUtils.java | 2 +- src/modified/java/com/owncloud/android/utils/PushUtils.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/gplay/java/com/owncloud/android/utils/PushUtils.java b/src/gplay/java/com/owncloud/android/utils/PushUtils.java index c0fec0eac56c..f87659c42e79 100644 --- a/src/gplay/java/com/owncloud/android/utils/PushUtils.java +++ b/src/gplay/java/com/owncloud/android/utils/PushUtils.java @@ -153,7 +153,7 @@ private static void deleteRegistrationForAccount(Account account) { if (remoteOperationResult.getHttpCode() == HttpStatus.SC_ACCEPTED) { String arbitraryValue; - if (!TextUtils.isEmpty(arbitraryValue = arbitraryDataProvider.getValue(account, KEY_PUSH))) { + if (!TextUtils.isEmpty(arbitraryValue = arbitraryDataProvider.getValue(account.name, KEY_PUSH))) { Gson gson = new Gson(); PushConfigurationState pushArbitraryData = gson.fromJson(arbitraryValue, PushConfigurationState.class); diff --git a/src/modified/java/com/owncloud/android/utils/PushUtils.java b/src/modified/java/com/owncloud/android/utils/PushUtils.java index 0fd4c7e7e473..850e301a3fec 100644 --- a/src/modified/java/com/owncloud/android/utils/PushUtils.java +++ b/src/modified/java/com/owncloud/android/utils/PushUtils.java @@ -153,7 +153,7 @@ private static void deleteRegistrationForAccount(Account account) { if (remoteOperationResult.getHttpCode() == HttpStatus.SC_ACCEPTED) { String arbitraryValue; - if (!TextUtils.isEmpty(arbitraryValue = arbitraryDataProvider.getValue(account, KEY_PUSH))) { + if (!TextUtils.isEmpty(arbitraryValue = arbitraryDataProvider.getValue(account.name, KEY_PUSH))) { Gson gson = new Gson(); PushConfigurationState pushArbitraryData = gson.fromJson(arbitraryValue, PushConfigurationState.class); From b9edd31e8f2bed4c7c51a71a37ee4c42b608ebd7 Mon Sep 17 00:00:00 2001 From: AndyScherzinger Date: Fri, 26 May 2017 11:15:05 +0200 Subject: [PATCH 046/206] fix PushUtils after rebase --- src/gplay/java/com/owncloud/android/utils/PushUtils.java | 4 ++-- src/modified/java/com/owncloud/android/utils/PushUtils.java | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/gplay/java/com/owncloud/android/utils/PushUtils.java b/src/gplay/java/com/owncloud/android/utils/PushUtils.java index f87659c42e79..009a0d905ef6 100644 --- a/src/gplay/java/com/owncloud/android/utils/PushUtils.java +++ b/src/gplay/java/com/owncloud/android/utils/PushUtils.java @@ -153,7 +153,7 @@ private static void deleteRegistrationForAccount(Account account) { if (remoteOperationResult.getHttpCode() == HttpStatus.SC_ACCEPTED) { String arbitraryValue; - if (!TextUtils.isEmpty(arbitraryValue = arbitraryDataProvider.getValue(account.name, KEY_PUSH))) { + if (!TextUtils.isEmpty(arbitraryValue = arbitraryDataProvider.getValue(account, KEY_PUSH))) { Gson gson = new Gson(); PushConfigurationState pushArbitraryData = gson.fromJson(arbitraryValue, PushConfigurationState.class); @@ -244,7 +244,7 @@ public static void pushRegistrationToServer() { PushConfigurationState pushArbitraryData = new PushConfigurationState(token, pushResponse.getDeviceIdentifier(), pushResponse.getSignature(), pushResponse.getPublicKey(), false); - arbitraryDataProvider.storeOrUpdateKeyValue(account, KEY_PUSH, + arbitraryDataProvider.storeOrUpdateKeyValue(account.name, KEY_PUSH, gson.toJson(pushArbitraryData)); } } diff --git a/src/modified/java/com/owncloud/android/utils/PushUtils.java b/src/modified/java/com/owncloud/android/utils/PushUtils.java index 850e301a3fec..d2df5b7912b3 100644 --- a/src/modified/java/com/owncloud/android/utils/PushUtils.java +++ b/src/modified/java/com/owncloud/android/utils/PushUtils.java @@ -153,7 +153,7 @@ private static void deleteRegistrationForAccount(Account account) { if (remoteOperationResult.getHttpCode() == HttpStatus.SC_ACCEPTED) { String arbitraryValue; - if (!TextUtils.isEmpty(arbitraryValue = arbitraryDataProvider.getValue(account.name, KEY_PUSH))) { + if (!TextUtils.isEmpty(arbitraryValue = arbitraryDataProvider.getValue(account, KEY_PUSH))) { Gson gson = new Gson(); PushConfigurationState pushArbitraryData = gson.fromJson(arbitraryValue, PushConfigurationState.class); @@ -244,7 +244,7 @@ public static void pushRegistrationToServer() { PushConfigurationState pushArbitraryData = new PushConfigurationState(token, pushResponse.getDeviceIdentifier(), pushResponse.getSignature(), pushResponse.getPublicKey(), false); - arbitraryDataProvider.storeOrUpdateKeyValue(account, KEY_PUSH, + arbitraryDataProvider.storeOrUpdateKeyValue(account.name, KEY_PUSH, gson.toJson(pushArbitraryData)); } } From dc213744fdfc6034eab486b49d399d28c1ec13d1 Mon Sep 17 00:00:00 2001 From: AndyScherzinger Date: Sat, 27 May 2017 11:42:46 +0200 Subject: [PATCH 047/206] fix file activity --- src/modified/AndroidManifest.xml | 9 --------- 1 file changed, 9 deletions(-) diff --git a/src/modified/AndroidManifest.xml b/src/modified/AndroidManifest.xml index 890ae33854c7..0a0f8fcc7c0c 100644 --- a/src/modified/AndroidManifest.xml +++ b/src/modified/AndroidManifest.xml @@ -60,15 +60,6 @@ - - - - Date: Sun, 28 May 2017 00:12:15 +0200 Subject: [PATCH 048/206] implement correct local/remote folder picking in preference dialog --- .../ui/activity/FolderSyncActivity.java | 8 +++-- .../ui/activity/UploadFilesActivity.java | 29 +++++++++++++++++-- .../ui/adapter/LocalFileListAdapter.java | 23 ++++++++++++--- ...SyncedFolderPreferencesDialogFragment.java | 20 +++++++++---- .../ui/fragment/LocalFileListFragment.java | 16 ++++++++-- src/main/res/layout/upload_files_layout.xml | 7 ++--- src/main/res/values/strings.xml | 1 + 7 files changed, 85 insertions(+), 19 deletions(-) diff --git a/src/main/java/com/owncloud/android/ui/activity/FolderSyncActivity.java b/src/main/java/com/owncloud/android/ui/activity/FolderSyncActivity.java index 96a43f562b21..d010942fe956 100644 --- a/src/main/java/com/owncloud/android/ui/activity/FolderSyncActivity.java +++ b/src/main/java/com/owncloud/android/ui/activity/FolderSyncActivity.java @@ -456,8 +456,12 @@ protected void onActivityResult(int requestCode, int resultCode, Intent data) { && resultCode == RESULT_OK && mSyncedFolderPreferencesDialogFragment != null) { OCFile chosenFolder = data.getParcelableExtra(FolderPickerActivity.EXTRA_FOLDER); mSyncedFolderPreferencesDialogFragment.setRemoteFolderSummary(chosenFolder.getRemotePath()); - - } else { + } if (requestCode == SyncedFolderPreferencesDialogFragment.REQUEST_CODE__SELECT_LOCAL_FOLDER + && resultCode == RESULT_OK && mSyncedFolderPreferencesDialogFragment != null) { + String localPath = data.getStringExtra(UploadFilesActivity.EXTRA_CHOSEN_FILES); + mSyncedFolderPreferencesDialogFragment.setLocalFolderSummary(localPath); + } + else { super.onActivityResult(requestCode, resultCode, data); } } diff --git a/src/main/java/com/owncloud/android/ui/activity/UploadFilesActivity.java b/src/main/java/com/owncloud/android/ui/activity/UploadFilesActivity.java index 049b566617b5..2e90246094c8 100644 --- a/src/main/java/com/owncloud/android/ui/activity/UploadFilesActivity.java +++ b/src/main/java/com/owncloud/android/ui/activity/UploadFilesActivity.java @@ -75,6 +75,7 @@ public class UploadFilesActivity extends FileActivity implements private ArrayAdapter mDirectories; private File mCurrentDir = null; private boolean mSelectAll = false; + private boolean mLocalFolderPickerMode = false; private LocalFileListFragment mFileListFragment; private Button mCancelBtn; protected Button mUploadBtn; @@ -88,6 +89,10 @@ public class UploadFilesActivity extends FileActivity implements public static final String EXTRA_CHOSEN_FILES = UploadFilesActivity.class.getCanonicalName() + ".EXTRA_CHOSEN_FILES"; + public static final String EXTRA_ACTION = UploadFilesActivity.class.getCanonicalName() + ".EXTRA_ACTION"; + public final static String KEY_LOCAL_FOLDER_PICKER_MODE = UploadFilesActivity.class.getCanonicalName() + + ".LOCAL_FOLDER_PICKER_MODE"; + public static final int RESULT_OK_AND_MOVE = RESULT_FIRST_USER; public static final int RESULT_OK_AND_DO_NOTHING = 2; public static final int RESULT_OK_AND_DELETE = 3; @@ -106,6 +111,11 @@ public void onCreate(Bundle savedInstanceState) { Log_OC.d(TAG, "onCreate() start"); super.onCreate(savedInstanceState); + Bundle extras = getIntent().getExtras(); + if (extras != null) { + mLocalFolderPickerMode = extras.getBoolean(KEY_LOCAL_FOLDER_PICKER_MODE, false); + } + if(savedInstanceState != null) { mCurrentDir = new File(savedInstanceState.getString(UploadFilesActivity.KEY_DIRECTORY_PATH, Environment .getExternalStorageDirectory().getAbsolutePath())); @@ -130,9 +140,14 @@ public void onCreate(Bundle savedInstanceState) { // Inflate and set the layout view setContentView(R.layout.upload_files_layout); + if (mLocalFolderPickerMode) { + findViewById(R.id.upload_options).setVisibility(View.GONE); + ((AppCompatButton) findViewById(R.id.upload_files_btn_upload)) + .setText(R.string.uploader_btn_alternative_text); + } + mFileListFragment = (LocalFileListFragment) getSupportFragmentManager().findFragmentById(R.id.local_files_list); - // Set input controllers mCancelBtn = (Button) findViewById(R.id.upload_files_btn_cancel); mCancelBtn.setOnClickListener(this); @@ -432,7 +447,17 @@ public void onClick(View v) { finish(); } else if (v.getId() == R.id.upload_files_btn_upload) { - new CheckAvailableSpaceTask().execute(mBehaviourSpinner.getSelectedItemPosition()==0); + if(mLocalFolderPickerMode) { + Intent data = new Intent(); + if(mCurrentDir != null) { + data.putExtra(EXTRA_CHOSEN_FILES, mCurrentDir.getAbsolutePath()); + } + setResult(RESULT_OK, data); + + finish(); + } else { + new CheckAvailableSpaceTask().execute(mBehaviourSpinner.getSelectedItemPosition() == 0); + } } } diff --git a/src/main/java/com/owncloud/android/ui/adapter/LocalFileListAdapter.java b/src/main/java/com/owncloud/android/ui/adapter/LocalFileListAdapter.java index 69f036585bea..01a683642318 100644 --- a/src/main/java/com/owncloud/android/ui/adapter/LocalFileListAdapter.java +++ b/src/main/java/com/owncloud/android/ui/adapter/LocalFileListAdapter.java @@ -41,6 +41,7 @@ import com.owncloud.android.utils.MimeTypeUtil; import java.io.File; +import java.io.FileFilter; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; @@ -58,10 +59,12 @@ public class LocalFileListAdapter extends BaseAdapter implements FilterableListA private Context mContext; private File[] mFiles = null; - private Vector mFilesAll = new Vector(); + private Vector mFilesAll = new Vector<>(); + private boolean mLocalFolderPicker; - public LocalFileListAdapter(File directory, Context context) { + public LocalFileListAdapter(boolean localFolderPickerMode, File directory, Context context) { mContext = context; + mLocalFolderPicker = localFolderPickerMode; // Read sorting order, default to sort by name ascending FileStorageUtils.mSortOrder = PreferenceManager.getSortOrder(context); @@ -272,7 +275,11 @@ public boolean isEmpty() { * @param directory New file to adapt. Can be NULL, meaning "no content to adapt". */ public void swapDirectory(final File directory) { - mFiles = (directory != null ? directory.listFiles() : null); + if(mLocalFolderPicker) { + mFiles = (directory != null ? getFolders(directory) : null); + } else { + mFiles = (directory != null ? directory.listFiles() : null); + } if (mFiles != null) { Arrays.sort(mFiles, new Comparator() { @Override @@ -288,7 +295,6 @@ public int compare(File lhs, File rhs) { private int compareNames(File lhs, File rhs) { return lhs.getName().toLowerCase().compareTo(rhs.getName().toLowerCase()); } - }); mFiles = FileStorageUtils.sortLocalFolder(mFiles); @@ -317,6 +323,15 @@ public void setSortOrder(Integer order, boolean ascending) { notifyDataSetChanged(); } + private File[] getFolders(final File directory) { + return directory.listFiles(new FileFilter() { + @Override + public boolean accept(File file) { + return file.isDirectory(); + } + }); + } + public void filter(String text){ if(text.isEmpty()){ mFiles = mFilesAll.toArray(new File[1]); diff --git a/src/main/java/com/owncloud/android/ui/dialog/SyncedFolderPreferencesDialogFragment.java b/src/main/java/com/owncloud/android/ui/dialog/SyncedFolderPreferencesDialogFragment.java index 37f630468f06..669ac5b02295 100644 --- a/src/main/java/com/owncloud/android/ui/dialog/SyncedFolderPreferencesDialogFragment.java +++ b/src/main/java/com/owncloud/android/ui/dialog/SyncedFolderPreferencesDialogFragment.java @@ -45,6 +45,7 @@ import com.owncloud.android.datamodel.SyncedFolderDisplayItem; import com.owncloud.android.lib.common.utils.Log_OC; import com.owncloud.android.ui.activity.FolderPickerActivity; +import com.owncloud.android.ui.activity.UploadFilesActivity; import com.owncloud.android.ui.dialog.parcel.SyncedFolderParcelable; import com.owncloud.android.utils.DisplayUtils; import com.owncloud.android.utils.ThemeUtils; @@ -218,6 +219,18 @@ public void setRemoteFolderSummary(String path) { mRemoteFolderSummary.setText(path); } + /** + * set (new) local path on activity result of the folder picker activity. The result gets originally propagated + * to the underlying activity since the picker is an activity and the result can't get passed to the dialog + * fragment directly. + * + * @param path the remote path to be set + */ + public void setLocalFolderSummary(String path) { + mSyncedFolder.setLocalPath(path); + mLocalFolderSummary.setText(path); + } + /** * setup all listeners. * @@ -258,8 +271,6 @@ public void onClick(View v) { @Override public void onClick(View v) { Intent action = new Intent(getActivity(), FolderPickerActivity.class); - action.putExtra( - FolderPickerActivity.EXTRA_ACTION, getResources().getText(R.string.choose_remote_folder)); getActivity().startActivityForResult(action, REQUEST_CODE__SELECT_REMOTE_FOLDER); } }); @@ -267,9 +278,8 @@ public void onClick(View v) { view.findViewById(R.id.local_folder_container).setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { - Intent action = new Intent(getActivity(), FolderPickerActivity.class); - action.putExtra( - FolderPickerActivity.EXTRA_ACTION, getResources().getText(R.string.choose_remote_folder)); + Intent action = new Intent(getActivity(), UploadFilesActivity.class); + action.putExtra(UploadFilesActivity.KEY_LOCAL_FOLDER_PICKER_MODE, true); getActivity().startActivityForResult(action, REQUEST_CODE__SELECT_LOCAL_FOLDER); } }); diff --git a/src/main/java/com/owncloud/android/ui/fragment/LocalFileListFragment.java b/src/main/java/com/owncloud/android/ui/fragment/LocalFileListFragment.java index 675cb6bbe555..d9f04d218f4a 100644 --- a/src/main/java/com/owncloud/android/ui/fragment/LocalFileListFragment.java +++ b/src/main/java/com/owncloud/android/ui/fragment/LocalFileListFragment.java @@ -49,6 +49,11 @@ */ public class LocalFileListFragment extends ExtendedListFragment { private static final String TAG = LocalFileListFragment.class.getSimpleName(); + + private static final String MY_PACKAGE = OCFileListFragment.class.getPackage() != null ? + OCFileListFragment.class.getPackage().getName() : "com.owncloud.android.ui.fragment"; + + public final static String ARG_LOCAL_FOLDER_PICKER_MODE = MY_PACKAGE + ".LOCAL_FOLDER_PICKER_MODE"; /** Reference to the Activity which this fragment is attached to. For callbacks */ private LocalFileListFragment.ContainerActivity mContainerActivity; @@ -59,6 +64,8 @@ public class LocalFileListFragment extends ExtendedListFragment { /** Adapter to connect the data from the directory with the View object */ private LocalFileListAdapter mAdapter = null; + private boolean mLocalFolderPicker; + private static final String SCREEN_NAME = "Local file browser"; @Override @@ -97,7 +104,9 @@ public void onAttach(Activity activity) { public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { Log_OC.i(TAG, "onCreateView() start"); View v = super.onCreateView(inflater, container, savedInstanceState); - setChoiceMode(ListView.CHOICE_MODE_MULTIPLE); + if(!mLocalFolderPicker) { + setChoiceMode(ListView.CHOICE_MODE_MULTIPLE); + } setSwipeEnabled(false); // Disable pull-to-refresh setFabEnabled(false); // Disable FAB setMessageForEmptyList(R.string.file_list_empty_headline, R.string.local_file_list_empty, @@ -115,7 +124,10 @@ public void onActivityCreated(Bundle savedInstanceState) { Log_OC.i(TAG, "onActivityCreated() start"); super.onActivityCreated(savedInstanceState); - mAdapter = new LocalFileListAdapter(mContainerActivity.getInitialDirectory(), getActivity()); + + Bundle args = getArguments(); + mLocalFolderPicker = (args != null) && args.getBoolean(ARG_LOCAL_FOLDER_PICKER_MODE, false); + mAdapter = new LocalFileListAdapter(mLocalFolderPicker, mContainerActivity.getInitialDirectory(), getActivity()); setListAdapter(mAdapter); Log_OC.i(TAG, "onActivityCreated() stop"); diff --git a/src/main/res/layout/upload_files_layout.xml b/src/main/res/layout/upload_files_layout.xml index b0b32982cf36..fb74bc9b7ddb 100644 --- a/src/main/res/layout/upload_files_layout.xml +++ b/src/main/res/layout/upload_files_layout.xml @@ -45,11 +45,12 @@ @@ -73,9 +74,7 @@ android:layout_height="wrap_content" android:gravity="center" android:orientation="horizontal" - android:paddingLeft="@dimen/standard_padding" - android:paddingRight="@dimen/standard_padding" - android:paddingBottom="@dimen/standard_padding"> + android:padding="@dimen/standard_padding"> Files Connect Upload + Choose Choose upload folder No account found There are no %1$s accounts on your device. Please set up an account first. From e286507357e5796b10f5081c964466a7603b8070 Mon Sep 17 00:00:00 2001 From: AndyScherzinger Date: Sun, 28 May 2017 00:45:14 +0200 Subject: [PATCH 049/206] added todo where the list refresh should take place --- .../com/owncloud/android/ui/activity/FolderSyncActivity.java | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/main/java/com/owncloud/android/ui/activity/FolderSyncActivity.java b/src/main/java/com/owncloud/android/ui/activity/FolderSyncActivity.java index d010942fe956..5e210db8fbd6 100644 --- a/src/main/java/com/owncloud/android/ui/activity/FolderSyncActivity.java +++ b/src/main/java/com/owncloud/android/ui/activity/FolderSyncActivity.java @@ -481,6 +481,11 @@ public void onSaveSyncedFolderPreference(SyncedFolderParcelable syncedFolder) { item.setId(storedId); } + // custom folders newly created aren't in the list already, + // so triggering a refresh + if (MediaFolder.CUSTOM.equals(item.getType())) { + // TODO add item to the list and enforce a refresh + } } else { // existing synced folder setup to be updated mSyncedFolderProvider.updateSyncFolder(item); From 2bee3579c32f0316a308c0b07f512df334781018 Mon Sep 17 00:00:00 2001 From: AndyScherzinger Date: Sun, 28 May 2017 01:31:14 +0200 Subject: [PATCH 050/206] correctly persist newly created/configured custom folder and refresh list --- .../ui/activity/FolderSyncActivity.java | 52 +++++++++++-------- .../android/ui/adapter/FolderSyncAdapter.java | 5 ++ 2 files changed, 36 insertions(+), 21 deletions(-) diff --git a/src/main/java/com/owncloud/android/ui/activity/FolderSyncActivity.java b/src/main/java/com/owncloud/android/ui/activity/FolderSyncActivity.java index 5e210db8fbd6..e64668650e96 100644 --- a/src/main/java/com/owncloud/android/ui/activity/FolderSyncActivity.java +++ b/src/main/java/com/owncloud/android/ui/activity/FolderSyncActivity.java @@ -468,33 +468,43 @@ protected void onActivityResult(int requestCode, int resultCode, Intent data) { @Override public void onSaveSyncedFolderPreference(SyncedFolderParcelable syncedFolder) { - SyncedFolderDisplayItem item = syncFolderItems.get(syncedFolder.getSection()); - boolean dirty = item.isEnabled() != syncedFolder.getEnabled(); - item = updateSyncedFolderItem(item, syncedFolder.getLocalPath(), syncedFolder.getRemotePath(), syncedFolder - .getWifiOnly(), syncedFolder.getChargingOnly(), syncedFolder.getSubfolderByDate(), syncedFolder - .getUploadAction(), syncedFolder.getEnabled()); - - if (syncedFolder.getId() == UNPERSISTED_ID) { - // newly set up folder sync config - long storedId = mSyncedFolderProvider.storeFolderSync(item); + // custom folders newly created aren't in the list already, + // so triggering a refresh + if (MediaFolder.CUSTOM.equals(syncedFolder.getType()) && syncedFolder.getId() == UNPERSISTED_ID) { + SyncedFolderDisplayItem newCustomFolder = new SyncedFolderDisplayItem( + SyncedFolder.UNPERSISTED_ID, syncedFolder.getLocalPath(), syncedFolder.getRemotePath(), + syncedFolder.getWifiOnly(), syncedFolder.getChargingOnly(), syncedFolder.getSubfolderByDate(), + syncedFolder.getAccount(), syncedFolder.getUploadAction(), syncedFolder.getEnabled(), + new File(syncedFolder.getLocalPath()).getName(), syncedFolder.getType()); + long storedId = mSyncedFolderProvider.storeFolderSync(newCustomFolder); if (storedId != -1) { - item.setId(storedId); + newCustomFolder.setId(storedId); + } + mAdapter.addSyncFolderItem(newCustomFolder); + } else { + SyncedFolderDisplayItem item = syncFolderItems.get(syncedFolder.getSection()); + boolean dirty = item.isEnabled() != syncedFolder.getEnabled(); + item = updateSyncedFolderItem(item, syncedFolder.getLocalPath(), syncedFolder.getRemotePath(), syncedFolder + .getWifiOnly(), syncedFolder.getChargingOnly(), syncedFolder.getSubfolderByDate(), syncedFolder + .getUploadAction(), syncedFolder.getEnabled()); + + if (syncedFolder.getId() == UNPERSISTED_ID) { + // newly set up folder sync config + long storedId = mSyncedFolderProvider.storeFolderSync(item); + if (storedId != -1) { + item.setId(storedId); + } + } else { + // existing synced folder setup to be updated + mSyncedFolderProvider.updateSyncFolder(item); } - // custom folders newly created aren't in the list already, - // so triggering a refresh - if (MediaFolder.CUSTOM.equals(item.getType())) { - // TODO add item to the list and enforce a refresh + if(dirty) { + mAdapter.setSyncFolderItem(syncedFolder.getSection(), item); } - } else { - // existing synced folder setup to be updated - mSyncedFolderProvider.updateSyncFolder(item); } - mSyncedFolderPreferencesDialogFragment = null; - if (dirty) { - mAdapter.setSyncFolderItem(syncedFolder.getSection(), item); - } + mSyncedFolderPreferencesDialogFragment = null; } @Override diff --git a/src/main/java/com/owncloud/android/ui/adapter/FolderSyncAdapter.java b/src/main/java/com/owncloud/android/ui/adapter/FolderSyncAdapter.java index c2d793c18535..64a3e26d733b 100644 --- a/src/main/java/com/owncloud/android/ui/adapter/FolderSyncAdapter.java +++ b/src/main/java/com/owncloud/android/ui/adapter/FolderSyncAdapter.java @@ -81,6 +81,11 @@ public void setSyncFolderItem(int location, SyncedFolderDisplayItem syncFolderIt notifyDataSetChanged(); } + public void addSyncFolderItem(SyncedFolderDisplayItem syncFolderItem) { + mSyncFolderItems.add(syncFolderItem); + notifyDataSetChanged(); + } + @Override public int getSectionCount() { return mSyncFolderItems.size(); From 8ec098856a1ba758e18734750d31844dec64ef34 Mon Sep 17 00:00:00 2001 From: AndyScherzinger Date: Mon, 29 May 2017 22:48:54 +0200 Subject: [PATCH 051/206] fix db upgrade logic after rebase --- .../providers/FileContentProvider.java | 52 ++++++------------- 1 file changed, 17 insertions(+), 35 deletions(-) diff --git a/src/main/java/com/owncloud/android/providers/FileContentProvider.java b/src/main/java/com/owncloud/android/providers/FileContentProvider.java index c071593fbff5..d397fd48329f 100644 --- a/src/main/java/com/owncloud/android/providers/FileContentProvider.java +++ b/src/main/java/com/owncloud/android/providers/FileContentProvider.java @@ -377,10 +377,8 @@ private Uri insert(SQLiteDatabase db, Uri uri, ContentValues values) { ContentUris.withAppendedId(ProviderTableMeta.CONTENT_URI_FILESYSTEM, filesystedId); } else { throw new SQLException("ERROR " + uri); - } return insertedFilesystemUri; - default: throw new IllegalArgumentException("Unknown uri id: " + uri); } @@ -1026,7 +1024,23 @@ public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { } if (oldVersion < 21 && newVersion >= 21) { - Log_OC.i(SQL, "Entering in the #21 ADD in onUpgrade"); + Log_OC.i(SQL, "Entering in the #21 Adding virtual table"); + db.beginTransaction(); + try { + createVirtualTable(db); + upgraded = true; + db.setTransactionSuccessful(); + } finally { + db.endTransaction(); + } + } + + if (!upgraded) { + Log_OC.i(SQL, String.format(Locale.ENGLISH, UPGRADE_VERSION_MSG, oldVersion, newVersion)); + } + + if (oldVersion < 22 && newVersion >= 22) { + Log_OC.i(SQL, "Entering in the #22 adding type colum for synced folders, Create filesystem table"); db.beginTransaction(); try { // add type column default being CUSTOM (0) @@ -1054,38 +1068,6 @@ public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { } } - if (oldVersion < 20 && newVersion >= 20) { - Log_OC.i(SQL, "Entering in the #20 Adding arbitrary data table"); - db.beginTransaction(); - try { - createArbitraryData(db); - upgraded = true; - db.setTransactionSuccessful(); - } finally { - db.endTransaction(); - } - } - - if (!upgraded) { - Log_OC.i(SQL, String.format(Locale.ENGLISH, UPGRADE_VERSION_MSG, oldVersion, newVersion)); - } - - if (oldVersion < 21 && newVersion >= 21) { - Log_OC.i(SQL, "Entering in the #21 Adding virtual table"); - db.beginTransaction(); - try { - createVirtualTable(db); - upgraded = true; - db.setTransactionSuccessful(); - } finally { - db.endTransaction(); - } - } - - if (!upgraded) { - Log_OC.i(SQL, String.format(Locale.ENGLISH, UPGRADE_VERSION_MSG, oldVersion, newVersion)); - } - if (oldVersion < 22 && newVersion >= 22) { Log_OC.i(SQL, "Entering in the #22 Adding user theming to capabilities table"); db.beginTransaction(); From fcfb67c85e04ab829b244ac7a798bbb0fa5c33ed Mon Sep 17 00:00:00 2001 From: AndyScherzinger Date: Tue, 30 May 2017 17:32:31 +0200 Subject: [PATCH 052/206] delete button (no function yet) --- .../layout/folder_sync_settings_layout.xml | 36 +++++++++++++------ src/main/res/values/colors.xml | 1 + src/main/res/values/styles.xml | 4 +++ 3 files changed, 30 insertions(+), 11 deletions(-) diff --git a/src/main/res/layout/folder_sync_settings_layout.xml b/src/main/res/layout/folder_sync_settings_layout.xml index 6411526f839a..205afd656ec7 100644 --- a/src/main/res/layout/folder_sync_settings_layout.xml +++ b/src/main/res/layout/folder_sync_settings_layout.xml @@ -392,25 +392,39 @@ - + android:layout_height="wrap_content"> + android:layout_alignParentLeft="true" + android:text="delete"/> - + android:layout_alignParentRight="true"> - + + + + + + + \ No newline at end of file diff --git a/src/main/res/values/colors.xml b/src/main/res/values/colors.xml index 14f140ef4d89..188c41f4b454 100644 --- a/src/main/res/values/colors.xml +++ b/src/main/res/values/colors.xml @@ -32,6 +32,7 @@ #EEEEEE #00000000 #a0a0a0 + #e53935 #757575 diff --git a/src/main/res/values/styles.xml b/src/main/res/values/styles.xml index 6f9d60d1aa96..0ea0adbf2230 100644 --- a/src/main/res/values/styles.xml +++ b/src/main/res/values/styles.xml @@ -119,6 +119,10 @@ @color/color_accent + + From c26249eeea9db2b85b0806fc659107cace02d622 Mon Sep 17 00:00:00 2001 From: AndyScherzinger Date: Tue, 30 May 2017 22:54:19 +0200 Subject: [PATCH 053/206] fix after rebase --- src/main/res/layout/folder_sync_layout.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/res/layout/folder_sync_layout.xml b/src/main/res/layout/folder_sync_layout.xml index 3f1316fd1bb2..77a565d1ce00 100644 --- a/src/main/res/layout/folder_sync_layout.xml +++ b/src/main/res/layout/folder_sync_layout.xml @@ -88,7 +88,7 @@ android:scrollbarStyle="outsideOverlay" android:scrollbars="vertical" android:visibility="visible"/> - + Date: Tue, 30 May 2017 22:54:40 +0200 Subject: [PATCH 054/206] implement delete action --- .../datamodel/SyncedFolderProvider.java | 11 +++++ .../ui/activity/FolderSyncActivity.java | 9 ++++ .../android/ui/activity/Preferences.java | 2 +- .../android/ui/adapter/FolderSyncAdapter.java | 45 ++++++++++--------- ...SyncedFolderPreferencesDialogFragment.java | 11 +++++ 5 files changed, 57 insertions(+), 21 deletions(-) diff --git a/src/main/java/com/owncloud/android/datamodel/SyncedFolderProvider.java b/src/main/java/com/owncloud/android/datamodel/SyncedFolderProvider.java index 53b871d5bc9b..de9aaab2ca4a 100644 --- a/src/main/java/com/owncloud/android/datamodel/SyncedFolderProvider.java +++ b/src/main/java/com/owncloud/android/datamodel/SyncedFolderProvider.java @@ -279,6 +279,17 @@ public int deleteSyncedFoldersInList(ArrayList ids) { ); } + /** + * delete record of synchronized folder with the given id. + */ + public int deleteSyncedFolder(long id) { + return mContentResolver.delete( + ProviderMeta.ProviderTableMeta.CONTENT_URI_SYNCED_FOLDERS, + ProviderMeta.ProviderTableMeta._ID + " = ?", + new String[]{String.valueOf(id)} + ); + } + /** * update given synced folder. * diff --git a/src/main/java/com/owncloud/android/ui/activity/FolderSyncActivity.java b/src/main/java/com/owncloud/android/ui/activity/FolderSyncActivity.java index e64668650e96..f25843180396 100644 --- a/src/main/java/com/owncloud/android/ui/activity/FolderSyncActivity.java +++ b/src/main/java/com/owncloud/android/ui/activity/FolderSyncActivity.java @@ -512,6 +512,15 @@ public void onCancelSyncedFolderPreference() { mSyncedFolderPreferencesDialogFragment = null; } + @Override + public void onDeleteSyncedFolderPreference(SyncedFolderParcelable syncedFolder) { + // TODO refresh recycler view + + mSyncedFolderProvider.deleteSyncedFolder(syncedFolder.getId()); + + mAdapter.removeItem(syncedFolder.getSection()); + } + /** * update given synced folder with the given values. * diff --git a/src/main/java/com/owncloud/android/ui/activity/Preferences.java b/src/main/java/com/owncloud/android/ui/activity/Preferences.java index e5c33bd9acfd..7be24010f234 100644 --- a/src/main/java/com/owncloud/android/ui/activity/Preferences.java +++ b/src/main/java/com/owncloud/android/ui/activity/Preferences.java @@ -209,7 +209,7 @@ public void onCreate(Bundle savedInstanceState) { pUploadOnWifiCheckbox.setOnPreferenceClickListener(new OnPreferenceClickListener() { @Override public boolean onPreferenceClick(Preference preference) { - arbitraryDataProvider.storeOrUpdateKeyValue(account, SYNCED_FOLDER_LIGHT_UPLOAD_ON_WIFI, + arbitraryDataProvider.storeOrUpdateKeyValue(account.name, SYNCED_FOLDER_LIGHT_UPLOAD_ON_WIFI, String.valueOf(pUploadOnWifiCheckbox.isChecked())); return true; diff --git a/src/main/java/com/owncloud/android/ui/adapter/FolderSyncAdapter.java b/src/main/java/com/owncloud/android/ui/adapter/FolderSyncAdapter.java index 64a3e26d733b..dbb6054635fa 100644 --- a/src/main/java/com/owncloud/android/ui/adapter/FolderSyncAdapter.java +++ b/src/main/java/com/owncloud/android/ui/adapter/FolderSyncAdapter.java @@ -86,6 +86,11 @@ public void addSyncFolderItem(SyncedFolderDisplayItem syncFolderItem) { notifyDataSetChanged(); } + public void removeItem(int section) { + mSyncFolderItems.remove(section); + notifyDataSetChanged(); + } + @Override public int getSectionCount() { return mSyncFolderItems.size(); @@ -132,30 +137,30 @@ public void onClick(View v) { }); setSyncButtonActiveIcon(holder.syncStatusButton, mSyncFolderItems.get(section).isEnabled()); - holder.syncStatusButton.setVisibility(View.VISIBLE); - holder.syncStatusButton.setTag(section); - holder.syncStatusButton.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - mSyncFolderItems.get(section).setEnabled(!mSyncFolderItems.get(section).isEnabled()); - setSyncButtonActiveIcon(holder.syncStatusButton, mSyncFolderItems.get(section).isEnabled()); - mListener.onSyncStatusToggleClick(section, mSyncFolderItems.get(section)); - } - }); - setSyncButtonActiveIcon(holder.syncStatusButton, mSyncFolderItems.get(section).isEnabled()); - - if (mLight) { - holder.menuButton.setVisibility(View.GONE); - } else { - holder.menuButton.setVisibility(View.VISIBLE); - holder.menuButton.setTag(section); - holder.menuButton.setOnClickListener(new View.OnClickListener() { + holder.syncStatusButton.setVisibility(View.VISIBLE); + holder.syncStatusButton.setTag(section); + holder.syncStatusButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { - mListener.onSyncFolderSettingsClick(section, mSyncFolderItems.get(section)); + mSyncFolderItems.get(section).setEnabled(!mSyncFolderItems.get(section).isEnabled()); + setSyncButtonActiveIcon(holder.syncStatusButton, mSyncFolderItems.get(section).isEnabled()); + mListener.onSyncStatusToggleClick(section, mSyncFolderItems.get(section)); } }); - } + setSyncButtonActiveIcon(holder.syncStatusButton, mSyncFolderItems.get(section).isEnabled()); + + if (mLight) { + holder.menuButton.setVisibility(View.GONE); + } else { + holder.menuButton.setVisibility(View.VISIBLE); + holder.menuButton.setTag(section); + holder.menuButton.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + mListener.onSyncFolderSettingsClick(section, mSyncFolderItems.get(section)); + } + }); + } } else { holder.mainHeaderContainer.setVisibility(View.GONE); holder.customFolderHeaderContainer.setVisibility(View.VISIBLE); diff --git a/src/main/java/com/owncloud/android/ui/dialog/SyncedFolderPreferencesDialogFragment.java b/src/main/java/com/owncloud/android/ui/dialog/SyncedFolderPreferencesDialogFragment.java index 669ac5b02295..afd3cb096ba7 100644 --- a/src/main/java/com/owncloud/android/ui/dialog/SyncedFolderPreferencesDialogFragment.java +++ b/src/main/java/com/owncloud/android/ui/dialog/SyncedFolderPreferencesDialogFragment.java @@ -239,6 +239,7 @@ public void setLocalFolderSummary(String path) { private void setupListeners(View view) { mSave.setOnClickListener(new OnSyncedFolderSaveClickListener()); mCancel.setOnClickListener(new OnSyncedFolderCancelClickListener()); + view.findViewById(R.id.delete).setOnClickListener(new OnSyncedFolderDeleteClickListener()); view.findViewById(R.id.setting_instant_upload_on_wifi_container).setOnClickListener( new OnClickListener() { @@ -368,10 +369,20 @@ public void onClick(View v) { } } + private class OnSyncedFolderDeleteClickListener implements OnClickListener { + @Override + public void onClick(View v) { + dismiss(); + ((OnSyncedFolderPreferenceListener) getActivity()).onDeleteSyncedFolderPreference(mSyncedFolder); + } + } + public interface OnSyncedFolderPreferenceListener { void onSaveSyncedFolderPreference(SyncedFolderParcelable syncedFolder); void onCancelSyncedFolderPreference(); + + void onDeleteSyncedFolderPreference(SyncedFolderParcelable syncedFolder); } @Override From 628267297d0aa02e17a7ebf013012e3f952337be Mon Sep 17 00:00:00 2001 From: AndyScherzinger Date: Tue, 30 May 2017 23:15:34 +0200 Subject: [PATCH 055/206] set sub headline after local path has been chosen --- .../ui/dialog/SyncedFolderPreferencesDialogFragment.java | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/main/java/com/owncloud/android/ui/dialog/SyncedFolderPreferencesDialogFragment.java b/src/main/java/com/owncloud/android/ui/dialog/SyncedFolderPreferencesDialogFragment.java index afd3cb096ba7..9c6ba35ba871 100644 --- a/src/main/java/com/owncloud/android/ui/dialog/SyncedFolderPreferencesDialogFragment.java +++ b/src/main/java/com/owncloud/android/ui/dialog/SyncedFolderPreferencesDialogFragment.java @@ -50,6 +50,8 @@ import com.owncloud.android.utils.DisplayUtils; import com.owncloud.android.utils.ThemeUtils; +import java.io.File; + /** * Dialog to show the preferences/configuration of a synced folder allowing the user to change the different parameters. */ @@ -229,6 +231,13 @@ public void setRemoteFolderSummary(String path) { public void setLocalFolderSummary(String path) { mSyncedFolder.setLocalPath(path); mLocalFolderSummary.setText(path); + mLocalFolderPath.setText( + DisplayUtils.createTextWithSpan( + String.format( + getString(R.string.folder_sync_preferences_folder_path), + mSyncedFolder.getLocalPath()), + new File(mSyncedFolder.getLocalPath()).getName(), + new StyleSpan(Typeface.BOLD))); } /** From c0c603d47d5e562a22cf78be4893e132a7b54795 Mon Sep 17 00:00:00 2001 From: AndyScherzinger Date: Tue, 30 May 2017 23:19:08 +0200 Subject: [PATCH 056/206] hide delete button for non-custom folders --- .../android/ui/dialog/SyncedFolderPreferencesDialogFragment.java | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main/java/com/owncloud/android/ui/dialog/SyncedFolderPreferencesDialogFragment.java b/src/main/java/com/owncloud/android/ui/dialog/SyncedFolderPreferencesDialogFragment.java index 9c6ba35ba871..4464fee8c2aa 100644 --- a/src/main/java/com/owncloud/android/ui/dialog/SyncedFolderPreferencesDialogFragment.java +++ b/src/main/java/com/owncloud/android/ui/dialog/SyncedFolderPreferencesDialogFragment.java @@ -140,6 +140,7 @@ private void setupDialogElements(View view) { if (mSyncedFolder.getType() > MediaFolder.CUSTOM) { view.findViewById(R.id.local_folder_container).setVisibility(View.GONE); + view.findViewById(R.id.delete).setVisibility(View.GONE); } // find/saves UI elements From 36cee051d5cf19bc5d257da4340e588ae660488f Mon Sep 17 00:00:00 2001 From: AndyScherzinger Date: Tue, 30 May 2017 23:23:44 +0200 Subject: [PATCH 057/206] hide delete button for unpersisted custom folders --- .../ui/dialog/SyncedFolderPreferencesDialogFragment.java | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/main/java/com/owncloud/android/ui/dialog/SyncedFolderPreferencesDialogFragment.java b/src/main/java/com/owncloud/android/ui/dialog/SyncedFolderPreferencesDialogFragment.java index 4464fee8c2aa..409a6f914167 100644 --- a/src/main/java/com/owncloud/android/ui/dialog/SyncedFolderPreferencesDialogFragment.java +++ b/src/main/java/com/owncloud/android/ui/dialog/SyncedFolderPreferencesDialogFragment.java @@ -52,6 +52,8 @@ import java.io.File; +import static com.owncloud.android.datamodel.SyncedFolderDisplayItem.UNPERSISTED_ID; + /** * Dialog to show the preferences/configuration of a synced folder allowing the user to change the different parameters. */ @@ -139,8 +141,12 @@ private void setupDialogElements(View view) { int accentColor = ThemeUtils.primaryAccentColor(); if (mSyncedFolder.getType() > MediaFolder.CUSTOM) { + // hide local folder chooser and delete for non-custom folders view.findViewById(R.id.local_folder_container).setVisibility(View.GONE); view.findViewById(R.id.delete).setVisibility(View.GONE); + } else if (mSyncedFolder.getId() <= UNPERSISTED_ID) { + // Hide delete for unpersisted custom folders + view.findViewById(R.id.delete).setVisibility(View.GONE); } // find/saves UI elements From 6ab0a15799f1cc2e4857ca982ace3bab51c2e648 Mon Sep 17 00:00:00 2001 From: Mario Danic Date: Wed, 7 Jun 2017 09:42:41 +0200 Subject: [PATCH 058/206] Fix the build Signed-off-by: Mario Danic --- build.gradle | 28 ++++++++++-------------- gradle/wrapper/gradle-wrapper.properties | 2 +- src/main/res/values/styles.xml | 1 + 3 files changed, 13 insertions(+), 18 deletions(-) diff --git a/build.gradle b/build.gradle index 94cfa1dedc65..36b1a279ecd3 100644 --- a/build.gradle +++ b/build.gradle @@ -13,7 +13,7 @@ buildscript { } } dependencies { - classpath 'com.android.tools.build:gradle:2.4.0-alpha7' + classpath 'com.android.tools.build:gradle:2.4+' classpath 'com.google.gms:google-services:3.0.0' } } @@ -29,7 +29,7 @@ configurations.all { } ext { - supportLibraryVersion = '25.0.0' + supportLibraryVersion = '25.2.0' googleLibraryVersion = '10.2.4' travisBuild = System.getenv("TRAVIS") == "true" @@ -78,14 +78,18 @@ android { // adapt structure from Eclipse to Gradle/Android Studio expectations; // see http://tools.android.com/tech-docs/new-build-system/user-guide#TOC-Configuring-the-Structure + flavorDimensions "default" + productFlavors { // used for f-droid generic { applicationId 'com.nextcloud.client' + dimension "default" } gplay { applicationId 'com.nextcloud.client' + dimension "default" } modified { @@ -94,6 +98,7 @@ android { // domain name // .client applicationId 'com.custom.client' + dimension "default" } } @@ -177,7 +182,6 @@ dependencies { /// dependencies for app building compile name: 'touch-image-view' compile 'com.android.support:multidex:1.0.1' - compile 'com.github.nextcloud:android-library:1.0.22' compile "com.android.support:support-v4:${supportLibraryVersion}" compile "com.android.support:design:${supportLibraryVersion}" @@ -186,46 +190,36 @@ dependencies { compile "com.android.support:cardview-v7:${supportLibraryVersion}" compile 'com.github.tobiasKaminsky:android-floating-action-button:1.10.2' compile 'com.google.code.findbugs:annotations:2.0.1' - compile group: 'commons-io', name: 'commons-io', version: '2.4' - compile 'com.github.evernote:android-job:v1.1.10' - compile 'com.jakewharton:butterknife:8.4.0' - annotationProcessor 'com.jakewharton:butterknife-compiler:8.4.0' + compile 'commons-io:commons-io:2.5' + compile 'com.github.evernote:android-job:v1.1.11' + compile 'com.jakewharton:butterknife:8.5.1' + annotationProcessor 'com.jakewharton:butterknife-compiler:8.5.1' compile 'org.greenrobot:eventbus:3.0.0' compile 'com.googlecode.ez-vcard:ez-vcard:0.10.2' compile 'org.lukhnos:nnio:0.2' - // uncomment for gplay, modified // compile "com.google.firebase:firebase-messaging:${googleLibraryVersion}" // compile "com.google.android.gms:play-services-base:${googleLibraryVersion}" - compile 'org.parceler:parceler-api:1.1.6' annotationProcessor 'org.parceler:parceler:1.1.6' - compile 'com.github.bumptech.glide:glide:3.7.0' compile 'com.caverock:androidsvg:1.2.1' - /// dependencies for local unit tests testCompile 'junit:junit:4.12' testCompile 'org.mockito:mockito-core:1.10.19' - /// dependencies for instrumented tests // JUnit4 Rules androidTestCompile 'com.android.support.test:rules:0.5' - // Android JUnit Runner androidTestCompile 'com.android.support.test:runner:0.5' - // Android Annotation Support androidTestCompile "com.android.support:support-annotations:${supportLibraryVersion}" - // Espresso core androidTestCompile 'com.android.support.test.espresso:espresso-core:2.2.2' - // UIAutomator - for cross-app UI tests, and to grant screen is turned on in Espresso tests //androidTestCompile 'com.android.support.test.uiautomator:uiautomator-v18:2.1.2' // fix conflict in dependencies; see http://g.co/androidstudio/app-test-app-conflict for details //androidTestCompile "com.android.support:support-annotations:${supportLibraryVersion}" - } configurations.all { diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 6c2b37cddb23..0fcb11ae880b 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-3.5-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-4.0-milestone-1-all.zip diff --git a/src/main/res/values/styles.xml b/src/main/res/values/styles.xml index 0ea0adbf2230..f911bac0c308 100644 --- a/src/main/res/values/styles.xml +++ b/src/main/res/values/styles.xml @@ -256,4 +256,5 @@ +