diff --git a/.drone.yml b/.drone.yml index 8fbf6f2d22fa..39d40183a4a6 100644 --- a/.drone.yml +++ b/.drone.yml @@ -1,4 +1,5 @@ kind: pipeline +type: docker name: generic steps: @@ -48,6 +49,7 @@ trigger: --- kind: pipeline +type: docker name: gplay steps: @@ -116,6 +118,13 @@ services: - su www-data -c "php /var/www/html/occ app:enable activity" - su www-data -c "git clone -b master https://github.com/nextcloud/text.git /var/www/html/apps/text/" - su www-data -c "php /var/www/html/occ app:enable text" + - su www-data -c "git clone -b master https://github.com/nextcloud/circles.git /var/www/html/apps/circles/" + - apt-get update; apt-get -y install composer + - su www-data -c "cd /var/www/html/apps/circles; composer install" + - su www-data -c "php /var/www/html/occ app:enable -f circles" + - su www-data -c "occ config:app:set circles --value 1 allow_non_ssl_links" + - su www-data -c "occ config:app:set circles --value 1 local_is_non_ssl" + - su www-data -c "php /var/www/html/occ circles:manage:create test public publicCircle - /usr/local/bin/run.sh trigger: @@ -126,6 +135,7 @@ trigger: - pull_request --- kind: pipeline +type: docker name: analysis steps: @@ -175,6 +185,7 @@ trigger: - pull_request --- kind: pipeline +type: docker name: qa steps: diff --git a/drawable_resources/circles.svg b/drawable_resources/circles.svg new file mode 100644 index 000000000000..f908e89543b7 --- /dev/null +++ b/drawable_resources/circles.svg @@ -0,0 +1,27 @@ + + + + + + image/svg+xml + + + + + + + + + + + diff --git a/screenshots/com.owncloud.android.ui.fragment.OCFileListFragmentIT_createAndShowShareToCircle.png b/screenshots/com.owncloud.android.ui.fragment.OCFileListFragmentIT_createAndShowShareToCircle.png new file mode 100644 index 000000000000..d846734141bb Binary files /dev/null and b/screenshots/com.owncloud.android.ui.fragment.OCFileListFragmentIT_createAndShowShareToCircle.png differ diff --git a/screenshots/com.owncloud.android.ui.fragment.OCFileListFragmentIT_createAndShowShareToGroup.png b/screenshots/com.owncloud.android.ui.fragment.OCFileListFragmentIT_createAndShowShareToGroup.png new file mode 100644 index 000000000000..efa4f3f13146 Binary files /dev/null and b/screenshots/com.owncloud.android.ui.fragment.OCFileListFragmentIT_createAndShowShareToGroup.png differ diff --git a/screenshots/com.owncloud.android.ui.fragment.OCFileListFragmentIT_createAndShowShareToUser.png b/screenshots/com.owncloud.android.ui.fragment.OCFileListFragmentIT_createAndShowShareToUser.png new file mode 100644 index 000000000000..8e720454beb6 Binary files /dev/null and b/screenshots/com.owncloud.android.ui.fragment.OCFileListFragmentIT_createAndShowShareToUser.png differ diff --git a/screenshots/com.owncloud.android.ui.fragment.OCFileListFragmentIT_createAndShowShareViaLink.png b/screenshots/com.owncloud.android.ui.fragment.OCFileListFragmentIT_createAndShowShareViaLink.png new file mode 100644 index 000000000000..40d5d4185313 Binary files /dev/null and b/screenshots/com.owncloud.android.ui.fragment.OCFileListFragmentIT_createAndShowShareViaLink.png differ diff --git a/scripts/analysis/lint-results.txt b/scripts/analysis/lint-results.txt index 385dc9a0803c..7fa79531c206 100644 --- a/scripts/analysis/lint-results.txt +++ b/scripts/analysis/lint-results.txt @@ -1,2 +1,2 @@ DO NOT TOUCH; GENERATED BY DRONE - Lint Report: 76 warnings + Lint Report: 78 warnings diff --git a/scripts/updateScreenshots.sh b/scripts/updateScreenshots.sh index a7f2ff81a217..ec14f59be21a 100755 --- a/scripts/updateScreenshots.sh +++ b/scripts/updateScreenshots.sh @@ -11,11 +11,15 @@ if ( [[ ! $(emulator -list-avds | grep uiComparison -c) -eq 0 ]] ); then (sleep 5; echo "no") | avdmanager create avd -n uiComparison -c 100M -k "system-images;android-27;google_apis;x86" --abi "google_apis/x86" fi -emulator -avd uiComparison -no-snapshot -gpu swiftshader_indirect -no-window -no-audio -skin 500x833 1>/dev/null & +if [ $1 == "debug" ]; then + emulator -avd uiComparison -no-snapshot -gpu swiftshader_indirect -no-audio -skin 500x833 1>/dev/null & +else + emulator -avd uiComparison -no-snapshot -gpu swiftshader_indirect -no-window -no-audio -skin 500x833 1>/dev/null & +fi PID=$(echo $!) ## server -docker run --name=uiComparison nextcloudci/server:server-17 1>/dev/null & +docker run --name=uiComparison nextcloudci/server --entrypoint '/usr/local/bin/initnc.sh' 1>/dev/null & sleep 5 IP=$(docker inspect -f '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' uiComparison) @@ -24,13 +28,15 @@ if [[ $IP = "" ]]; then exit 1 fi +## wait for server to finish +scripts/wait_for_server.sh $IP + ## run on server cp gradle.properties gradle.properties_ sed -i s"/server/$IP/" gradle.properties scripts/wait_for_emulator.sh # setup test server -docker exec uiComparison /bin/sh -c "/usr/local/bin/initnc.sh" docker exec uiComparison /bin/sh -c "su www-data -c \"OC_PASS=user1 php /var/www/html/occ user:add --password-from-env --display-name='User One' user1\"" docker exec uiComparison /bin/sh -c "su www-data -c \"OC_PASS=user2 php /var/www/html/occ user:add --password-from-env --display-name='User Two' user2\"" docker exec uiComparison /bin/sh -c "su www-data -c \"OC_PASS=user3 php /var/www/html/occ user:add --password-from-env --display-name='User Three' user3\"" @@ -40,6 +46,13 @@ docker exec uiComparison /bin/sh -c "su www-data -c \"php /var/www/html/occ grou docker exec uiComparison /bin/sh -c "su www-data -c \"php /var/www/html/occ group:adduser users user2\"" docker exec uiComparison /bin/sh -c "su www-data -c \"git clone -b master https://github.com/nextcloud/text.git /var/www/html/apps/text/\"" docker exec uiComparison /bin/sh -c "su www-data -c \"php /var/www/html/occ app:enable text\"" +docker exec uiComparison /bin/sh -c "su www-data -c \"git clone -b master https://github.com/nextcloud/circles.git /var/www/html/apps/circles/\"" +docker exec uiComparison /bin/sh -c "apt-get update; apt-get -y install composer" +docker exec uiComparison /bin/sh -c "su www-data -c \"cd /var/www/html/apps/circles; composer install\"" +docker exec uiComparison /bin/sh -c "su www-data -c \"php /var/www/html/occ app:enable -f circles\"" +docker exec uiComparison /bin/sh -c "su www-data -c \"php /var/www/html/occ config:app:set circles --value 1 allow_non_ssl_links\"" +docker exec uiComparison /bin/sh -c "su www-data -c \"php /var/www/html/occ config:app:set circles --value 1 local_is_non_ssl\"" +docker exec uiComparison /bin/sh -c "su www-data -c \"php /var/www/html/occ circles:manage:create test public publicCircle\"" docker exec uiComparison /bin/sh -c "/usr/local/bin/run.sh" ## update/create all screenshots @@ -59,6 +72,10 @@ docker exec uiComparison /bin/sh -c "/usr/local/bin/run.sh" mv gradle.properties_ gradle.properties +if [ $1 == "debug" ]; then + exit +fi + # tidy up kill $PID docker stop uiComparison diff --git a/scripts/wait_for_server.sh b/scripts/wait_for_server.sh index 66b150045c4e..0157dc65cd10 100755 --- a/scripts/wait_for_server.sh +++ b/scripts/wait_for_server.sh @@ -1,10 +1,12 @@ #!/usr/bin/env bash -counter=0 status="" +counter=0 +checkcounter=0 until [[ $status = "false" ]]; do status=$(curl 2>/dev/null "http://$1/status.php" | jq .maintenance) + echo "($checkcounter) $status" if [[ "$status" =~ "false" || "$status" = "" ]]; then let "counter += 1" @@ -14,5 +16,8 @@ until [[ $status = "false" ]]; do fi fi + let "checkcounter += 1" sleep 10 done + +echo "($checkcounter) Done" diff --git a/src/androidTest/java/com/nextcloud/client/FileDisplayActivityIT.java b/src/androidTest/java/com/nextcloud/client/FileDisplayActivityIT.java index 71ed3b670813..10863285f810 100644 --- a/src/androidTest/java/com/nextcloud/client/FileDisplayActivityIT.java +++ b/src/androidTest/java/com/nextcloud/client/FileDisplayActivityIT.java @@ -29,10 +29,12 @@ import com.owncloud.android.AbstractIT; import com.owncloud.android.R; import com.owncloud.android.datamodel.OCFile; +import com.owncloud.android.lib.common.operations.RemoteOperationResult; import com.owncloud.android.lib.resources.files.CreateFolderRemoteOperation; import com.owncloud.android.lib.resources.files.ExistenceCheckRemoteOperation; import com.owncloud.android.lib.resources.files.SearchRemoteOperation; import com.owncloud.android.lib.resources.shares.CreateShareRemoteOperation; +import com.owncloud.android.lib.resources.shares.GetShareesRemoteOperation; import com.owncloud.android.lib.resources.shares.OCShare; import com.owncloud.android.lib.resources.shares.ShareType; import com.owncloud.android.operations.CreateFolderOperation; @@ -40,6 +42,8 @@ import com.owncloud.android.ui.events.SearchEvent; import org.greenrobot.eventbus.EventBus; +import org.json.JSONException; +import org.json.JSONObject; import org.junit.Assert; import org.junit.Rule; import org.junit.Test; @@ -67,10 +71,10 @@ public class FileDisplayActivityIT extends AbstractIT { Manifest.permission.WRITE_EXTERNAL_STORAGE); @Test - public void open() throws InterruptedException { + public void open() { Activity sut = activityRule.launchActivity(null); - Thread.sleep(3000); + shortSleep(); Screenshot.snapActivity(sut).record(); } @@ -85,12 +89,13 @@ public void drawer() { } @Test - public void showShares() { + public void showShares() throws JSONException { assertTrue(new ExistenceCheckRemoteOperation("/shareToAdmin/", true).execute(client).isSuccess()); assertTrue(new CreateFolderRemoteOperation("/shareToAdmin/", true).execute(client).isSuccess()); assertTrue(new CreateFolderRemoteOperation("/shareToGroup/", true).execute(client).isSuccess()); assertTrue(new CreateFolderRemoteOperation("/shareViaLink/", true).execute(client).isSuccess()); assertTrue(new CreateFolderRemoteOperation("/noShare/", true).execute(client).isSuccess()); + assertTrue(new CreateFolderRemoteOperation("/shareToCircle/", true).execute(client).isSuccess()); // share folder to user "admin" assertTrue(new CreateShareRemoteOperation("/shareToAdmin/", @@ -119,12 +124,27 @@ public void showShares() { OCShare.DEFAULT_PERMISSION) .execute(client).isSuccess()); + // share folder to circle + // get share + RemoteOperationResult searchResult = new GetShareesRemoteOperation("publicCircle", 1, 50).execute(client); + assertTrue(searchResult.getLogMessage(), searchResult.isSuccess()); + + JSONObject resultJson = (JSONObject) searchResult.getData().get(0); + String circleId = resultJson.getJSONObject("value").getString("shareWith"); + + assertTrue(new CreateShareRemoteOperation("/shareToCircle/", + ShareType.CIRCLE, + circleId, + false, + "", + OCShare.DEFAULT_PERMISSION) + .execute(client).isSuccess()); + Activity sut = activityRule.launchActivity(null); getInstrumentation().waitForIdleSync(); - EventBus.getDefault().post(new SearchEvent("", - SearchRemoteOperation.SearchType.SHARED_FILTER)); + EventBus.getDefault().post(new SearchEvent("", SearchRemoteOperation.SearchType.SHARED_FILTER)); getInstrumentation().waitForIdleSync(); @@ -142,7 +162,7 @@ public void showAccounts() { } @Test - public void allFiles() throws InterruptedException { + public void allFiles() { // ActivityScenario sut = ActivityScenario.launch(FileDisplayActivity.class); FileDisplayActivity sut = activityRule.launchActivity(null); @@ -150,8 +170,6 @@ public void allFiles() throws InterruptedException { assertTrue(new CreateFolderOperation("/test/", true).execute(client, getStorageManager()).isSuccess()); // navigate into it - // sut.onActivity(activity -> activity.onBrowsedDownTo(getStorageManager().getFileByPath("/test/"))); - //sut.onBrowsedDownTo(getStorageManager().getFileByPath("/test/")); OCFile test = getStorageManager().getFileByPath("/test/"); sut.setFile(test); sut.startSyncFolderOperation(test, false); @@ -166,9 +184,7 @@ public void allFiles() throws InterruptedException { .perform(NavigationViewActions.navigateTo(R.id.nav_all_files)); // then should be in root again - Thread.sleep(2000); + shortSleep(); assertEquals(getStorageManager().getFileByPath("/"), sut.getCurrentDir()); - - } } diff --git a/src/androidTest/java/com/owncloud/android/AbstractIT.java b/src/androidTest/java/com/owncloud/android/AbstractIT.java index 5ddc81a410db..cc2a550691e7 100644 --- a/src/androidTest/java/com/owncloud/android/AbstractIT.java +++ b/src/androidTest/java/com/owncloud/android/AbstractIT.java @@ -97,6 +97,8 @@ public static void beforeAll() { createDummyFiles(); waitForServer(client, baseUrl); + + deleteAllFiles(); // makes sure that no file/folder is in root } catch (OperationCanceledException e) { e.printStackTrace(); } catch (AuthenticatorException e) { @@ -110,6 +112,10 @@ public static void beforeAll() { @After public void after() { + deleteAllFiles(); + } + + public static void deleteAllFiles() { RemoteOperationResult result = new ReadFolderRemoteOperation("/").execute(client); assertTrue(result.getLogMessage(), result.isSuccess()); @@ -186,7 +192,7 @@ protected void waitForIdleSync() { protected void openDrawer(IntentsTestRule activityRule) throws InterruptedException { Activity sut = activityRule.launchActivity(null); - Thread.sleep(3000); + shortSleep(); onView(withId(R.id.drawer_layout)).perform(DrawerActions.open()); @@ -208,4 +214,20 @@ protected Activity getCurrentActivity() { return currentActivity; } + + protected void shortSleep() { + try { + Thread.sleep(2000); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + + protected void longSleep() { + try { + Thread.sleep(20000); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } } diff --git a/src/androidTest/java/com/owncloud/android/ScreenshotsIT.java b/src/androidTest/java/com/owncloud/android/ScreenshotsIT.java index 53d61c032ade..c4f8cf471363 100644 --- a/src/androidTest/java/com/owncloud/android/ScreenshotsIT.java +++ b/src/androidTest/java/com/owncloud/android/ScreenshotsIT.java @@ -60,7 +60,7 @@ public void gridViewScreenshot() throws InterruptedException { openOverflowMenu(); onView(anyOf(withText(R.string.action_switch_grid_view), withId(R.id.action_switch_view))).perform(click()); - Thread.sleep(1000); + shortSleep(); Screengrab.screenshot("01_gridView"); @@ -70,12 +70,12 @@ public void gridViewScreenshot() throws InterruptedException { Assert.assertTrue(true); // if we reach this, everything is ok } - private void openOverflowMenu() throws InterruptedException { + private void openOverflowMenu() { try { Espresso.openContextualActionModeOverflowMenu(); } catch (NoActivityResumedException e) { ActivityScenario.launch(FileDisplayActivity.class); - Thread.sleep(1000); + shortSleep(); Espresso.openContextualActionModeOverflowMenu(); } } @@ -143,12 +143,12 @@ public void autoUploadScreenshot() { } @Test - public void davdroidScreenshot() throws InterruptedException { + public void davdroidScreenshot() { ActivityScenario.launch(SettingsActivity.class); onData(PreferenceMatchers.withTitle(R.string.prefs_category_more)).perform(ViewActions.scrollTo()); - Thread.sleep(1000); + shortSleep(); Screengrab.screenshot("06_davdroid"); diff --git a/src/androidTest/java/com/owncloud/android/files/services/FileUploaderIT.kt b/src/androidTest/java/com/owncloud/android/files/services/FileUploaderIT.kt index 724390f37c4d..8a38892a165b 100644 --- a/src/androidTest/java/com/owncloud/android/files/services/FileUploaderIT.kt +++ b/src/androidTest/java/com/owncloud/android/files/services/FileUploaderIT.kt @@ -42,9 +42,6 @@ import org.junit.Test import java.io.File class FileUploaderIT : AbstractIT() { - val SHORT_WAIT: Long = 5000 - val LONG_WAIT: Long = 20000 - var uploadsStorageManager: UploadsStorageManager? = null val connectivityServiceMock: ConnectivityService = object : ConnectivityService { @@ -149,7 +146,7 @@ class FileUploaderIT : AbstractIT() { false, FileUploader.NameCollisionPolicy.DEFAULT) - Thread.sleep(LONG_WAIT) + longSleep() val result = ReadFileRemoteOperation("/testFile.txt").execute(client) assertTrue(result.isSuccess) @@ -166,7 +163,7 @@ class FileUploaderIT : AbstractIT() { FileUploader.LOCAL_BEHAVIOUR_COPY, FileUploader.NameCollisionPolicy.OVERWRITE) - Thread.sleep(SHORT_WAIT) + shortSleep() val result2 = ReadFileRemoteOperation("/testFile.txt").execute(client) assertTrue(result2.isSuccess) @@ -256,7 +253,7 @@ class FileUploaderIT : AbstractIT() { false, FileUploader.NameCollisionPolicy.DEFAULT) - Thread.sleep(LONG_WAIT) + longSleep() val result = ReadFileRemoteOperation("/testFile.txt").execute(client) assertTrue(result.isSuccess) @@ -273,7 +270,7 @@ class FileUploaderIT : AbstractIT() { FileUploader.LOCAL_BEHAVIOUR_COPY, FileUploader.NameCollisionPolicy.RENAME) - Thread.sleep(SHORT_WAIT) + shortSleep() val result2 = ReadFileRemoteOperation("/testFile.txt").execute(client) assertTrue(result2.isSuccess) @@ -356,7 +353,7 @@ class FileUploaderIT : AbstractIT() { false, FileUploader.NameCollisionPolicy.DEFAULT) - Thread.sleep(LONG_WAIT) + longSleep() val result = ReadFileRemoteOperation("/testFile.txt").execute(client) assertTrue(result.isSuccess) @@ -373,7 +370,7 @@ class FileUploaderIT : AbstractIT() { FileUploader.LOCAL_BEHAVIOUR_COPY, FileUploader.NameCollisionPolicy.CANCEL) - Thread.sleep(SHORT_WAIT) + shortSleep() val result2 = ReadFileRemoteOperation("/testFile.txt").execute(client) assertTrue(result2.isSuccess) diff --git a/src/androidTest/java/com/owncloud/android/ui/activity/ConflictsResolveActivityIT.java b/src/androidTest/java/com/owncloud/android/ui/activity/ConflictsResolveActivityIT.java index bfe237f0319f..8dc32f8faf43 100644 --- a/src/androidTest/java/com/owncloud/android/ui/activity/ConflictsResolveActivityIT.java +++ b/src/androidTest/java/com/owncloud/android/ui/activity/ConflictsResolveActivityIT.java @@ -59,7 +59,7 @@ public class ConflictsResolveActivityIT extends AbstractIT { private boolean returnCode; @Test - public void screenshotTextFiles() throws InterruptedException { + public void screenshotTextFiles() { OCFile newFile = new OCFile("/newFile.txt"); newFile.setFileLength(56000); newFile.setModificationTimestamp(1522019340); @@ -88,13 +88,13 @@ public void screenshotTextFiles() throws InterruptedException { getInstrumentation().waitForIdleSync(); - Thread.sleep(2000); + shortSleep(); Screenshot.snap(dialog.getDialog().getWindow().getDecorView()).record(); } @Test - public void screenshotImages() throws InterruptedException, IOException { + public void screenshotImages() throws IOException { FileDataStorageManager storageManager = new FileDataStorageManager(account, targetContext.getContentResolver()); @@ -141,13 +141,13 @@ public void screenshotImages() throws InterruptedException, IOException { dialog.listener = listener; getInstrumentation().waitForIdleSync(); - Thread.sleep(2000); + shortSleep(); Screenshot.snap(dialog.getDialog().getWindow().getDecorView()).record(); } @Test - public void cancel() throws InterruptedException { + public void cancel() { returnCode = false; OCUpload newUpload = new OCUpload(FileStorageUtils.getSavePath(account.name) + "/nonEmpty.txt", @@ -179,7 +179,7 @@ public void cancel() throws InterruptedException { }; getInstrumentation().waitForIdleSync(); - Thread.sleep(2000); + shortSleep(); onView(withText("Cancel")).perform(click()); diff --git a/src/androidTest/java/com/owncloud/android/ui/activity/ManageAccountsActivityIT.java b/src/androidTest/java/com/owncloud/android/ui/activity/ManageAccountsActivityIT.java index e0de88c845f0..58aa429de820 100644 --- a/src/androidTest/java/com/owncloud/android/ui/activity/ManageAccountsActivityIT.java +++ b/src/androidTest/java/com/owncloud/android/ui/activity/ManageAccountsActivityIT.java @@ -40,22 +40,22 @@ public class ManageAccountsActivityIT extends AbstractIT { false); @Test - public void open() throws InterruptedException { + public void open() { Activity sut = activityRule.launchActivity(null); - Thread.sleep(2000); + shortSleep(); Screenshot.snapActivity(sut).record(); } @Test - public void userInfoDetail() throws InterruptedException { + public void userInfoDetail() { ManageAccountsActivity sut = activityRule.launchActivity(null); User user = sut.accountManager.getUser(); sut.onClick(user); - Thread.sleep(2000); + shortSleep(); Screenshot.snapActivity(getCurrentActivity()).record(); } diff --git a/src/androidTest/java/com/owncloud/android/ui/fragment/OCFileListFragmentIT.kt b/src/androidTest/java/com/owncloud/android/ui/fragment/OCFileListFragmentIT.kt index d46694e471ed..2e4750ff9f82 100644 --- a/src/androidTest/java/com/owncloud/android/ui/fragment/OCFileListFragmentIT.kt +++ b/src/androidTest/java/com/owncloud/android/ui/fragment/OCFileListFragmentIT.kt @@ -23,8 +23,11 @@ package com.owncloud.android.ui.fragment import android.Manifest import androidx.test.core.app.ActivityScenario +import androidx.test.espresso.intent.rule.IntentsTestRule +import androidx.test.platform.app.InstrumentationRegistry import androidx.test.rule.GrantPermissionRule import com.evernote.android.job.JobRequest +import com.facebook.testing.screenshot.Screenshot import com.nextcloud.client.account.UserAccountManagerImpl import com.nextcloud.client.device.PowerManagementService import com.nextcloud.client.network.ConnectivityService @@ -36,16 +39,30 @@ import com.owncloud.android.MainApp import com.owncloud.android.datamodel.UploadsStorageManager import com.owncloud.android.db.OCUpload import com.owncloud.android.files.services.FileUploader +import com.owncloud.android.lib.resources.shares.CreateShareRemoteOperation +import com.owncloud.android.lib.resources.shares.GetShareesRemoteOperation +import com.owncloud.android.lib.resources.shares.OCShare +import com.owncloud.android.lib.resources.shares.ShareType import com.owncloud.android.operations.CreateFolderOperation import com.owncloud.android.operations.RefreshFolderOperation import com.owncloud.android.operations.UploadFileOperation import com.owncloud.android.ui.activity.FileDisplayActivity import com.owncloud.android.utils.FileStorageUtils -import junit.framework.Assert.assertTrue +import junit.framework.TestCase +import org.json.JSONObject +import org.junit.Assert.assertTrue import org.junit.Rule import org.junit.Test class OCFileListFragmentIT : AbstractIT() { + companion object { + val SECOND_IN_MILLIS = 1000L + val RESULT_PER_PAGE = 50 + } + + @get:Rule + val activityRule = IntentsTestRule(FileDisplayActivity::class.java, true, false) + @get:Rule val permissionRule = GrantPermissionRule.grant(Manifest.permission.WRITE_EXTERNAL_STORAGE) @@ -92,16 +109,15 @@ class OCFileListFragmentIT : AbstractIT() { FileUploader.LOCAL_BEHAVIOUR_COPY, targetContext, false, - false - ) - newUpload.addRenameUploadListener {} + false) + newUpload.addRenameUploadListener {} newUpload.setRemoteFolderToBeCreated() assertTrue(newUpload.execute(client, storageManager).isSuccess) assertTrue(RefreshFolderOperation(storageManager.getFileByPath("/test/"), - System.currentTimeMillis() / 1000, + System.currentTimeMillis() / SECOND_IN_MILLIS, false, true, storageManager, @@ -111,10 +127,10 @@ class OCFileListFragmentIT : AbstractIT() { val sut = ActivityScenario.launch(FileDisplayActivity::class.java) sut.onActivity { activity -> activity.onBrowsedDownTo(storageManager.getFileByPath("/test/")) } - Thread.sleep(2000) + shortSleep() sut.onActivity { activity -> - com.facebook.testing.screenshot.Screenshot.snapActivity(activity).setName("richworkspaces_light").record() + Screenshot.snapActivity(activity).setName("richworkspaces_light").record() } val preferences: AppPreferences = AppPreferencesImpl.fromContext(targetContext) @@ -127,10 +143,10 @@ class OCFileListFragmentIT : AbstractIT() { sut.onActivity { activity -> activity.onBrowsedDownTo(storageManager.getFileByPath("/test/")) } - Thread.sleep(2000) + shortSleep() sut.onActivity { activity -> - com.facebook.testing.screenshot.Screenshot.snapActivity(activity).setName("richworkspaces_dark").record() + Screenshot.snapActivity(activity).setName("richworkspaces_dark").record() } // switch back to light mode @@ -141,4 +157,99 @@ class OCFileListFragmentIT : AbstractIT() { sut.recreate() } + + @Test + fun createAndShowShareToUser() { + val path = "/shareToAdmin/" + TestCase.assertTrue(CreateFolderOperation(path, true).execute(client, storageManager).isSuccess) + + // share folder to user "admin" + TestCase.assertTrue(CreateShareRemoteOperation(path, + ShareType.USER, + "admin", + false, + "", + OCShare.MAXIMUM_PERMISSIONS_FOR_FOLDER) + .execute(client).isSuccess) + + val sut: FileDisplayActivity = activityRule.launchActivity(null) + sut.startSyncFolderOperation(storageManager.getFileByPath("/"), true) + + shortSleep() + InstrumentationRegistry.getInstrumentation().waitForIdleSync() + Screenshot.snapActivity(sut).record() + } + + @Test + fun createAndShowShareToGroup() { + val path = "/shareToGroup/" + TestCase.assertTrue(CreateFolderOperation(path, true).execute(client, storageManager).isSuccess) + + // share folder to group + assertTrue(CreateShareRemoteOperation("/shareToGroup/", + ShareType.GROUP, + "users", + false, + "", + OCShare.DEFAULT_PERMISSION) + .execute(client).isSuccess) + + val sut: FileDisplayActivity = activityRule.launchActivity(null) + sut.startSyncFolderOperation(storageManager.getFileByPath("/"), true) + + shortSleep() + InstrumentationRegistry.getInstrumentation().waitForIdleSync() + Screenshot.snapActivity(sut).record() + } + + @Test + fun createAndShowShareToCircle() { + val path = "/shareToCircle/" + TestCase.assertTrue(CreateFolderOperation(path, true).execute(client, storageManager).isSuccess) + + // share folder to circle + // get circleId + val searchResult = GetShareesRemoteOperation("publicCircle", 1, RESULT_PER_PAGE).execute(client) + assertTrue(searchResult.logMessage, searchResult.isSuccess) + + val resultJson: JSONObject = searchResult.data[0] as JSONObject + val circleId: String = resultJson.getJSONObject("value").getString("shareWith") + + assertTrue(CreateShareRemoteOperation("/shareToCircle/", + ShareType.CIRCLE, + circleId, + false, + "", + OCShare.DEFAULT_PERMISSION) + .execute(client).isSuccess) + + val sut: FileDisplayActivity = activityRule.launchActivity(null) + sut.startSyncFolderOperation(storageManager.getFileByPath("/"), true) + + shortSleep() + InstrumentationRegistry.getInstrumentation().waitForIdleSync() + Screenshot.snapActivity(sut).record() + } + + @Test + fun createAndShowShareViaLink() { + val path = "/shareViaLink/" + TestCase.assertTrue(CreateFolderOperation(path, true).execute(client, storageManager).isSuccess) + + // share folder via public link + TestCase.assertTrue(CreateShareRemoteOperation("/shareViaLink/", + ShareType.PUBLIC_LINK, + "", + true, + "", + OCShare.READ_PERMISSION_FLAG) + .execute(client).isSuccess) + + val sut: FileDisplayActivity = activityRule.launchActivity(null) + sut.startSyncFolderOperation(storageManager.getFileByPath("/"), true) + + shortSleep() + InstrumentationRegistry.getInstrumentation().waitForIdleSync() + Screenshot.snapActivity(sut).record() + } } diff --git a/src/androidTest/java/com/owncloud/android/ui/preview/PreviewTextFileFragmentTest.java b/src/androidTest/java/com/owncloud/android/ui/preview/PreviewTextFileFragmentTest.java index ad7f04bce4ef..0c64a9894111 100644 --- a/src/androidTest/java/com/owncloud/android/ui/preview/PreviewTextFileFragmentTest.java +++ b/src/androidTest/java/com/owncloud/android/ui/preview/PreviewTextFileFragmentTest.java @@ -53,7 +53,7 @@ public class PreviewTextFileFragmentTest extends AbstractIT { public void displaySimpleTextFile() throws InterruptedException { FileDisplayActivity sut = activityRule.launchActivity(null); - Thread.sleep(3000); + shortSleep(); File file = new File(FileStorageUtils.getSavePath(account.name) + "/nonEmpty.txt"); OCFile test = new OCFile("/text.md"); @@ -61,7 +61,7 @@ public void displaySimpleTextFile() throws InterruptedException { test.setStoragePath(file.getAbsolutePath()); sut.startTextPreview(test, false); - Thread.sleep(3000); + shortSleep(); Screenshot.snapActivity(sut).record(); } @@ -70,7 +70,7 @@ public void displaySimpleTextFile() throws InterruptedException { public void displayJavaSnippetFile() throws IOException, InterruptedException { FileDisplayActivity sut = activityRule.launchActivity(null); - Thread.sleep(3000); + shortSleep(); File file = getFile("java.md"); OCFile test = new OCFile("/java.md"); @@ -78,7 +78,7 @@ public void displayJavaSnippetFile() throws IOException, InterruptedException { test.setStoragePath(file.getAbsolutePath()); sut.startTextPreview(test, false); - Thread.sleep(3000); + shortSleep(); Screenshot.snapActivity(sut).record(); } diff --git a/src/main/java/com/owncloud/android/datamodel/FileDataStorageManager.java b/src/main/java/com/owncloud/android/datamodel/FileDataStorageManager.java index 60bbc4652923..5da0cb4e90f1 100644 --- a/src/main/java/com/owncloud/android/datamodel/FileDataStorageManager.java +++ b/src/main/java/com/owncloud/android/datamodel/FileDataStorageManager.java @@ -1695,15 +1695,20 @@ public List getSharesWithForAFile(String filePath, String accountName) + ProviderTableMeta.OCSHARES_SHARE_TYPE + "=? OR " + ProviderTableMeta.OCSHARES_SHARE_TYPE + "=? OR " + ProviderTableMeta.OCSHARES_SHARE_TYPE + "=? OR " + + ProviderTableMeta.OCSHARES_SHARE_TYPE + "=? OR " + ProviderTableMeta.OCSHARES_SHARE_TYPE + "=? ) "; - String[] whereArgs = new String[]{filePath, accountName, - Integer.toString(ShareType.USER.getValue()), - Integer.toString(ShareType.GROUP.getValue()), - Integer.toString(ShareType.EMAIL.getValue()), - Integer.toString(ShareType.FEDERATED.getValue()), - Integer.toString(ShareType.ROOM.getValue())}; - - Cursor cursor = null; + String[] whereArgs = new String[]{ + filePath, + accountName, + Integer.toString(ShareType.USER.getValue()), + Integer.toString(ShareType.GROUP.getValue()), + Integer.toString(ShareType.EMAIL.getValue()), + Integer.toString(ShareType.FEDERATED.getValue()), + Integer.toString(ShareType.ROOM.getValue()), + Integer.toString(ShareType.CIRCLE.getValue()) + }; + + Cursor cursor; if (getContentResolver() != null) { cursor = getContentResolver().query(ProviderTableMeta.CONTENT_URI_SHARE, null, where, whereArgs, null); } else { diff --git a/src/main/java/com/owncloud/android/operations/CreateShareWithShareeOperation.java b/src/main/java/com/owncloud/android/operations/CreateShareWithShareeOperation.java index e9b13895334b..c32f46d7e3e8 100644 --- a/src/main/java/com/owncloud/android/operations/CreateShareWithShareeOperation.java +++ b/src/main/java/com/owncloud/android/operations/CreateShareWithShareeOperation.java @@ -47,7 +47,11 @@ public class CreateShareWithShareeOperation extends SyncOperation { private int permissions; private static final List supportedShareTypes = new ArrayList<>(Arrays.asList(ShareType.USER, - ShareType.GROUP, ShareType.FEDERATED, ShareType.EMAIL, ShareType.ROOM)); + ShareType.GROUP, + ShareType.FEDERATED, + ShareType.EMAIL, + ShareType.ROOM, + ShareType.CIRCLE)); /** * Constructor. @@ -76,8 +80,8 @@ protected RemoteOperationResult run(OwnCloudClient client) { path, shareType, shareeName, - false, - "", + false, + "", permissions ); operation.setGetShareDetails(true); diff --git a/src/main/java/com/owncloud/android/providers/FileContentProvider.java b/src/main/java/com/owncloud/android/providers/FileContentProvider.java index 3fd9a3af48da..95b95108e292 100644 --- a/src/main/java/com/owncloud/android/providers/FileContentProvider.java +++ b/src/main/java/com/owncloud/android/providers/FileContentProvider.java @@ -402,6 +402,7 @@ private void updateFilesTableAccordingToShareInsertion(SQLiteDatabase db, Conten case EMAIL: case FEDERATED: case ROOM: + case CIRCLE: fileValues.put(ProviderTableMeta.FILE_SHARED_WITH_SHAREE, 1); break; diff --git a/src/main/java/com/owncloud/android/providers/UsersAndGroupsSearchProvider.java b/src/main/java/com/owncloud/android/providers/UsersAndGroupsSearchProvider.java index 0daa551ab90a..f04bcde178ea 100644 --- a/src/main/java/com/owncloud/android/providers/UsersAndGroupsSearchProvider.java +++ b/src/main/java/com/owncloud/android/providers/UsersAndGroupsSearchProvider.java @@ -20,7 +20,6 @@ package com.owncloud.android.providers; -import android.accounts.Account; import android.app.SearchManager; import android.content.ContentProvider; import android.content.ContentValues; @@ -90,6 +89,7 @@ public class UsersAndGroupsSearchProvider extends ContentProvider { private String DATA_ROOM; private String DATA_REMOTE; private String DATA_EMAIL; + private String DATA_CIRCLE; private UriMatcher mUriMatcher; @@ -129,12 +129,14 @@ public boolean onCreate() { DATA_ROOM = AUTHORITY + ".data.room"; DATA_REMOTE = AUTHORITY + ".data.remote"; DATA_EMAIL = AUTHORITY + ".data.email"; + DATA_CIRCLE = AUTHORITY + ".data.circle"; sShareTypes.put(DATA_USER, ShareType.USER); sShareTypes.put(DATA_GROUP, ShareType.GROUP); sShareTypes.put(DATA_ROOM, ShareType.ROOM); sShareTypes.put(DATA_REMOTE, ShareType.FEDERATED); sShareTypes.put(DATA_EMAIL, ShareType.EMAIL); + sShareTypes.put(DATA_CIRCLE, ShareType.CIRCLE); mUriMatcher = new UriMatcher(UriMatcher.NO_MATCH); mUriMatcher.addURI(AUTHORITY, SearchManager.SUGGEST_URI_PATH_QUERY + "/*", SEARCH); @@ -213,6 +215,7 @@ private Cursor searchForUsersOrGroups(Uri uri) { Uri roomBaseUri = new Uri.Builder().scheme(CONTENT).authority(DATA_ROOM).build(); Uri remoteBaseUri = new Uri.Builder().scheme(CONTENT).authority(DATA_REMOTE).build(); Uri emailBaseUri = new Uri.Builder().scheme(CONTENT).authority(DATA_EMAIL).build(); + Uri circleBaseUri = new Uri.Builder().scheme(CONTENT).authority(DATA_CIRCLE).build(); FileDataStorageManager manager = new FileDataStorageManager(user.toPlatformAccount(), getContext().getContentResolver()); @@ -276,6 +279,12 @@ private Cursor searchForUsersOrGroups(Uri uri) { dataUri = Uri.withAppendedPath(roomBaseUri, shareWith); break; + case CIRCLE: + icon = R.drawable.ic_circles; + displayName = userName; + dataUri = Uri.withAppendedPath(circleBaseUri, shareWith); + break; + default: break; } diff --git a/src/main/java/com/owncloud/android/ui/adapter/OCFileListAdapter.java b/src/main/java/com/owncloud/android/ui/adapter/OCFileListAdapter.java index 5c09d8ba50f1..ff08e048d5ac 100644 --- a/src/main/java/com/owncloud/android/ui/adapter/OCFileListAdapter.java +++ b/src/main/java/com/owncloud/android/ui/adapter/OCFileListAdapter.java @@ -30,7 +30,6 @@ import android.content.Context; import android.content.res.Resources; import android.graphics.Bitmap; -import android.graphics.Color; import android.graphics.LinearGradient; import android.graphics.PorterDuff; import android.graphics.Shader; @@ -408,7 +407,9 @@ public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int positi // use fileOwner if not oneself, then add at first ShareeUser fileOwnerSharee = new ShareeUser(fileOwner, file.getOwnerDisplayName(), ShareType.USER); - if (fileOwner != null && !fileOwner.equals(userId) && !sharees.contains(fileOwnerSharee)) { + if (!TextUtils.isEmpty(fileOwner) && + !fileOwner.equals(userId) && + !sharees.contains(fileOwnerSharee)) { sharees.add(fileOwnerSharee); } @@ -422,7 +423,7 @@ public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int positi int size = 60 * (shareeSize - 1) + w; for (int i = 0; i < shareeSize; i++) { - ShareeUser sharee = file.getSharees().get(i); + ShareeUser sharee = sharees.get(i); ImageView avatar = new ImageView(activity); @@ -437,6 +438,8 @@ public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int positi Log_OC.e(TAG, "Error calculating RGB value for active account icon.", e); avatar.setImageResource(R.drawable.ic_people); } + } else if (sharee.getShareType().equals(ShareType.CIRCLE)) { + avatar.setImageResource(R.drawable.ic_circles); } else if (sharee.getUserId().contains("@")) { showFederatedShareAvatar(sharee.getUserId(), avatarRadius, resources, avatar); } else { @@ -900,8 +903,12 @@ private void parseShares(List objects) { ShareType newShareType = ocShare.getShareType(); if (newShareType == ShareType.PUBLIC_LINK) { file.setSharedViaLink(true); - } else if (newShareType == ShareType.USER || newShareType == ShareType.GROUP || - newShareType == ShareType.EMAIL || newShareType == ShareType.FEDERATED) { + } else if (newShareType == ShareType.USER || + newShareType == ShareType.GROUP || + newShareType == ShareType.EMAIL || + newShareType == ShareType.FEDERATED || + newShareType == ShareType.ROOM || + newShareType == ShareType.CIRCLE) { file.setSharedWithSharee(true); } diff --git a/src/main/java/com/owncloud/android/ui/adapter/ShareeListAdapter.java b/src/main/java/com/owncloud/android/ui/adapter/ShareeListAdapter.java index 52e213ceb0ee..54840880174f 100644 --- a/src/main/java/com/owncloud/android/ui/adapter/ShareeListAdapter.java +++ b/src/main/java/com/owncloud/android/ui/adapter/ShareeListAdapter.java @@ -117,6 +117,9 @@ public void onBindViewHolder(@NonNull UserViewHolder holder, int position) { name = context.getString(R.string.share_room_clarification, name); setImage(holder, name, R.drawable.ic_chat_bubble); break; + case CIRCLE: + holder.avatar.setImageResource(R.drawable.ic_circles); + break; default: setImage(holder, name, R.drawable.ic_user); break; diff --git a/src/main/res/drawable/ic_circles.xml b/src/main/res/drawable/ic_circles.xml new file mode 100644 index 000000000000..5a68c98fbf5f --- /dev/null +++ b/src/main/res/drawable/ic_circles.xml @@ -0,0 +1,25 @@ + + + + + +