mirror of
				https://github.com/owncloud/android-library.git
				synced 2025-10-31 18:37:17 +00:00 
			
		
		
		
	
						commit
						c36096d08c
					
				| @ -1,6 +1,6 @@ | ||||
| buildscript { | ||||
|     ext { | ||||
|         kotlinVersion = '1.3.72' | ||||
|         kotlinVersion = '1.4.20' | ||||
|         moshiVersion = "1.9.2" | ||||
|     } | ||||
| 
 | ||||
| @ -9,9 +9,8 @@ buildscript { | ||||
|         jcenter() | ||||
|     } | ||||
|     dependencies { | ||||
|         classpath 'com.android.tools.build:gradle:4.0.1' | ||||
|         classpath 'com.android.tools.build:gradle:4.1.1' | ||||
|         classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlinVersion" | ||||
|         classpath "org.jetbrains.kotlin:kotlin-allopen:$kotlinVersion" | ||||
|     } | ||||
| } | ||||
| 
 | ||||
|  | ||||
| @ -1,3 +1,3 @@ | ||||
| android.enableJetifier=true | ||||
| android.useAndroidX=true | ||||
| org.gradle.jvmargs=-Xmx1536M | ||||
| org.gradle.jvmargs=-Xmx1536M | ||||
|  | ||||
| @ -1,25 +1,20 @@ | ||||
| apply plugin: 'com.android.library' | ||||
| apply plugin: 'kotlin-android' | ||||
| apply plugin: 'kotlin-kapt' | ||||
| apply plugin: 'kotlin-allopen' | ||||
| 
 | ||||
| dependencies { | ||||
|     api 'com.squareup.okhttp3:okhttp:4.6.0' | ||||
|     implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlinVersion" | ||||
|     api 'com.gitlab.ownclouders:dav4android:oc_support_2.1.5' | ||||
|     api 'com.github.AppDevNext.Logcat:LogcatCore:2.1.1' | ||||
|     api 'net.openid:appauth:0.7.1' | ||||
|     api 'com.github.AppDevNext.Logcat:LogcatCore:2.2.2' | ||||
| 
 | ||||
|     // Moshi | ||||
|     implementation ("com.squareup.moshi:moshi-kotlin:$moshiVersion") { | ||||
|         exclude module: "kotlin-reflect" | ||||
|     } | ||||
|     kapt "com.squareup.moshi:moshi-kotlin-codegen:$moshiVersion" | ||||
| } | ||||
| 
 | ||||
| allOpen { | ||||
|     // allows mocking for classes w/o directly opening them for release builds | ||||
|     annotation 'com.owncloud.android.lib.testing.OpenClass' | ||||
|     testImplementation 'junit:junit:4.13.1' | ||||
| } | ||||
| 
 | ||||
| android { | ||||
| @ -29,12 +24,8 @@ android { | ||||
|         minSdkVersion 21 | ||||
|         targetSdkVersion 29 | ||||
| 
 | ||||
|         versionCode = 10000800 | ||||
|         versionName = "1.0.8" | ||||
| 
 | ||||
|         // This is pretty ugly but manifest placeholders don't seem to work very well when using different modules | ||||
|         // See https://github.com/openid/AppAuth-Android/issues/325 | ||||
|         manifestPlaceholders = [appAuthRedirectScheme: ''] | ||||
|         versionCode = 10000900 | ||||
|         versionName = "1.0.9" | ||||
|     } | ||||
| 
 | ||||
|     lintOptions { | ||||
|  | ||||
| @ -57,6 +57,7 @@ public class HttpClient { | ||||
|     private static OkHttpClient sOkHttpClient; | ||||
|     private static Context sContext; | ||||
|     private static HashMap<String, List<Cookie>> sCookieStore = new HashMap<>(); | ||||
|     private static LogInterceptor sLogInterceptor; | ||||
| 
 | ||||
|     public static OkHttpClient getOkHttpClient() { | ||||
|         if (sOkHttpClient == null) { | ||||
| @ -110,6 +111,7 @@ public class HttpClient { | ||||
|                 }; | ||||
| 
 | ||||
|                 OkHttpClient.Builder clientBuilder = new OkHttpClient.Builder() | ||||
|                         .addNetworkInterceptor(getLogInterceptor()) | ||||
|                         .protocols(Arrays.asList(Protocol.HTTP_1_1)) | ||||
|                         .readTimeout(HttpConstants.DEFAULT_DATA_TIMEOUT, TimeUnit.MILLISECONDS) | ||||
|                         .writeTimeout(HttpConstants.DEFAULT_DATA_TIMEOUT, TimeUnit.MILLISECONDS) | ||||
| @ -120,6 +122,7 @@ public class HttpClient { | ||||
|                         .cookieJar(cookieJar); | ||||
|                 // TODO: Not verifying the hostname against certificate. ask owncloud security human if this is ok. | ||||
|                 //.hostnameVerifier(new BrowserCompatHostnameVerifier()); | ||||
| 
 | ||||
|                 sOkHttpClient = clientBuilder.build(); | ||||
| 
 | ||||
|             } catch (Exception e) { | ||||
| @ -137,6 +140,13 @@ public class HttpClient { | ||||
|         sContext = context; | ||||
|     } | ||||
| 
 | ||||
|     public static LogInterceptor getLogInterceptor() { | ||||
|         if (sLogInterceptor == null) { | ||||
|             sLogInterceptor = new LogInterceptor(); | ||||
|         } | ||||
|         return sLogInterceptor; | ||||
|     } | ||||
| 
 | ||||
|     public List<Cookie> getCookiesFromUrl(HttpUrl httpUrl) { | ||||
|         return sCookieStore.get(httpUrl.host()); | ||||
|     } | ||||
|  | ||||
| @ -51,6 +51,14 @@ public class HttpConstants { | ||||
|     public static final String ACCEPT_ENCODING_IDENTITY = "identity"; | ||||
|     public static final String OC_FILE_REMOTE_ID = "OC-FileId"; | ||||
| 
 | ||||
|     /*********************************************************************************************************** | ||||
|      ************************************************ CONTENT TYPES ******************************************** | ||||
|      ***********************************************************************************************************/ | ||||
| 
 | ||||
|     public static final String CONTENT_TYPE_XML = "application/xml"; | ||||
|     public static final String CONTENT_TYPE_JSON = "application/json"; | ||||
|     public static final String CONTENT_TYPE_WWW_FORM = "application/x-www-form-urlencoded"; | ||||
| 
 | ||||
|     /*********************************************************************************************************** | ||||
|      ************************************************ STATUS CODES ********************************************* | ||||
|      ***********************************************************************************************************/ | ||||
|  | ||||
| @ -0,0 +1,65 @@ | ||||
| /* ownCloud Android Library is available under MIT license | ||||
|  *   Copyright (C) 2020 ownCloud GmbH. | ||||
|  * | ||||
|  *   Permission is hereby granted, free of charge, to any person obtaining a copy | ||||
|  *   of this software and associated documentation files (the "Software"), to deal | ||||
|  *   in the Software without restriction, including without limitation the rights | ||||
|  *   to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | ||||
|  *   copies of the Software, and to permit persons to whom the Software is | ||||
|  *   furnished to do so, subject to the following conditions: | ||||
|  * | ||||
|  *   The above copyright notice and this permission notice shall be included in | ||||
|  *   all copies or substantial portions of the Software. | ||||
|  * | ||||
|  *   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, | ||||
|  *   EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF | ||||
|  *   MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND | ||||
|  *   NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS | ||||
|  *   BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN | ||||
|  *   ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN | ||||
|  *   CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | ||||
|  *   THE SOFTWARE. | ||||
|  */ | ||||
| package com.owncloud.android.lib.common.http | ||||
| 
 | ||||
| import com.owncloud.android.lib.common.http.HttpConstants.CONTENT_TYPE_JSON | ||||
| import com.owncloud.android.lib.common.http.HttpConstants.CONTENT_TYPE_WWW_FORM | ||||
| import com.owncloud.android.lib.common.http.HttpConstants.CONTENT_TYPE_XML | ||||
| import okhttp3.MediaType | ||||
| import timber.log.Timber | ||||
| import java.util.Locale | ||||
| 
 | ||||
| object LogBuilder { | ||||
|     fun logHttp( | ||||
|         networkPetition: NetworkPetition, | ||||
|         networkNode: NetworkNode, | ||||
|         requestId: String? = "", | ||||
|         description: String | ||||
|     ) = Timber.d("[Network, $networkPetition] [$networkNode] [$requestId] $description") | ||||
| } | ||||
| 
 | ||||
| enum class NetworkPetition { | ||||
|     REQUEST, RESPONSE; | ||||
| 
 | ||||
|     override fun toString(): String = super.toString().toLowerCase(Locale.ROOT) | ||||
| } | ||||
| 
 | ||||
| enum class NetworkNode { | ||||
|     INFO, HEADER, BODY; | ||||
| 
 | ||||
|     override fun toString(): String = super.toString().toLowerCase(Locale.ROOT) | ||||
| } | ||||
| 
 | ||||
| /** | ||||
|  * Check whether a media type is loggable. | ||||
|  * | ||||
|  * @return true if its type is text, xml, json, or x-www-form-urlencoded. | ||||
|  */ | ||||
| fun MediaType?.isLoggable(): Boolean = | ||||
|     this?.let { mediaType -> | ||||
|         val mediaTypeString = mediaType.toString() | ||||
|         (mediaType.type == "text" || | ||||
|                 mediaTypeString.contains(CONTENT_TYPE_XML) || | ||||
|                 mediaTypeString.contains(CONTENT_TYPE_JSON) || | ||||
|                 mediaTypeString.contains(CONTENT_TYPE_WWW_FORM)) | ||||
|     } ?: false | ||||
| @ -0,0 +1,179 @@ | ||||
| /* ownCloud Android Library is available under MIT license | ||||
|  *   Copyright (C) 2020 ownCloud GmbH. | ||||
|  * | ||||
|  *   Permission is hereby granted, free of charge, to any person obtaining a copy | ||||
|  *   of this software and associated documentation files (the "Software"), to deal | ||||
|  *   in the Software without restriction, including without limitation the rights | ||||
|  *   to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | ||||
|  *   copies of the Software, and to permit persons to whom the Software is | ||||
|  *   furnished to do so, subject to the following conditions: | ||||
|  * | ||||
|  *   The above copyright notice and this permission notice shall be included in | ||||
|  *   all copies or substantial portions of the Software. | ||||
|  * | ||||
|  *   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, | ||||
|  *   EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF | ||||
|  *   MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND | ||||
|  *   NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS | ||||
|  *   BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN | ||||
|  *   ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN | ||||
|  *   CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | ||||
|  *   THE SOFTWARE. | ||||
|  * | ||||
|  */ | ||||
| package com.owncloud.android.lib.common.http | ||||
| 
 | ||||
| import com.owncloud.android.lib.common.http.HttpConstants.AUTHORIZATION_HEADER | ||||
| import com.owncloud.android.lib.common.http.HttpConstants.OC_X_REQUEST_ID | ||||
| import com.owncloud.android.lib.common.http.LogBuilder.logHttp | ||||
| import com.owncloud.android.lib.common.http.NetworkNode.BODY | ||||
| import com.owncloud.android.lib.common.http.NetworkNode.HEADER | ||||
| import com.owncloud.android.lib.common.http.NetworkNode.INFO | ||||
| import com.owncloud.android.lib.common.http.NetworkPetition.REQUEST | ||||
| import com.owncloud.android.lib.common.http.NetworkPetition.RESPONSE | ||||
| import okhttp3.Headers | ||||
| import okhttp3.Interceptor | ||||
| import okhttp3.RequestBody | ||||
| import okhttp3.Response | ||||
| import okhttp3.ResponseBody | ||||
| import okio.Buffer | ||||
| import java.nio.charset.Charset | ||||
| import java.nio.charset.StandardCharsets | ||||
| import kotlin.math.max | ||||
| 
 | ||||
| class LogInterceptor : Interceptor { | ||||
| 
 | ||||
|     override fun intercept(chain: Interceptor.Chain): Response { | ||||
| 
 | ||||
|         if (!httpLogsEnabled) { | ||||
|             return chain.proceed(chain.request()) | ||||
|         } | ||||
| 
 | ||||
|         val request = chain.request().also { | ||||
|             val requestId = it.headers[OC_X_REQUEST_ID] | ||||
|             logHttp(REQUEST, INFO, requestId, "Type: ${it.method} URL: ${it.url}") | ||||
|             logHeaders(requestId, it.headers, REQUEST) | ||||
|             logRequestBody(requestId, it.body) | ||||
|         } | ||||
| 
 | ||||
|         val response = chain.proceed(request) | ||||
| 
 | ||||
|         return response.also { | ||||
|             val requestId = it.request.headers[OC_X_REQUEST_ID] | ||||
|             logHttp( | ||||
|                 RESPONSE, | ||||
|                 INFO, | ||||
|                 requestId, | ||||
|                 "Code: ${it.code}  Message: ${it.message} IsSuccessful: ${it.isSuccessful}" | ||||
|             ) | ||||
|             logHeaders(requestId, it.headers, RESPONSE) | ||||
|             logResponseBody(requestId, it.body) | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     private fun logHeaders(requestId: String?, headers: Headers, networkPetition: NetworkPetition) { | ||||
|         headers.forEach { header -> | ||||
|             val headerValue: String = if (header.first.equals(AUTHORIZATION_HEADER, true)) { | ||||
|                 "[redacted]" | ||||
|             } else { | ||||
|                 header.second | ||||
|             } | ||||
|             logHttp(networkPetition, HEADER, requestId, "${header.first}: $headerValue") | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     private fun logRequestBody(requestId: String?, requestBodyParam: RequestBody?) { | ||||
|         requestBodyParam?.let { requestBody -> | ||||
| 
 | ||||
|             if (requestBody.isOneShot()) { | ||||
|                 logHttp(REQUEST, BODY, requestId, "One shot body -- Omitted") | ||||
|                 return@let | ||||
|             } | ||||
| 
 | ||||
|             if (requestBody.isDuplex()) { | ||||
|                 logHttp(REQUEST, BODY, requestId, "Duplex body -- Omitted") | ||||
|                 return@let | ||||
|             } | ||||
| 
 | ||||
|             val buffer = Buffer() | ||||
|             requestBody.writeTo(buffer) | ||||
| 
 | ||||
|             val contentType = requestBody.contentType() | ||||
|             val charset: Charset = contentType?.charset(StandardCharsets.UTF_8) ?: StandardCharsets.UTF_8 | ||||
| 
 | ||||
|             logHttp(REQUEST, BODY, requestId, "Length: ${requestBody.contentLength()} byte body") | ||||
|             logHttp(REQUEST, BODY, requestId, "Type: ${requestBody.contentType()}") | ||||
|             logHttp(REQUEST, BODY, requestId, "--> Body start for request") | ||||
| 
 | ||||
|             if (contentType.isLoggable()) { | ||||
|                 if (requestBody.contentLength() < LIMIT_BODY_LOG) { | ||||
|                     logHttp(REQUEST, BODY, requestId, buffer.readString(charset)) | ||||
|                 } else { | ||||
|                     logHttp(REQUEST, BODY, requestId, buffer.readString(LIMIT_BODY_LOG, charset)) | ||||
|                 } | ||||
|                 logHttp( | ||||
|                     REQUEST, | ||||
|                     BODY, | ||||
|                     requestId, | ||||
|                     "<-- Body end for request -- Omitted: ${max(0, requestBody.contentLength() - LIMIT_BODY_LOG)} bytes" | ||||
|                 ) | ||||
|             } else { | ||||
|                 logHttp( | ||||
|                     REQUEST, | ||||
|                     BODY, | ||||
|                     requestId, | ||||
|                     "<-- Body end for request -- Binary -- Omitted: ${requestBody.contentLength()} bytes" | ||||
|                 ) | ||||
|             } | ||||
| 
 | ||||
|         } ?: logHttp(REQUEST, BODY, requestId, "Empty body") | ||||
|     } | ||||
| 
 | ||||
|     private fun logResponseBody(requestId: String?, responseBodyParam: ResponseBody?) { | ||||
|         responseBodyParam?.let { responseBody -> | ||||
| 
 | ||||
|             val contentType = responseBody.contentType() | ||||
|             val charset: Charset = contentType?.charset(StandardCharsets.UTF_8) ?: StandardCharsets.UTF_8 | ||||
| 
 | ||||
|             logHttp(RESPONSE, BODY, requestId, "Length: ${responseBody.contentLength()} byte body") | ||||
|             logHttp(RESPONSE, BODY, requestId, "Type: ${responseBody.contentType()}") | ||||
|             logHttp(RESPONSE, BODY, requestId, "--> Body start for response") | ||||
| 
 | ||||
|             val source = responseBody.source() | ||||
|             source.request(LIMIT_BODY_LOG) | ||||
|             val buffer = source.buffer | ||||
| 
 | ||||
|             if (contentType.isLoggable()) { | ||||
| 
 | ||||
|                 if (responseBody.contentLength() < LIMIT_BODY_LOG) { | ||||
|                     logHttp(RESPONSE, BODY, requestId, buffer.clone().readString(charset)) | ||||
|                 } else { | ||||
|                     logHttp(RESPONSE, BODY, requestId, buffer.clone().readString(LIMIT_BODY_LOG, charset)) | ||||
|                 } | ||||
|                 logHttp( | ||||
|                     RESPONSE, | ||||
|                     BODY, | ||||
|                     requestId, | ||||
|                     "<-- Body end for response -- Omitted: ${ | ||||
|                         max( | ||||
|                             0, | ||||
|                             responseBody.contentLength() - LIMIT_BODY_LOG | ||||
|                         ) | ||||
|                     } bytes" | ||||
|                 ) | ||||
|             } else { | ||||
|                 logHttp( | ||||
|                     RESPONSE, | ||||
|                     BODY, | ||||
|                     requestId, | ||||
|                     "<-- Body end for response -- Binary -- Omitted: ${responseBody.contentLength()} bytes" | ||||
|                 ) | ||||
|             } | ||||
|         } ?: logHttp(RESPONSE, BODY, requestId, "Empty body") | ||||
|     } | ||||
| 
 | ||||
|     companion object { | ||||
|         var httpLogsEnabled: Boolean = false | ||||
|         private const val LIMIT_BODY_LOG: Long = 1024 | ||||
|     } | ||||
| } | ||||
| @ -84,18 +84,12 @@ public class ChunkFromFileRequestBody extends FileRequestBody { | ||||
|             long maxCount = Math.min(mOffset + mChunkSize, mChannel.size()); | ||||
|             while (mChannel.position() < maxCount) { | ||||
| 
 | ||||
|                 Timber.v("Sink buffer size: %s", sink.buffer().size()); | ||||
| 
 | ||||
|                 readCount = mChannel.read(mBuffer); | ||||
| 
 | ||||
|                 Timber.v("Read " + readCount + " bytes from file channel to " + mBuffer.toString()); | ||||
| 
 | ||||
|                 sink.buffer().write(mBuffer.array(), 0, readCount); | ||||
|                 sink.getBuffer().write(mBuffer.array(), 0, readCount); | ||||
| 
 | ||||
|                 sink.flush(); | ||||
| 
 | ||||
|                 Timber.v("Write " + readCount + " bytes to sink buffer with size " + sink.buffer().size()); | ||||
| 
 | ||||
|                 mBuffer.clear(); | ||||
|                 if (mTransferred < maxCount) {  // condition to avoid accumulate progress for repeated chunks | ||||
|                     mTransferred += readCount; | ||||
|  | ||||
| @ -53,6 +53,11 @@ public class FileRequestBody extends RequestBody implements ProgressiveDataTrans | ||||
|         mContentType = contentType; | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public boolean isOneShot() { | ||||
|         return true; | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public MediaType contentType() { | ||||
|         return mContentType; | ||||
|  | ||||
| @ -21,8 +21,9 @@ | ||||
|  *   CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | ||||
|  *   THE SOFTWARE. | ||||
|  */ | ||||
| package com.owncloud.android.lib.resources.response | ||||
| package com.owncloud.android.lib.resources | ||||
| 
 | ||||
| import com.squareup.moshi.Json | ||||
| import com.squareup.moshi.JsonClass | ||||
| 
 | ||||
| // Response retrieved by OCS Rest API, used to obtain capabilities, shares and user info among others. | ||||
| @ -41,6 +42,11 @@ data class OCSResponse<T>( | ||||
| @JsonClass(generateAdapter = true) | ||||
| data class MetaData( | ||||
|     val status: String, | ||||
|     val statuscode: Int, | ||||
|     val message: String? | ||||
|     @Json(name = "statuscode") | ||||
|     val statusCode: Int, | ||||
|     val message: String?, | ||||
|     @Json(name = "itemsperpage") | ||||
|     val itemsPerPage: String?, | ||||
|     @Json(name = "totalitems") | ||||
|     val totalItems: String? | ||||
| ) | ||||
| @ -30,6 +30,8 @@ import java.io.File; | ||||
| 
 | ||||
| public class FileUtils { | ||||
|     public static final String FINAL_CHUNKS_FILE = ".file"; | ||||
|     public static final String MIME_DIR = "DIR"; | ||||
|     public static final String MIME_DIR_UNIX = "httpd/unix-directory"; | ||||
| 
 | ||||
|     static String getParentPath(String remotePath) { | ||||
|         String parentPath = new File(remotePath).getParent(); | ||||
|  | ||||
| @ -29,7 +29,17 @@ import android.os.Parcelable; | ||||
| 
 | ||||
| import at.bitfire.dav4jvm.Property; | ||||
| import at.bitfire.dav4jvm.Response; | ||||
| import at.bitfire.dav4jvm.property.*; | ||||
| import at.bitfire.dav4jvm.property.CreationDate; | ||||
| import at.bitfire.dav4jvm.property.GetContentLength; | ||||
| import at.bitfire.dav4jvm.property.GetContentType; | ||||
| import at.bitfire.dav4jvm.property.GetETag; | ||||
| import at.bitfire.dav4jvm.property.GetLastModified; | ||||
| import at.bitfire.dav4jvm.property.OCId; | ||||
| import at.bitfire.dav4jvm.property.OCPermissions; | ||||
| import at.bitfire.dav4jvm.property.OCPrivatelink; | ||||
| import at.bitfire.dav4jvm.property.OCSize; | ||||
| import at.bitfire.dav4jvm.property.QuotaAvailableBytes; | ||||
| import at.bitfire.dav4jvm.property.QuotaUsedBytes; | ||||
| 
 | ||||
| import java.io.File; | ||||
| import java.io.Serializable; | ||||
| @ -83,7 +93,8 @@ public class RemoteFile implements Parcelable, Serializable { | ||||
|     /** | ||||
|      * Create new {@link RemoteFile} with given path. | ||||
|      * <p> | ||||
|      * The path received must be URL-decoded. Path separator must be File.separator, and it must be the first character in 'path'. | ||||
|      * The path received must be URL-decoded. Path separator must be File.separator, and it must be the first | ||||
|      * character in 'path'. | ||||
|      * | ||||
|      * @param path The remote path of the file. | ||||
|      */ | ||||
| @ -95,7 +106,7 @@ public class RemoteFile implements Parcelable, Serializable { | ||||
|         mRemotePath = path; | ||||
|         mCreationTimestamp = 0; | ||||
|         mLength = 0; | ||||
|         mMimeType = "DIR"; | ||||
|         mMimeType = FileUtils.MIME_DIR; | ||||
|         mQuotaUsedBytes = BigDecimal.ZERO; | ||||
|         mQuotaAvailableBytes = BigDecimal.ZERO; | ||||
|         mPrivateLink = null; | ||||
| @ -154,6 +165,14 @@ public class RemoteFile implements Parcelable, Serializable { | ||||
|         readFromParcel(source); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Use this to find out if this file is a folder. | ||||
|      * | ||||
|      * @return true if it is a folder | ||||
|      */ | ||||
|     public boolean isFolder() { | ||||
|         return mMimeType != null && (mMimeType.equals(FileUtils.MIME_DIR) || mMimeType.equals(FileUtils.MIME_DIR_UNIX)); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Getters and Setters | ||||
|  | ||||
| @ -46,9 +46,9 @@ import java.net.URL; | ||||
| 
 | ||||
| public class GetRemoteShareOperation extends RemoteOperation<ShareParserResult> { | ||||
| 
 | ||||
|     private long mRemoteId; | ||||
|     private String mRemoteId; | ||||
| 
 | ||||
|     public GetRemoteShareOperation(long remoteId) { | ||||
|     public GetRemoteShareOperation(String remoteId) { | ||||
|         mRemoteId = remoteId; | ||||
|     } | ||||
| 
 | ||||
| @ -60,7 +60,7 @@ public class GetRemoteShareOperation extends RemoteOperation<ShareParserResult> | ||||
|             Uri requestUri = client.getBaseUri(); | ||||
|             Uri.Builder uriBuilder = requestUri.buildUpon(); | ||||
|             uriBuilder.appendEncodedPath(ShareUtils.SHARING_API_PATH); | ||||
|             uriBuilder.appendEncodedPath(Long.toString(mRemoteId)); | ||||
|             uriBuilder.appendEncodedPath(mRemoteId); | ||||
| 
 | ||||
|             GetMethod getMethod = new GetMethod(new URL(uriBuilder.build().toString())); | ||||
|             getMethod.addRequestHeader(OCS_API_HEADER, OCS_API_HEADER_VALUE); | ||||
|  | ||||
| @ -1,5 +1,6 @@ | ||||
| /* ownCloud Android Library is available under MIT license | ||||
|  * | ||||
|  *   @author Christian Schabesberger | ||||
|  *   @author masensio | ||||
|  *   @author David A. Velasco | ||||
|  *   @author David González Verdugo | ||||
| @ -28,16 +29,21 @@ | ||||
| 
 | ||||
| package com.owncloud.android.lib.resources.shares | ||||
| 
 | ||||
| import android.net.Uri | ||||
| import com.owncloud.android.lib.common.OwnCloudClient | ||||
| import com.owncloud.android.lib.common.http.HttpConstants | ||||
| import com.owncloud.android.lib.common.http.methods.nonwebdav.GetMethod | ||||
| import com.owncloud.android.lib.common.operations.RemoteOperation | ||||
| import com.owncloud.android.lib.common.operations.RemoteOperationResult | ||||
| import com.owncloud.android.lib.common.operations.RemoteOperationResult.ResultCode.OK | ||||
| import org.json.JSONObject | ||||
| import com.owncloud.android.lib.resources.CommonOcsResponse | ||||
| import com.owncloud.android.lib.resources.shares.responses.ShareeOcsResponse | ||||
| import com.squareup.moshi.JsonAdapter | ||||
| import com.squareup.moshi.Moshi | ||||
| import com.squareup.moshi.Types | ||||
| import timber.log.Timber | ||||
| import java.lang.reflect.Type | ||||
| import java.net.URL | ||||
| import java.util.ArrayList | ||||
| 
 | ||||
| /** | ||||
|  * Created by masensio on 08/10/2015. | ||||
| @ -65,6 +71,7 @@ import java.util.ArrayList | ||||
|  * Status codes: | ||||
|  * 100 - successful | ||||
|  * | ||||
|  * @author Christian Schabesberger | ||||
|  * @author masensio | ||||
|  * @author David A. Velasco | ||||
|  * @author David González Verdugo | ||||
| @ -78,80 +85,66 @@ class GetRemoteShareesOperation | ||||
|  * @param perPage      maximum number of results in a single page | ||||
|  */ | ||||
|     (private val searchString: String, private val page: Int, private val perPage: Int) : | ||||
|     RemoteOperation<ArrayList<JSONObject>>() { | ||||
|     RemoteOperation<ShareeOcsResponse>() { | ||||
| 
 | ||||
|     override fun run(client: OwnCloudClient): RemoteOperationResult<ArrayList<JSONObject>> { | ||||
|         var result: RemoteOperationResult<ArrayList<JSONObject>> | ||||
|     private fun buildRequestUri(baseUri: Uri) = | ||||
|         baseUri.buildUpon() | ||||
|             .appendEncodedPath(OCS_ROUTE) | ||||
|             .appendQueryParameter(PARAM_FORMAT, VALUE_FORMAT) | ||||
|             .appendQueryParameter(PARAM_ITEM_TYPE, VALUE_ITEM_TYPE) | ||||
|             .appendQueryParameter(PARAM_SEARCH, searchString) | ||||
|             .appendQueryParameter(PARAM_PAGE, page.toString()) | ||||
|             .appendQueryParameter(PARAM_PER_PAGE, perPage.toString()) | ||||
|             .build() | ||||
| 
 | ||||
|         try { | ||||
|             val requestUri = client.baseUri | ||||
|             val uriBuilder = requestUri.buildUpon() | ||||
|                 .appendEncodedPath(OCS_ROUTE) | ||||
|                 .appendQueryParameter(PARAM_FORMAT, VALUE_FORMAT) | ||||
|                 .appendQueryParameter(PARAM_ITEM_TYPE, VALUE_ITEM_TYPE) | ||||
|                 .appendQueryParameter(PARAM_SEARCH, searchString) | ||||
|                 .appendQueryParameter(PARAM_PAGE, page.toString()) | ||||
|                 .appendQueryParameter(PARAM_PER_PAGE, perPage.toString()) | ||||
|     private fun parseResponse(response: String): ShareeOcsResponse? { | ||||
|         val moshi = Moshi.Builder().build() | ||||
|         val type: Type = Types.newParameterizedType(CommonOcsResponse::class.java, ShareeOcsResponse::class.java) | ||||
|         val adapter: JsonAdapter<CommonOcsResponse<ShareeOcsResponse>> = moshi.adapter(type) | ||||
|         return adapter.fromJson(response)!!.ocs.data | ||||
|     } | ||||
| 
 | ||||
|             val getMethod = GetMethod(URL(uriBuilder.build().toString())) | ||||
|     private fun onResultUnsuccessful( | ||||
|         method: GetMethod, | ||||
|         response: String?, | ||||
|         status: Int | ||||
|     ): RemoteOperationResult<ShareeOcsResponse> { | ||||
|         Timber.e("Failed response while getting users/groups from the server ") | ||||
|         if (response != null) { | ||||
|             Timber.e("*** status code: $status; response message: $response") | ||||
|         } else { | ||||
|             Timber.e("*** status code: $status") | ||||
|         } | ||||
|         return RemoteOperationResult(method) | ||||
|     } | ||||
| 
 | ||||
|             getMethod.addRequestHeader(OCS_API_HEADER, OCS_API_HEADER_VALUE) | ||||
|     private fun onRequestSuccessful(response: String?): RemoteOperationResult<ShareeOcsResponse> { | ||||
|         val result = RemoteOperationResult<ShareeOcsResponse>(OK) | ||||
|         Timber.d("Successful response: $response") | ||||
|         result.data = parseResponse(response!!) | ||||
|         Timber.d("*** Get Users or groups completed ") | ||||
|         return result | ||||
|     } | ||||
| 
 | ||||
|     override fun run(client: OwnCloudClient): RemoteOperationResult<ShareeOcsResponse> { | ||||
|         val requestUri = buildRequestUri(client.baseUri) | ||||
| 
 | ||||
|         val getMethod = GetMethod(URL(requestUri.toString())) | ||||
|         getMethod.addRequestHeader(OCS_API_HEADER, OCS_API_HEADER_VALUE) | ||||
| 
 | ||||
|         return try { | ||||
|             val status = client.executeHttpMethod(getMethod) | ||||
|             val response = getMethod.getResponseBodyAsString() | ||||
| 
 | ||||
|             if (isSuccess(status)) { | ||||
|                 Timber.d("Successful response: $response") | ||||
| 
 | ||||
|                 // Parse the response | ||||
|                 val respJSON = JSONObject(response) | ||||
|                 val respOCS = respJSON.getJSONObject(NODE_OCS) | ||||
|                 val respData = respOCS.getJSONObject(NODE_DATA) | ||||
|                 val respExact = respData.getJSONObject(NODE_EXACT) | ||||
|                 val respExactUsers = respExact.getJSONArray(NODE_USERS) | ||||
|                 val respExactGroups = respExact.getJSONArray(NODE_GROUPS) | ||||
|                 val respExactRemotes = respExact.getJSONArray(NODE_REMOTES) | ||||
|                 val respPartialUsers = respData.getJSONArray(NODE_USERS) | ||||
|                 val respPartialGroups = respData.getJSONArray(NODE_GROUPS) | ||||
|                 val respPartialRemotes = respData.getJSONArray(NODE_REMOTES) | ||||
|                 val jsonResults = arrayOf( | ||||
|                     respExactUsers, | ||||
|                     respExactGroups, | ||||
|                     respExactRemotes, | ||||
|                     respPartialUsers, | ||||
|                     respPartialGroups, | ||||
|                     respPartialRemotes | ||||
|                 ) | ||||
| 
 | ||||
|                 val data = ArrayList<JSONObject>() // For result data | ||||
|                 for (i in 0..5) { | ||||
|                     for (j in 0 until jsonResults[i].length()) { | ||||
|                         val jsonResult = jsonResults[i].getJSONObject(j) | ||||
|                         data.add(jsonResult) | ||||
|                         Timber.d("*** Added item: ${jsonResult.getString(PROPERTY_LABEL)}") | ||||
|                     } | ||||
|                 } | ||||
| 
 | ||||
|                 result = RemoteOperationResult(OK) | ||||
|                 result.data = data | ||||
| 
 | ||||
|                 Timber.d("*** Get Users or groups completed ") | ||||
| 
 | ||||
|             if (!isSuccess(status)) { | ||||
|                 onResultUnsuccessful(getMethod, response, status) | ||||
|             } else { | ||||
|                 result = RemoteOperationResult(getMethod) | ||||
|                 Timber.e("Failed response while getting users/groups from the server ") | ||||
|                 if (response != null) { | ||||
|                     Timber.e("*** status code: $status; response message: $response") | ||||
|                 } else { | ||||
|                     Timber.e("*** status code: $status") | ||||
|                 } | ||||
|                 onRequestSuccessful(response) | ||||
|             } | ||||
|         } catch (e: Exception) { | ||||
|             result = RemoteOperationResult(e) | ||||
|             Timber.e(e, "Exception while getting users/groups") | ||||
|             RemoteOperationResult(e) | ||||
|         } | ||||
| 
 | ||||
|         return result | ||||
|     } | ||||
| 
 | ||||
|     private fun isSuccess(status: Int) = status == HttpConstants.HTTP_OK | ||||
| @ -171,18 +164,5 @@ class GetRemoteShareesOperation | ||||
|         // Arguments - constant values | ||||
|         private const val VALUE_FORMAT = "json" | ||||
|         private const val VALUE_ITEM_TYPE = "file"         //  to get the server search for users / groups | ||||
| 
 | ||||
|         // JSON Node names | ||||
|         private const val NODE_OCS = "ocs" | ||||
|         private const val NODE_DATA = "data" | ||||
|         private const val NODE_EXACT = "exact" | ||||
|         private const val NODE_USERS = "users" | ||||
|         private const val NODE_GROUPS = "groups" | ||||
|         private const val NODE_REMOTES = "remotes" | ||||
|         const val NODE_VALUE = "value" | ||||
|         const val PROPERTY_LABEL = "label" | ||||
|         const val PROPERTY_SHARE_TYPE = "shareType" | ||||
|         const val PROPERTY_SHARE_WITH = "shareWith" | ||||
|         const val PROPERTY_SHARE_WITH_ADDITIONAL_INFO = "shareWithAdditionalInfo" | ||||
|     } | ||||
| } | ||||
|  | ||||
| @ -24,7 +24,6 @@ | ||||
| 
 | ||||
| package com.owncloud.android.lib.resources.shares | ||||
| 
 | ||||
| import com.owncloud.android.lib.resources.files.FileUtils | ||||
| import java.io.File | ||||
| 
 | ||||
| /** | ||||
| @ -35,7 +34,7 @@ import java.io.File | ||||
|  * @author David González Verdugo | ||||
|  */ | ||||
| data class RemoteShare( | ||||
|     var id: Long = 0, | ||||
|     var id: String = "0", | ||||
|     var shareWith: String = "", | ||||
|     var path: String = "", | ||||
|     var token: String = "", | ||||
| @ -43,15 +42,11 @@ data class RemoteShare( | ||||
|     var sharedWithAdditionalInfo: String = "", | ||||
|     var name: String = "", | ||||
|     var shareLink: String = "", | ||||
|     var fileSource: String = "0", | ||||
|     var itemSource: String = "0", | ||||
|     var shareType: ShareType? = ShareType.UNKNOWN, | ||||
|     var permissions: Int = DEFAULT_PERMISSION, | ||||
|     var sharedDate: Long = INIT_SHARED_DATE, | ||||
|     var expirationDate: Long = INIT_EXPIRATION_DATE_IN_MILLIS, | ||||
|     var isFolder: Boolean = path.endsWith(File.separator), | ||||
|     var userId: Long = 0, | ||||
|     val isValid: Boolean = id > -1 | ||||
|     var isFolder: Boolean = path.endsWith(File.separator) | ||||
| ) { | ||||
| 
 | ||||
|     companion object { | ||||
| @ -106,17 +101,6 @@ enum class ShareType constructor(val value: Int) { | ||||
|     FEDERATED(6); | ||||
| 
 | ||||
|     companion object { | ||||
|         fun fromValue(value: Int): ShareType? { | ||||
|             return when (value) { | ||||
|                 -1 -> UNKNOWN | ||||
|                 0 -> USER | ||||
|                 1 -> GROUP | ||||
|                 3 -> PUBLIC_LINK | ||||
|                 4 -> EMAIL | ||||
|                 5 -> CONTACT | ||||
|                 6 -> FEDERATED | ||||
|                 else -> null | ||||
|             } | ||||
|         } | ||||
|         fun fromValue(value: Int) = values().firstOrNull { it.value == value } | ||||
|     } | ||||
| } | ||||
|  | ||||
| @ -48,7 +48,7 @@ import java.net.URL | ||||
|  * | ||||
|  * @param remoteShareId Share ID | ||||
|  */ | ||||
| class RemoveRemoteShareOperation(private val remoteShareId: Long) : RemoteOperation<ShareParserResult>() { | ||||
| class RemoveRemoteShareOperation(private val remoteShareId: String) : RemoteOperation<ShareParserResult>() { | ||||
| 
 | ||||
|     override fun run(client: OwnCloudClient): RemoteOperationResult<ShareParserResult> { | ||||
|         var result: RemoteOperationResult<ShareParserResult> | ||||
| @ -57,7 +57,7 @@ class RemoveRemoteShareOperation(private val remoteShareId: Long) : RemoteOperat | ||||
|             val requestUri = client.baseUri | ||||
|             val uriBuilder = requestUri.buildUpon() | ||||
|             uriBuilder.appendEncodedPath(ShareUtils.SHARING_API_PATH) | ||||
|             uriBuilder.appendEncodedPath(remoteShareId.toString()) | ||||
|             uriBuilder.appendEncodedPath(remoteShareId) | ||||
| 
 | ||||
|             val deleteMethod = DeleteMethod( | ||||
|                 URL(uriBuilder.build().toString()) | ||||
|  | ||||
| @ -179,7 +179,7 @@ class ShareXMLParser { | ||||
|                 name.equals(NODE_ID, ignoreCase = true) -> {// Parse Create XML Response | ||||
|                     share = RemoteShare() | ||||
|                     val value = readNode(parser, NODE_ID) | ||||
|                     share.id = Integer.parseInt(value).toLong() | ||||
|                     share.id = value | ||||
|                 } | ||||
|                 name.equals(NODE_URL, ignoreCase = true) -> { | ||||
|                     // NOTE: this field is received in all the public shares from OC 9.0.0 | ||||
| @ -236,7 +236,7 @@ class ShareXMLParser { | ||||
|                 } | ||||
| 
 | ||||
|                 name.equals(NODE_ID, ignoreCase = true) -> { | ||||
|                     remoteShare.id = Integer.parseInt(readNode(parser, NODE_ID)).toLong() | ||||
|                     remoteShare.id = readNode(parser, NODE_ID) | ||||
|                 } | ||||
| 
 | ||||
|                 name.equals(NODE_ITEM_TYPE, ignoreCase = true) -> { | ||||
| @ -244,10 +244,6 @@ class ShareXMLParser { | ||||
|                     fixPathForFolder(remoteShare) | ||||
|                 } | ||||
| 
 | ||||
|                 name.equals(NODE_ITEM_SOURCE, ignoreCase = true) -> { | ||||
|                     remoteShare.itemSource = readNode(parser, NODE_ITEM_SOURCE) | ||||
|                 } | ||||
| 
 | ||||
|                 name.equals(NODE_PARENT, ignoreCase = true) -> { | ||||
|                     readNode(parser, NODE_PARENT) | ||||
|                 } | ||||
| @ -261,10 +257,6 @@ class ShareXMLParser { | ||||
|                     remoteShare.shareWith = readNode(parser, NODE_SHARE_WITH) | ||||
|                 } | ||||
| 
 | ||||
|                 name.equals(NODE_FILE_SOURCE, ignoreCase = true) -> { | ||||
|                     remoteShare.fileSource = readNode(parser, NODE_FILE_SOURCE) | ||||
|                 } | ||||
| 
 | ||||
|                 name.equals(NODE_PATH, ignoreCase = true) -> { | ||||
|                     remoteShare.path = readNode(parser, NODE_PATH) | ||||
|                     fixPathForFolder(remoteShare) | ||||
| @ -320,9 +312,7 @@ class ShareXMLParser { | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         if (remoteShare.isValid) { | ||||
|             shares.add(remoteShare) | ||||
|         } | ||||
|         shares.add(remoteShare) | ||||
|     } | ||||
| 
 | ||||
|     private fun fixPathForFolder(share: RemoteShare) { | ||||
| @ -403,11 +393,9 @@ class ShareXMLParser { | ||||
|         private const val NODE_ELEMENT = "element" | ||||
|         private const val NODE_ID = "id" | ||||
|         private const val NODE_ITEM_TYPE = "item_type" | ||||
|         private const val NODE_ITEM_SOURCE = "item_source" | ||||
|         private const val NODE_PARENT = "parent" | ||||
|         private const val NODE_SHARE_TYPE = "share_type" | ||||
|         private const val NODE_SHARE_WITH = "share_with" | ||||
|         private const val NODE_FILE_SOURCE = "file_source" | ||||
|         private const val NODE_PATH = "path" | ||||
|         private const val NODE_PERMISSIONS = "permissions" | ||||
|         private const val NODE_STIME = "stime" | ||||
|  | ||||
| @ -55,7 +55,7 @@ class UpdateRemoteShareOperation | ||||
|     /** | ||||
|      * @param remoteId Identifier of the share to update. | ||||
|      */ | ||||
|     private val remoteId: Long | ||||
|     private val remoteId: String | ||||
| 
 | ||||
| ) : RemoteOperation<ShareParserResult>() { | ||||
|     /** | ||||
|  | ||||
| @ -0,0 +1,72 @@ | ||||
| /* ownCloud Android Library is available under MIT license | ||||
|  * | ||||
|  *   Copyright (C) 2020 ownCloud GmbH. | ||||
|  * | ||||
|  *   Permission is hereby granted, free of charge, to any person obtaining a copy | ||||
|  *   of this software and associated documentation files (the "Software"), to deal | ||||
|  *   in the Software without restriction, including without limitation the rights | ||||
|  *   to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | ||||
|  *   copies of the Software, and to permit persons to whom the Software is | ||||
|  *   furnished to do so, subject to the following conditions: | ||||
|  * | ||||
|  *   The above copyright notice and this permission notice shall be included in | ||||
|  *   all copies or substantial portions of the Software. | ||||
|  * | ||||
|  *   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, | ||||
|  *   EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF | ||||
|  *   MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND | ||||
|  *   NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS | ||||
|  *   BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN | ||||
|  *   ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN | ||||
|  *   CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | ||||
|  *   THE SOFTWARE. | ||||
|  */ | ||||
| package com.owncloud.android.lib.resources.shares.responses | ||||
| 
 | ||||
| import com.squareup.moshi.Json | ||||
| import com.squareup.moshi.JsonClass | ||||
| 
 | ||||
| /** | ||||
|  * This was modeled according to the documentation: | ||||
|  * https://doc.owncloud.com/server/developer_manual/core/apis/ocs-recipient-api.html#get-shares-recipients | ||||
|  */ | ||||
| @JsonClass(generateAdapter = true) | ||||
| data class ShareeOcsResponse( | ||||
|     val exact: ExactSharees?, | ||||
|     val groups: List<ShareeItem>, | ||||
|     val remotes: List<ShareeItem>, | ||||
|     val users: List<ShareeItem> | ||||
| ) { | ||||
|     fun getFlatRepresentationWithoutExact() = ArrayList<ShareeItem>().apply { | ||||
|         addAll(users) | ||||
|         addAll(remotes) | ||||
|         addAll(groups) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| @JsonClass(generateAdapter = true) | ||||
| data class ExactSharees( | ||||
|     val groups: List<ShareeItem>, | ||||
|     val remotes: List<ShareeItem>, | ||||
|     val users: List<ShareeItem> | ||||
| ) { | ||||
|     fun getFlatRepresentation() = ArrayList<ShareeItem>().apply { | ||||
|         addAll(users) | ||||
|         addAll(remotes) | ||||
|         addAll(groups) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| @JsonClass(generateAdapter = true) | ||||
| data class ShareeItem( | ||||
|     val label: String, | ||||
|     val value: ShareeValue | ||||
| ) | ||||
| 
 | ||||
| @JsonClass(generateAdapter = true) | ||||
| data class ShareeValue( | ||||
|     val shareType: Int, | ||||
|     val shareWith: String, | ||||
|     @Json(name = "shareWithAdditionalInfo") | ||||
|     val additionalInfo: String? | ||||
| ) | ||||
| @ -44,7 +44,7 @@ interface ShareService : Service { | ||||
|     ): RemoteOperationResult<ShareParserResult> | ||||
| 
 | ||||
|     fun updateShare( | ||||
|         remoteId: Long, | ||||
|         remoteId: String, | ||||
|         name: String, | ||||
|         password: String?, | ||||
|         expirationDate: Long, | ||||
| @ -52,5 +52,5 @@ interface ShareService : Service { | ||||
|         publicUpload: Boolean | ||||
|     ): RemoteOperationResult<ShareParserResult> | ||||
| 
 | ||||
|     fun deleteShare(remoteId: Long): RemoteOperationResult<ShareParserResult> | ||||
|     fun deleteShare(remoteId: String): RemoteOperationResult<ShareParserResult> | ||||
| } | ||||
|  | ||||
| @ -1,6 +1,7 @@ | ||||
| /** | ||||
|  * ownCloud Android client application | ||||
|  * | ||||
|  * @author Christian Schabesberger | ||||
|  * @author David González Verdugo | ||||
|  * | ||||
|  * Copyright (C) 2020 ownCloud GmbH. | ||||
| @ -22,13 +23,12 @@ package com.owncloud.android.lib.resources.shares.services | ||||
| 
 | ||||
| import com.owncloud.android.lib.common.operations.RemoteOperationResult | ||||
| import com.owncloud.android.lib.resources.Service | ||||
| import org.json.JSONObject | ||||
| import java.util.ArrayList | ||||
| import com.owncloud.android.lib.resources.shares.responses.ShareeOcsResponse | ||||
| 
 | ||||
| interface ShareeService : Service { | ||||
|     fun getSharees( | ||||
|         searchString: String, | ||||
|         page: Int, | ||||
|         perPage: Int | ||||
|     ): RemoteOperationResult<ArrayList<JSONObject>> | ||||
|     ): RemoteOperationResult<ShareeOcsResponse> | ||||
| } | ||||
|  | ||||
| @ -66,7 +66,7 @@ class OCShareService(override val client: OwnCloudClient) : | ||||
|         }.execute(client) | ||||
| 
 | ||||
|     override fun updateShare( | ||||
|         remoteId: Long, | ||||
|         remoteId: String, | ||||
|         name: String, | ||||
|         password: String?, | ||||
|         expirationDate: Long, | ||||
| @ -84,7 +84,7 @@ class OCShareService(override val client: OwnCloudClient) : | ||||
|             this.retrieveShareDetails = true | ||||
|         }.execute(client) | ||||
| 
 | ||||
|     override fun deleteShare(remoteId: Long): RemoteOperationResult<ShareParserResult> = | ||||
|     override fun deleteShare(remoteId: String): RemoteOperationResult<ShareParserResult> = | ||||
|         RemoveRemoteShareOperation( | ||||
|             remoteId | ||||
|         ).execute(client) | ||||
|  | ||||
| @ -23,9 +23,8 @@ package com.owncloud.android.lib.resources.shares.services.implementation | ||||
| import com.owncloud.android.lib.common.OwnCloudClient | ||||
| import com.owncloud.android.lib.common.operations.RemoteOperationResult | ||||
| import com.owncloud.android.lib.resources.shares.GetRemoteShareesOperation | ||||
| import com.owncloud.android.lib.resources.shares.responses.ShareeOcsResponse | ||||
| import com.owncloud.android.lib.resources.shares.services.ShareeService | ||||
| import org.json.JSONObject | ||||
| import java.util.ArrayList | ||||
| 
 | ||||
| class OCShareeService(override val client: OwnCloudClient) : | ||||
|     ShareeService { | ||||
| @ -33,7 +32,7 @@ class OCShareeService(override val client: OwnCloudClient) : | ||||
|         searchString: String, | ||||
|         page: Int, | ||||
|         perPage: Int | ||||
|     ): RemoteOperationResult<ArrayList<JSONObject>> = | ||||
|     ): RemoteOperationResult<ShareeOcsResponse> = | ||||
|         GetRemoteShareesOperation( | ||||
|             searchString, | ||||
|             page, | ||||
|  | ||||
| @ -34,8 +34,8 @@ import com.owncloud.android.lib.common.http.methods.nonwebdav.GetMethod | ||||
| import com.owncloud.android.lib.common.operations.RemoteOperation | ||||
| import com.owncloud.android.lib.common.operations.RemoteOperationResult | ||||
| import com.owncloud.android.lib.common.operations.RemoteOperationResult.ResultCode.OK | ||||
| import com.owncloud.android.lib.resources.response.CapabilityResponse | ||||
| import com.owncloud.android.lib.resources.response.CommonOcsResponse | ||||
| import com.owncloud.android.lib.resources.status.responses.CapabilityResponse | ||||
| import com.owncloud.android.lib.resources.CommonOcsResponse | ||||
| import com.squareup.moshi.JsonAdapter | ||||
| import com.squareup.moshi.Moshi | ||||
| import com.squareup.moshi.Types | ||||
|  | ||||
| @ -22,7 +22,7 @@ | ||||
|  *   THE SOFTWARE. | ||||
|  * | ||||
|  */ | ||||
| package com.owncloud.android.lib.resources.response | ||||
| package com.owncloud.android.lib.resources.status.responses | ||||
| 
 | ||||
| import com.owncloud.android.lib.resources.status.RemoteCapability | ||||
| import com.owncloud.android.lib.resources.status.RemoteCapability.CapabilityBooleanType | ||||
| @ -29,8 +29,8 @@ import com.owncloud.android.lib.common.http.methods.nonwebdav.GetMethod | ||||
| import com.owncloud.android.lib.common.operations.RemoteOperation | ||||
| import com.owncloud.android.lib.common.operations.RemoteOperationResult | ||||
| import com.owncloud.android.lib.common.operations.RemoteOperationResult.ResultCode | ||||
| import com.owncloud.android.lib.resources.response.CommonOcsResponse | ||||
| import com.owncloud.android.lib.resources.response.UserInfoResponse | ||||
| import com.owncloud.android.lib.resources.CommonOcsResponse | ||||
| import com.owncloud.android.lib.resources.users.responses.UserInfoResponse | ||||
| import com.squareup.moshi.JsonAdapter | ||||
| import com.squareup.moshi.Moshi | ||||
| import com.squareup.moshi.Types | ||||
|  | ||||
| @ -21,7 +21,7 @@ | ||||
|  *   CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | ||||
|  *   THE SOFTWARE. | ||||
|  */ | ||||
| package com.owncloud.android.lib.resources.response | ||||
| package com.owncloud.android.lib.resources.users.responses | ||||
| 
 | ||||
| import com.owncloud.android.lib.resources.users.RemoteUserInfo | ||||
| import com.squareup.moshi.Json | ||||
| @ -0,0 +1,93 @@ | ||||
| /* ownCloud Android Library is available under MIT license | ||||
|  *   Copyright (C) 2020 ownCloud GmbH. | ||||
|  * | ||||
|  *   Permission is hereby granted, free of charge, to any person obtaining a copy | ||||
|  *   of this software and associated documentation files (the "Software"), to deal | ||||
|  *   in the Software without restriction, including without limitation the rights | ||||
|  *   to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | ||||
|  *   copies of the Software, and to permit persons to whom the Software is | ||||
|  *   furnished to do so, subject to the following conditions: | ||||
|  * | ||||
|  *   The above copyright notice and this permission notice shall be included in | ||||
|  *   all copies or substantial portions of the Software. | ||||
|  * | ||||
|  *   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, | ||||
|  *   EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF | ||||
|  *   MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND | ||||
|  *   NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS | ||||
|  *   BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN | ||||
|  *   ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN | ||||
|  *   CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | ||||
|  *   THE SOFTWARE. | ||||
|  * | ||||
|  */ | ||||
| 
 | ||||
| package com.owncloud.android.lib.resources.shares.responses | ||||
| 
 | ||||
| import com.owncloud.android.lib.resources.CommonOcsResponse | ||||
| import com.squareup.moshi.JsonAdapter | ||||
| import com.squareup.moshi.Moshi | ||||
| import com.squareup.moshi.Types | ||||
| import org.junit.Assert.assertNull | ||||
| import org.junit.Assert.assertEquals | ||||
| import org.junit.Assert.assertTrue | ||||
| import org.junit.Before | ||||
| import org.junit.Test | ||||
| import java.io.File | ||||
| import java.lang.reflect.Type | ||||
| 
 | ||||
| class ShareeResponseTest { | ||||
| 
 | ||||
|     lateinit var adapter: JsonAdapter<CommonOcsResponse<ShareeOcsResponse>> | ||||
| 
 | ||||
|     private fun loadResponses(fileName: String) = | ||||
|         adapter.fromJson(File(fileName).readText()) | ||||
| 
 | ||||
|     @Before | ||||
|     fun prepare() { | ||||
|         val moshi = Moshi.Builder().build() | ||||
|         val type: Type = Types.newParameterizedType(CommonOcsResponse::class.java, ShareeOcsResponse::class.java) | ||||
|         adapter = moshi.adapter(type) | ||||
|     } | ||||
| 
 | ||||
|     @Test | ||||
|     fun `check structure - ok - contains meta`() { | ||||
|         val response = loadResponses(EXAMPLE_RESPONSE_JSON)!! | ||||
|         assertEquals("OK", response.ocs.meta.message!!) | ||||
|         assertEquals(200, response.ocs.meta.statusCode) | ||||
|         assertEquals("ok", response.ocs.meta.status) | ||||
|         assertTrue(response.ocs.meta.itemsPerPage?.isEmpty()!!) | ||||
|         assertTrue(response.ocs.meta.totalItems?.isEmpty()!!) | ||||
|     } | ||||
| 
 | ||||
|     @Test | ||||
|     fun `example response - ok - correct sturcture`() { | ||||
|         val response = loadResponses(EXAMPLE_RESPONSE_JSON)!! | ||||
|         assertEquals(2, response.ocs.data.groups.size) | ||||
|         assertEquals(0, response.ocs.data.remotes.size) | ||||
|         assertEquals(2, response.ocs.data.users.size) | ||||
|         assertEquals(0, response.ocs.data.exact?.groups?.size) | ||||
|         assertEquals(0, response.ocs.data.exact?.remotes?.size) | ||||
|         assertEquals(1, response.ocs.data.exact?.users?.size) | ||||
|         assertEquals("user1@user1.com", response.ocs.data.users.get(0).value.additionalInfo) | ||||
|         assertNull(response.ocs.data.users[1].value.additionalInfo) | ||||
|     } | ||||
| 
 | ||||
|     @Test | ||||
|     fun `check empty response - ok - parsing ok`() { | ||||
|         val response = loadResponses(EMPTY_RESPONSE_JSON)!! | ||||
|         assertTrue(response.ocs.data.exact?.groups?.isEmpty()!!) | ||||
|         assertTrue(response.ocs.data.exact?.remotes?.isEmpty()!!) | ||||
|         assertTrue(response.ocs.data.exact?.users?.isEmpty()!!) | ||||
|         assertTrue(response.ocs.data.groups.isEmpty()) | ||||
|         assertTrue(response.ocs.data.remotes.isEmpty()) | ||||
|         assertTrue(response.ocs.data.users.isEmpty()) | ||||
|     } | ||||
| 
 | ||||
|     companion object { | ||||
|         val RESOURCES_PATH = | ||||
|             "src/test/responses/com.owncloud.android.lib.resources.sharees.responses" | ||||
|         val EXAMPLE_RESPONSE_JSON = "$RESOURCES_PATH/example_sharee_response.json" | ||||
|         val EMPTY_RESPONSE_JSON = "$RESOURCES_PATH/empty_sharee_response.json" | ||||
|     } | ||||
| } | ||||
| @ -0,0 +1,21 @@ | ||||
| { | ||||
|   "ocs": { | ||||
|     "meta": { | ||||
|       "status": "ok", | ||||
|       "statuscode": 100, | ||||
|       "message": "OK", | ||||
|       "totalitems": "", | ||||
|       "itemsperpage": "" | ||||
|     }, | ||||
|     "data": { | ||||
|       "exact": { | ||||
|         "users": [], | ||||
|         "groups": [], | ||||
|         "remotes": [] | ||||
|       }, | ||||
|       "users": [], | ||||
|       "groups": [], | ||||
|       "remotes": [] | ||||
|     } | ||||
|   } | ||||
| } | ||||
| @ -0,0 +1,60 @@ | ||||
| { | ||||
|   "ocs": { | ||||
|     "data": { | ||||
|       "exact": { | ||||
|         "groups": [], | ||||
|         "remotes": [], | ||||
|         "users": [ | ||||
|           { | ||||
|             "label": "admin", | ||||
|             "value": { | ||||
|               "shareType": 0, | ||||
|               "shareWith": "admin" | ||||
|             } | ||||
|           } | ||||
|         ] | ||||
|       }, | ||||
|       "groups": [ | ||||
|         { | ||||
|           "label": "group1", | ||||
|           "value": { | ||||
|             "shareType": 1, | ||||
|             "shareWith": "group1" | ||||
|           } | ||||
|         }, | ||||
|         { | ||||
|           "label": "group2", | ||||
|           "value": { | ||||
|             "shareType": 1, | ||||
|             "shareWith": "group2" | ||||
|           } | ||||
|         } | ||||
|       ], | ||||
|       "remotes": [], | ||||
|       "users": [ | ||||
|         { | ||||
|           "label": "user1", | ||||
|           "value": { | ||||
|             "shareType": 0, | ||||
|             "shareWith": "user1", | ||||
|             "shareWithAdditionalInfo": "user1@user1.com" | ||||
|           } | ||||
|         }, | ||||
|         { | ||||
|           "label": "user2", | ||||
|           "value": { | ||||
|             "shareType": 0, | ||||
|             "shareWith": "user2" | ||||
|           } | ||||
|         } | ||||
|       ] | ||||
|     }, | ||||
|     "meta": { | ||||
|       "itemsperpage": "", | ||||
|       "message": "OK", | ||||
|       "status": "ok", | ||||
|       "statuscode": 200, | ||||
|       "totalitems": "" | ||||
|     } | ||||
|   } | ||||
| } | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user