diff --git a/app/build.gradle b/app/build.gradle index 47b7899db..10ff43979 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -13,8 +13,8 @@ android { targetSdkVersion rootProject.ext.targetSdkVersion compileSdk rootProject.ext.compileSdkVersion - versionCode 150 - versionName "3.0.1-beta" + versionCode 152 + versionName "3.0.2-pre" multiDexEnabled true resValue "string", "authorities", defaultConfig.applicationId + '.debug.cameraupload.provider' @@ -185,14 +185,18 @@ android { implementation 'com.blankj:utilcode:1.30.7' //https://github.com/CymChad/BaseRecyclerViewAdapterHelper implementation "io.github.cymchad:BaseRecyclerViewAdapterHelper4:4.1.2" + //https://github.com/panpf/stickyitemdecoration + implementation "io.github.panpf.stickyitemdecoration:stickyitemdecoration:1.0.2" + //https://github.com/timusus/RecyclerView-FastScroll + implementation "com.simplecityapps:recyclerview-fastscroll:2.0.1" // Firebase - implementation platform('com.google.firebase:firebase-bom:32.8.1') + implementation platform('com.google.firebase:firebase-bom:33.1.1') implementation 'com.google.firebase:firebase-crashlytics' implementation 'com.google.firebase:firebase-analytics' //media3 - final def media3_version = '1.2.1' + final def media3_version = '1.3.1' implementation "androidx.media3:media3-exoplayer:$media3_version" implementation "androidx.media3:media3-ui:$media3_version" @@ -206,7 +210,8 @@ android { // implementation "androidx.datastore:datastore-preferences-rxjava2:1.0.0" implementation "androidx.core:core-splashscreen:1.0.1" - implementation "com.google.android.material:material:1.11.0" + //https://github.com/material-components/material-components-android + implementation "com.google.android.material:material:1.12.0" def lifecycle_version = "2.7.0" implementation "androidx.lifecycle:lifecycle-livedata:$lifecycle_version" @@ -237,11 +242,11 @@ android { annotationProcessor "androidx.room:room-compiler:$room_version" //https://github.com/google/guava - implementation "com.google.guava:guava:32.1.2-android" + implementation "com.google.guava:guava:33.0.0-android" //gson implementation 'com.google.code.gson:gson:2.10.1' //https://github.com/apache/commons-io - implementation 'commons-io:commons-io:2.13.0' + implementation 'commons-io:commons-io:2.16.1' //https://github.com/apache/commons-lang implementation 'com.github.apache:commons-lang:rel~commons-lang-3.12.0' @@ -252,6 +257,9 @@ android { //https://github.com/elvishew/xLog implementation 'com.elvishew:xlog:1.10.1' + //https://github.com/zhanghai/AndroidFastScroll + implementation 'me.zhanghai.android.fastscroll:library:1.3.0' + // //https://github.com/wasabeef/recyclerview-animators // implementation 'jp.wasabeef:recyclerview-animators:4.0.2' diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 81ed6c96d..2476ab46e 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -33,9 +33,6 @@ - - - @@ -94,10 +91,6 @@ - - { // The full URL of the server, like 'http://gonggeng.org/seahub/' or 'http://gonggeng.org/' public String server; @@ -48,8 +52,14 @@ public void setTotalSpace(long total) { this.total = total; } - public boolean isQuotaNoLimit() { - return total < 0; + /** + * in fact, the value should be less than 0. + * however, in some cases, it may be 0, and should also return unlimited. + * even if the non-limit is returned, App does not need to verify "Out of quota" status. + * and the "Out of quota" error will be returned in the file upload result. + */ + public boolean isQuotaUnlimited() { + return total <= 0; } public void setLoginTimestamp(long timestamp) { @@ -114,12 +124,6 @@ public String getServerDomainName() { return dn; } - /** - * https://dev.xxx.com/dev/ => https://dev.xxx.com - */ - public String getProtocolHost() { - return URLs.getProtocolHost(server); - } public String getEmail() { return email; @@ -133,10 +137,21 @@ public String getName() { return name; } + public String getServer() { return server; } + /** + * https://dev.xxx.com/dev/ => https://dev.xxx.com + */ + public String getProtocolHost() { + return URLs.getProtocolHost(server); + } + + /** + * https://dev.xxx.com/dev/ => dev.xxx.com/dev + */ public String getServerNoProtocol() { String result = server.substring(server.indexOf("://") + 3); if (result.endsWith("/")) @@ -149,7 +164,7 @@ public String getToken() { } public boolean isHttps() { - return server.startsWith("https"); + return server.toLowerCase(Locale.getDefault()).startsWith("https"); } public boolean isShib() { @@ -165,6 +180,9 @@ public void setSessionKey(String sessionKey) { } + /** + * NOTICE: Do not modify the splicing format of this string + */ public String getSignature() { return String.format("%s (%s)", getServerNoProtocol(), email); } @@ -189,16 +207,22 @@ public int hashCode() { @Override public boolean equals(Object obj) { - if (this == obj) + if (this == obj) { return true; - if (obj == null || (obj.getClass() != this.getClass())) + } + + if (obj == null || (obj.getClass() != this.getClass())) { return false; + } - Account a = (Account) obj; - if (a.server == null || a.email == null || a.token == null) + Account newAccount = (Account) obj; + if (newAccount.server == null || newAccount.email == null) { return false; + } - return a.server.equals(this.server) && a.email.equals(this.email); + return Objects.equal(newAccount.server, this.server) + && Objects.equal(newAccount.email, this.email) + && Objects.equal(newAccount.token, this.token); } diff --git a/app/src/main/java/com/seafile/seadroid2/account/Authenticator.java b/app/src/main/java/com/seafile/seadroid2/account/Authenticator.java index b885e69e1..05706fa8c 100644 --- a/app/src/main/java/com/seafile/seadroid2/account/Authenticator.java +++ b/app/src/main/java/com/seafile/seadroid2/account/Authenticator.java @@ -10,7 +10,7 @@ import android.text.TextUtils; import android.util.Log; -import com.seafile.seadroid2.framework.http.IO; +import com.seafile.seadroid2.framework.http.HttpIO; import com.seafile.seadroid2.framework.util.SLogs; import com.seafile.seadroid2.ui.account.AccountService; import com.seafile.seadroid2.ui.account.SeafileAuthenticatorActivity; @@ -129,7 +129,7 @@ public Bundle confirmCredentials( try { Account a = SupportAccountManager.getInstance().getSeafileAccount(account); - Call call = IO.getInstanceByAccount(a).execute(AccountService.class).getAccountInfoCall(); + Call call = HttpIO.getInstanceByAccount(a).execute(AccountService.class).getAccountInfoCall(); Response res = call.execute(); if (res.isSuccessful()) { diff --git a/app/src/main/java/com/seafile/seadroid2/config/GlideLoadConfig.java b/app/src/main/java/com/seafile/seadroid2/config/GlideLoadConfig.java index 6b2db3d13..4056c3008 100644 --- a/app/src/main/java/com/seafile/seadroid2/config/GlideLoadConfig.java +++ b/app/src/main/java/com/seafile/seadroid2/config/GlideLoadConfig.java @@ -1,35 +1,31 @@ package com.seafile.seadroid2.config; -import com.bumptech.glide.load.model.GlideUrl; -import com.bumptech.glide.load.model.LazyHeaders; import com.bumptech.glide.request.RequestOptions; import com.bumptech.glide.signature.ObjectKey; import com.seafile.seadroid2.R; -import com.seafile.seadroid2.account.Account; -import com.seafile.seadroid2.account.SupportAccountManager; import com.seafile.seadroid2.ui.WidgetUtils; public class GlideLoadConfig { - public static GlideUrl getGlideUrl(String url) { - - Account account = SupportAccountManager.getInstance().getCurrentAccount(); - if (account == null) { - return new GlideUrl(url, new LazyHeaders.Builder().build()); - } - - String token = account.token; - - return new GlideUrl(url, new LazyHeaders.Builder() - .addHeader("Authorization", "Token " + token) - .build()); - } - - public static GlideUrl getGlideUrl(String url, String token) { - return new GlideUrl(url, new LazyHeaders.Builder() - .addHeader("Authorization", "Token " + token) - .build()); - } +// public static GlideUrl getGlideUrl(String url) { +// +// Account account = SupportAccountManager.getInstance().getCurrentAccount(); +// if (account == null) { +// return new GlideUrl(url, new LazyHeaders.Builder().build()); +// } +// +// String token = account.token; +// +// return new GlideUrl(url, new LazyHeaders.Builder() +// .addHeader("Authorization", "Token " + token) +// .build()); +// } +// +// public static GlideUrl getGlideUrl(String url, String token) { +// return new GlideUrl(url, new LazyHeaders.Builder() +// .addHeader("Authorization", "Token " + token) +// .build()); +// } public static RequestOptions getAvatarOptions() { return new RequestOptions() @@ -51,10 +47,4 @@ public static RequestOptions getOptions(String key) { .signature(new ObjectKey(key)) .override(WidgetUtils.getThumbnailWidth(), WidgetUtils.getThumbnailWidth()); } - - public static RequestOptions getDefaultAvatarOptions() { - return new RequestOptions() - .placeholder(R.drawable.default_avatar) - .override(WidgetUtils.getThumbnailWidth(), WidgetUtils.getThumbnailWidth()); - } } diff --git a/app/src/main/java/com/seafile/seadroid2/framework/data/db/dao/CertCacheDAO.java b/app/src/main/java/com/seafile/seadroid2/framework/data/db/dao/CertCacheDAO.java index 3adeb765a..402a47878 100644 --- a/app/src/main/java/com/seafile/seadroid2/framework/data/db/dao/CertCacheDAO.java +++ b/app/src/main/java/com/seafile/seadroid2/framework/data/db/dao/CertCacheDAO.java @@ -3,6 +3,7 @@ import androidx.room.Dao; import androidx.room.Insert; import androidx.room.OnConflictStrategy; +import androidx.room.Query; import com.seafile.seadroid2.framework.data.db.entities.CertEntity; @@ -21,6 +22,12 @@ public interface CertCacheDAO { @Insert(onConflict = OnConflictStrategy.REPLACE) void insertAll(List entities); + @Query("DELETE FROM cert_cache where url = :u") + void deleteByUrl(String u); + + @Query("select * from cert_cache where url = :u limit 1") + List getListByUrl(String u); + // @Query("select * from repo_config_cache where repo_id = :repoId limit 1") // Single> getByRepoId(String repoId); } diff --git a/app/src/main/java/com/seafile/seadroid2/framework/data/db/dao/FileTransferDAO.java b/app/src/main/java/com/seafile/seadroid2/framework/data/db/dao/FileTransferDAO.java index 68c6e558a..ead4f514e 100644 --- a/app/src/main/java/com/seafile/seadroid2/framework/data/db/dao/FileTransferDAO.java +++ b/app/src/main/java/com/seafile/seadroid2/framework/data/db/dao/FileTransferDAO.java @@ -98,23 +98,12 @@ public interface FileTransferDAO { int countPendingDownloadListSync(String related_account); - @Query("select * from file_transfer_list where related_account = :related_account and transfer_action = 'UPLOAD' and data_source in ('FOLDER_BACKUP','FILE_BACKUP','ALBUM_BACKUP') and data_status = 0 order by created_at desc") - Single> getUploadListAsync(String related_account); - - @Query("select * from file_transfer_list where related_account = :related_account and transfer_action = 'UPLOAD' and data_source in ('FOLDER_BACKUP','FILE_BACKUP','ALBUM_BACKUP') and data_status = 0 order by created_at desc limit :limit offset :offset") + @Query("select * from file_transfer_list where related_account = :related_account and transfer_action = 'UPLOAD' and data_source in ('FOLDER_BACKUP','FILE_BACKUP','ALBUM_BACKUP') and data_status = 0 order by modified_at desc limit :limit offset :offset") List getPageUploadListSync(String related_account, int limit, int offset); - - @Query("select * from file_transfer_list where related_account = :related_account and transfer_action = 'DOWNLOAD' and data_status = 0 order by created_at desc") - Single> getDownloadListAsync(String related_account); - - @Query("select * from file_transfer_list where related_account = :related_account and transfer_action = 'DOWNLOAD' and data_status = 0 order by created_at desc limit :limit offset :offset") + @Query("select * from file_transfer_list where related_account = :related_account and transfer_action = 'DOWNLOAD' and data_status = 0 order by modified_at desc limit :limit offset :offset") List getPageDownloadListSync(String related_account, int limit, int offset); - - @Query("select * from file_transfer_list where related_account = :related_account and transfer_action = 'DOWNLOAD' and data_status = 0 order by created_at desc") - List getDownloadListSync(String related_account); - @Query("select * from file_transfer_list where related_account = :related_account and transfer_action = :transferAction and target_path = :target_path and data_status = 0 order by created_at desc limit 1") List getByTargetPathSync(String related_account, TransferAction transferAction, String target_path); diff --git a/app/src/main/java/com/seafile/seadroid2/framework/http/BaseIO.java b/app/src/main/java/com/seafile/seadroid2/framework/http/BaseIO.java deleted file mode 100644 index 2a4e9043a..000000000 --- a/app/src/main/java/com/seafile/seadroid2/framework/http/BaseIO.java +++ /dev/null @@ -1,205 +0,0 @@ -package com.seafile.seadroid2.framework.http; - -import com.blankj.utilcode.util.NetworkUtils; -import com.seafile.seadroid2.SeadroidApplication; -import com.seafile.seadroid2.account.Account; -import com.seafile.seadroid2.account.SupportAccountManager; -import com.seafile.seadroid2.ssl.SSLTrustManager; - -import java.io.File; -import java.security.cert.CertificateException; -import java.security.cert.X509Certificate; -import java.util.Arrays; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.ConcurrentMap; -import java.util.concurrent.TimeUnit; - -import javax.net.ssl.SSLContext; -import javax.net.ssl.SSLSocketFactory; -import javax.net.ssl.TrustManager; -import javax.net.ssl.X509TrustManager; - -import okhttp3.Cache; -import okhttp3.CacheControl; -import okhttp3.ConnectionSpec; -import okhttp3.Interceptor; -import okhttp3.OkHttpClient; -import okhttp3.Request; -import okhttp3.Response; -import retrofit2.Converter; -import retrofit2.Retrofit; -import retrofit2.adapter.rxjava2.RxJava2CallAdapterFactory; - -public abstract class BaseIO { - - private final int DEFAULT_TIME_OUT = 120000; - private final File cachePath = SeadroidApplication.getAppContext().getCacheDir(); - - //cache path - final File httpCacheDirectory = new File(cachePath, "cache"); - - //20M - private static final long MAX_CACHE_SIZE = 20 * 1024 * 1024L; - - private final Cache cache; - - //8h - private static final int MAX_STALE = 60 * 60 * 8; - - //10min - private static final int MAX_AGE = 600; - - private OkHttpClient okHttpClient = null; - - protected static final ConcurrentMap CLIENT_MAP = new ConcurrentHashMap<>(); - - public BaseIO() { - this.cache = new Cache(httpCacheDirectory, MAX_CACHE_SIZE); - } - - public abstract String getServerUrl(); - - public abstract Converter.Factory getConverterFactory(); - - public abstract List getInterceptors(); - - public abstract Account getAccount(); - - public T execute(Class clazz) { - Retrofit retrofit = getRetrofit(getAccount(), false); - return retrofit.create(clazz); - } - - public T execute(Class clazz, boolean isForceCreateClient) { - Retrofit retrofit = getRetrofit(getAccount(), isForceCreateClient); - return retrofit.create(clazz); - } - - //cache interceptor - private final Interceptor REWRITE_CACHE_CONTROL_INTERCEPTOR = chain -> { - Request request = chain.request(); - - boolean isConnected = NetworkUtils.isConnected(); - if (!isConnected) { - //no network,use cache data - request = request.newBuilder().cacheControl(CacheControl.FORCE_CACHE).build(); - } else { - request = request.newBuilder().cacheControl(CacheControl.FORCE_NETWORK).build(); - } - - Response originalResponse = chain.proceed(request); - if (isConnected) { - return originalResponse.newBuilder() - .removeHeader("Pragma") - .removeHeader("Cache-Control") - .header("Cache-Control", "public, max-age=" + MAX_AGE) - .build(); - } else { - return originalResponse.newBuilder() - .removeHeader("Pragma") - .removeHeader("Cache-Control") - .header("Cache-Control", "public, only-if-cached, max-stale=" + MAX_STALE) - .build(); - } - }; - - /** - * @param isForceCreate reset retrofit/okhttp client(current account) if true - */ - private Retrofit getRetrofit(Account account, boolean isForceCreate) { - if (account == null) { - throw new IllegalStateException("account is null."); - } - - if (!isForceCreate) { - if (CLIENT_MAP.containsKey(account.getSignature())) { - return CLIENT_MAP.get(account.getSignature()); - } - } - - return createRetrofit(account); - } - - private Retrofit createRetrofit(Account account) { - Retrofit.Builder rBuilder = new Retrofit.Builder(); - rBuilder.baseUrl(getServerUrl()); - rBuilder.addConverterFactory(getConverterFactory()); - rBuilder.addCallAdapterFactory(RxJava2CallAdapterFactory.create()); - - rBuilder.client(getClient()); - Retrofit retrofit = rBuilder.build(); - - CLIENT_MAP.put(account.getSignature(), retrofit); - - return retrofit; - } - - final TrustManager[] trustAllCerts = new TrustManager[]{ - new X509TrustManager() { - @Override - public void checkClientTrusted(java.security.cert.X509Certificate[] chain, String authType) throws CertificateException { - } - - @Override - public void checkServerTrusted(java.security.cert.X509Certificate[] chain, String authType) throws CertificateException { - } - - @Override - public java.security.cert.X509Certificate[] getAcceptedIssuers() { - return new X509Certificate[]{}; - } - } - }; - - public OkHttpClient getClient() { - if (okHttpClient == null) { - synchronized (BaseIO.class) { - if (okHttpClient == null) { - OkHttpClient.Builder builder = new OkHttpClient.Builder(); - - try { - // Install the all-trusting trust manager - final SSLContext sslContext = SSLContext.getInstance("TLS"); - sslContext.init(null, trustAllCerts, new java.security.SecureRandom()); - // Create an ssl socket factory with our all-trusting manager - final SSLSocketFactory sslSocketFactory = sslContext.getSocketFactory(); - - builder.sslSocketFactory(sslSocketFactory, (X509TrustManager) trustAllCerts[0]); - } catch (Exception e) { - e.printStackTrace(); - } - - builder.connectionSpecs(Arrays.asList( - ConnectionSpec.MODERN_TLS, - ConnectionSpec.COMPATIBLE_TLS, - ConnectionSpec.CLEARTEXT)); - builder.cache(cache); - - //cache control - builder.interceptors().add(REWRITE_CACHE_CONTROL_INTERCEPTOR); - builder.networkInterceptors().add(REWRITE_CACHE_CONTROL_INTERCEPTOR); - - //add interceptors - List interceptors = getInterceptors(); - if (interceptors != null && !interceptors.isEmpty()) { - for (Interceptor i : interceptors) { - builder.interceptors().add(i); - } - } - - //timeout - builder.writeTimeout(DEFAULT_TIME_OUT, TimeUnit.MILLISECONDS); - builder.readTimeout(DEFAULT_TIME_OUT, TimeUnit.MILLISECONDS); - builder.connectTimeout(DEFAULT_TIME_OUT, TimeUnit.MILLISECONDS); - - okHttpClient = builder.build(); - } - } - } - return okHttpClient; - } - -} diff --git a/app/src/main/java/com/seafile/seadroid2/framework/http/BaseOkHttpClient.java b/app/src/main/java/com/seafile/seadroid2/framework/http/BaseOkHttpClient.java new file mode 100644 index 000000000..cf31fd645 --- /dev/null +++ b/app/src/main/java/com/seafile/seadroid2/framework/http/BaseOkHttpClient.java @@ -0,0 +1,90 @@ +package com.seafile.seadroid2.framework.http; + +import android.text.TextUtils; + +import com.blankj.utilcode.util.NetworkUtils; +import com.seafile.seadroid2.SeadroidApplication; +import com.seafile.seadroid2.account.Account; +import com.seafile.seadroid2.framework.http.interceptor.HeaderInterceptor; + +import java.io.File; +import java.util.ArrayList; +import java.util.List; + +import okhttp3.Cache; +import okhttp3.CacheControl; +import okhttp3.Interceptor; +import okhttp3.OkHttpClient; +import okhttp3.Request; +import okhttp3.Response; +import okhttp3.logging.HttpLoggingInterceptor; + +public abstract class BaseOkHttpClient { + protected final int DEFAULT_TIME_OUT = 120000; + protected final int MAX_AGE = 600; + protected final int MAX_STALE = 60 * 60 * 8; + protected final long MAX_CACHE_SIZE = 20 * 1024 * 1024L; + + protected final Cache cache; + protected final File cachePath = SeadroidApplication.getAppContext().getCacheDir(); + + //cache path + final File httpCacheDirectory = new File(cachePath, "cache"); + + protected Account account; + + public BaseOkHttpClient(Account account) { + this.account = account; + this.cache = new Cache(httpCacheDirectory, MAX_CACHE_SIZE); + } + + public abstract OkHttpClient getOkClient(); + + protected List getInterceptors() { + + List interceptors = new ArrayList<>(); + + //print log + HttpLoggingInterceptor loggingInterceptor = new HttpLoggingInterceptor(); + loggingInterceptor.setLevel(HttpLoggingInterceptor.Level.BASIC); +// loggingInterceptor.setLevel(BuildConfig.DEBUG ? HttpLoggingInterceptor.Level.BODY : HttpLoggingInterceptor.Level.BASIC); + interceptors.add(loggingInterceptor); + + if (account != null && !TextUtils.isEmpty(account.token)) { + interceptors.add(new HeaderInterceptor(account.token)); + } +// interceptors.add(new AddCookiesInterceptor()); +// interceptors.add(new ReceivedCookiesInterceptor()); + + return interceptors; + } + + + //cache interceptor + protected final Interceptor REWRITE_CACHE_CONTROL_INTERCEPTOR = chain -> { + Request request = chain.request(); + + boolean isConnected = NetworkUtils.isConnected(); + if (!isConnected) { + //no network,use cache data + request = request.newBuilder().cacheControl(CacheControl.FORCE_CACHE).build(); + } else { + request = request.newBuilder().cacheControl(CacheControl.FORCE_NETWORK).build(); + } + + Response originalResponse = chain.proceed(request); + if (isConnected) { + return originalResponse.newBuilder() + .removeHeader("Pragma") + .removeHeader("Cache-Control") + .header("Cache-Control", "public, max-age=" + MAX_AGE) + .build(); + } else { + return originalResponse.newBuilder() + .removeHeader("Pragma") + .removeHeader("Cache-Control") + .header("Cache-Control", "public, only-if-cached, max-stale=" + MAX_STALE) + .build(); + } + }; +} diff --git a/app/src/main/java/com/seafile/seadroid2/framework/http/HttpIO.java b/app/src/main/java/com/seafile/seadroid2/framework/http/HttpIO.java new file mode 100644 index 000000000..00702a34a --- /dev/null +++ b/app/src/main/java/com/seafile/seadroid2/framework/http/HttpIO.java @@ -0,0 +1,217 @@ +package com.seafile.seadroid2.framework.http; + +import android.text.TextUtils; + +import com.blankj.utilcode.util.CloneUtils; +import com.seafile.seadroid2.account.Account; +import com.seafile.seadroid2.account.SupportAccountManager; +import com.seafile.seadroid2.framework.http.callback.ProgressCallback; +import com.seafile.seadroid2.framework.http.converter.ConverterFactory; +import com.seafile.seadroid2.framework.http.download.BinaryFileDownloader; +import com.seafile.seadroid2.framework.http.download.BinaryFileWriter; +import com.seafile.seadroid2.framework.util.TokenManager; +import com.seafile.seadroid2.listener.ProgressListener; + +import java.io.File; +import java.io.IOException; +import java.io.OutputStream; +import java.nio.file.Files; +import java.util.Objects; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; + +import io.reactivex.BackpressureStrategy; +import io.reactivex.Flowable; +import io.reactivex.FlowableEmitter; +import io.reactivex.FlowableOnSubscribe; +import okhttp3.OkHttpClient; +import retrofit2.Retrofit; +import retrofit2.adapter.rxjava2.RxJava2CallAdapterFactory; + +public class HttpIO { + private static volatile HttpIO INSTANCE; + + private final Account account; + private BaseOkHttpClient okHttpClient; + + private static final ConcurrentMap IO_MAP = new ConcurrentHashMap<>(); + + private HttpIO(Account account) { + if (account == null) { + throw new IllegalArgumentException("IO constructor(): account is null."); + } + + if (TextUtils.isEmpty(account.server)) { + throw new IllegalArgumentException("IO constructor(): account.server is null."); + } + + if (TextUtils.isEmpty(account.email)) { + throw new IllegalArgumentException("IO constructor(): account.email is null."); + } + + this.account = account; + } + + /** + * Logged in + */ + public static HttpIO getCurrentInstance() { + if (INSTANCE != null) { + return INSTANCE; + } + + // singleton and map + synchronized (HttpIO.class) { + if (INSTANCE == null) { + + Account curAccount = SupportAccountManager.getInstance().getCurrentAccount(); + if (curAccount == null) { + throw new IllegalStateException("IO instance(): No current account"); + } + + // + INSTANCE = new HttpIO(curAccount); + + // + TokenManager.getInstance().setToken(curAccount.token); + } + } + + return INSTANCE; + } + + /** + * Not logged in/Log in to another server + */ + public static HttpIO getInstanceByAccount(Account account) { + if (account == null) { + return null; + } + + if (IO_MAP.containsKey(account.getSignature())) { + HttpIO httpIo = IO_MAP.get(account.getSignature()); + if (httpIo == null) { + return getAndRemoveIO(account); + } + + if (!Objects.equals(httpIo.account, account)) { + return getAndRemoveIO(account); + } + + return httpIo; + } + + return getAndRemoveIO(account); + } + + private static HttpIO getAndRemoveIO(Account account) { + Account newAccount = CloneUtils.deepClone(account, Account.class); + HttpIO httpIo = new HttpIO(newAccount); + + IO_MAP.remove(newAccount.getSignature()); + IO_MAP.put(newAccount.getSignature(), httpIo); + + return httpIo; + } + + public Account getAccount() { + return account; + } + + /** + * get server url + */ + public String getServerUrl() { + return account.getServer(); + } + + /** + * get client + */ + public BaseOkHttpClient getOkHttpClient() { + if (okHttpClient == null) { + okHttpClient = new SafeOkHttpClient(account); + } + return okHttpClient; + } + + + /** + * When log in again or switch account, you should reset this IO Singleton. + *

because it's a SINGLETON, unless kill the APP!

+ */ + public static void resetLoggedInInstance() { + INSTANCE = null; + IO_MAP.clear(); + } + + + public T execute(Class clazz) { + Retrofit retrofit = createRetrofit(); + return retrofit.create(clazz); + } + + private Retrofit createRetrofit() { + Retrofit.Builder rBuilder = new Retrofit.Builder(); + rBuilder.baseUrl(getServerUrl()); + rBuilder.addConverterFactory(ConverterFactory.create()); + rBuilder.addCallAdapterFactory(RxJava2CallAdapterFactory.create()); + + rBuilder.client(getOkHttpClient().getOkClient()); + + return rBuilder.build(); + } + + + public void downloadBinarySync(String url, File destinationFile, ProgressListener callback) throws IOException { + OkHttpClient client = getOkHttpClient().getOkClient(); + + try (OutputStream outputStream = Files.newOutputStream(destinationFile.toPath())) { + BinaryFileWriter fileWriter = new BinaryFileWriter(outputStream, new ProgressCallback() { + @Override + public void onProgress(long transferSize, long totalSize) { + if (callback != null) { + callback.onProgress(destinationFile.getName(), transferSize, totalSize); + } + } + }); + + try (BinaryFileDownloader fileDownloader = new BinaryFileDownloader(client, fileWriter)) { + fileDownloader.download(url); + + } catch (Exception e) { + if (callback != null) { + callback.isCancelled(); + } + e.printStackTrace(); + } + } + } + + public Flowable downloadBinary(String url, File destinationFile) { + + return Flowable.create(new FlowableOnSubscribe() { + @Override + public void subscribe(FlowableEmitter emitter) throws Exception { + OkHttpClient client = getOkHttpClient().getOkClient(); + + try (OutputStream outputStream = Files.newOutputStream(destinationFile.toPath())) { + BinaryFileWriter fileWriter = new BinaryFileWriter(outputStream, new ProgressCallback() { + @Override + public void onProgress(long transferSize, long totalSize) { + emitter.onNext(new Long[]{transferSize, totalSize}); + } + }); + + try (BinaryFileDownloader fileDownloader = new BinaryFileDownloader(client, fileWriter)) { + fileDownloader.download(url); + + emitter.onComplete(); + } catch (Exception e) { + emitter.onError(e); + } + } + } + }, BackpressureStrategy.BUFFER); + } +} diff --git a/app/src/main/java/com/seafile/seadroid2/framework/http/IO.java b/app/src/main/java/com/seafile/seadroid2/framework/http/IO.java deleted file mode 100644 index 6c8bd0181..000000000 --- a/app/src/main/java/com/seafile/seadroid2/framework/http/IO.java +++ /dev/null @@ -1,264 +0,0 @@ -package com.seafile.seadroid2.framework.http; - -import com.seafile.seadroid2.BuildConfig; -import com.seafile.seadroid2.account.Account; -import com.seafile.seadroid2.account.SupportAccountManager; -import com.seafile.seadroid2.config.Constants; -import com.seafile.seadroid2.framework.http.callback.ProgressCallback; -import com.seafile.seadroid2.framework.http.download.BinaryFileDownloader; -import com.seafile.seadroid2.framework.http.download.BinaryFileWriter; -import com.seafile.seadroid2.framework.util.SLogs; -import com.seafile.seadroid2.framework.http.converter.ConverterFactory; -import com.seafile.seadroid2.framework.http.interceptor.HeaderInterceptor; -import com.seafile.seadroid2.listener.ProgressListener; - -import org.apache.commons.lang3.StringUtils; - -import java.io.File; -import java.io.IOException; -import java.io.OutputStream; -import java.nio.file.Files; -import java.util.ArrayList; -import java.util.List; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.ConcurrentMap; - -import io.reactivex.BackpressureStrategy; -import io.reactivex.Flowable; -import io.reactivex.FlowableEmitter; -import io.reactivex.FlowableOnSubscribe; -import okhttp3.Interceptor; -import okhttp3.OkHttpClient; -import okhttp3.logging.HttpLoggingInterceptor; -import retrofit2.Converter; - - -public class IO extends BaseIO { - private static volatile IO LOGGED_IN_INSTANCE; - private String mServerUrl; - private String mToken; - - private Account account; - - private static final ConcurrentMap IO_MAP = new ConcurrentHashMap<>(); - - /** - * Logged in - */ - public static IO getInstanceWithLoggedIn() { - if (LOGGED_IN_INSTANCE != null) { - return LOGGED_IN_INSTANCE; - } - - // singleton and map - synchronized (IO.class) { - if (LOGGED_IN_INSTANCE == null) { - LOGGED_IN_INSTANCE = new IO(); - - Account currentAccount = SupportAccountManager.getInstance().getCurrentAccount(); - if (currentAccount == null) { - throw new IllegalStateException("IO: No current account"); - } - - SLogs.d(currentAccount.toString()); - LOGGED_IN_INSTANCE.setAccount(currentAccount); - LOGGED_IN_INSTANCE.setToken(currentAccount.token); - LOGGED_IN_INSTANCE.setServerUrl(currentAccount.server); - - // - IO_MAP.put(currentAccount.getSignature(), LOGGED_IN_INSTANCE); - } - } - - return LOGGED_IN_INSTANCE; - } - - - /** - * Not logged in/Log in to another server - */ - public static IO getInstanceByAccount(Account account) { - if (IO_MAP.containsKey(account.getSignature())) { - return IO_MAP.get(account.getSignature()); - } - - IO io = new IO(); - io.setServerUrl(account.server); - io.setToken(account.token); - io.account = account; - - IO_MAP.put(account.getSignature(), io); - return io; - } - - public static void removeInstanceByAccount(Account account) { - CLIENT_MAP.remove(account.getSignature()); - IO_MAP.remove(account.getSignature()); - } - - /** - * Not logged in/Log in to another server - */ - public static IO newInstanceByAccount(Account account) { - - - IO io = new IO(); - io.setServerUrl(account.server); - io.setToken(account.token); - io.account = account; - return io; - } - - public static void updateInstanceByAccount(Account account) { - IO_MAP.remove(account.getSignature()); - - //reset okhttp client - CLIENT_MAP.remove(account.getSignature()); - - // - IO io = new IO(); - io.setServerUrl(account.server); - io.setToken(account.token); - io.setAccount(account); - - IO_MAP.put(account.getSignature(), io); - } - - @Override - public Account getAccount() { - return account; - } - -// /** -// * Not logged in/Log in to another server -// */ -// public static IO getNewInstance(String hostUrl, String token) { -// IO io = new IO(); -// io.setServerUrl(hostUrl); -// io.setToken(token); -// -// // -// resetSingleton(); -// -// return io; -// } - - /** - * When you log in again or switch account, you should reset this IO Singleton. - *

because it's a SINGLETON, unless kill the APP!

- */ - public static void resetLoggedInInstance() { - LOGGED_IN_INSTANCE = null; - } - - private void setServerUrl(String mHostUrl) { - this.mServerUrl = mHostUrl; - } - - private void setToken(String mToken) { - this.mToken = mToken; - } - - public String getToken() { - return mToken; - } - - public void setAccount(Account account) { - this.account = account; - } - - /** - * server url - */ - @Override - public String getServerUrl() { - return mServerUrl; - } - - /** - * @return host.com - */ - public String getHostDomain() { - String host = mServerUrl; - host = StringUtils.toRootLowerCase(host); - host = StringUtils.removeStart(host, Constants.Protocol.HTTPS); - host = StringUtils.removeStart(host, Constants.Protocol.HTTP); - return StringUtils.removeEnd(host, "/"); - } - - - @Override - public List getInterceptors() { - - List interceptors = new ArrayList<>(); - - //print log - HttpLoggingInterceptor loggingInterceptor = new HttpLoggingInterceptor(); - loggingInterceptor.setLevel(HttpLoggingInterceptor.Level.BASIC); -// loggingInterceptor.setLevel(BuildConfig.DEBUG ? HttpLoggingInterceptor.Level.BODY : HttpLoggingInterceptor.Level.BASIC); - interceptors.add(loggingInterceptor); - - interceptors.add(new HeaderInterceptor(mToken)); -// interceptors.add(new AddCookiesInterceptor()); -// interceptors.add(new ReceivedCookiesInterceptor()); - - return interceptors; - } - - @Override - public Converter.Factory getConverterFactory() { - return ConverterFactory.create(); - } - - public void downloadBinarySync(String url, File destinationFile, ProgressListener callback) throws IOException { - OkHttpClient client = getClient(); - - try (OutputStream outputStream = Files.newOutputStream(destinationFile.toPath())) { - BinaryFileWriter fileWriter = new BinaryFileWriter(outputStream, new ProgressCallback() { - @Override - public void onProgress(long transferSize, long totalSize) { - if (callback != null) { - callback.onProgress(destinationFile.getName(), transferSize, totalSize); - } - } - }); - - try (BinaryFileDownloader fileDownloader = new BinaryFileDownloader(client, fileWriter)) { - fileDownloader.download(url); - - } catch (Exception e) { - if (callback != null) { - callback.isCancelled(); - } - e.printStackTrace(); - } - } - } - - public Flowable downloadBinary(String url, File destinationFile) throws IOException { - - return Flowable.create(new FlowableOnSubscribe() { - @Override - public void subscribe(FlowableEmitter emitter) throws Exception { - OkHttpClient client = getClient(); - - try (OutputStream outputStream = Files.newOutputStream(destinationFile.toPath())) { - BinaryFileWriter fileWriter = new BinaryFileWriter(outputStream, new ProgressCallback() { - @Override - public void onProgress(long transferSize, long totalSize) { - emitter.onNext(new Long[]{transferSize, totalSize}); - } - }); - - try (BinaryFileDownloader fileDownloader = new BinaryFileDownloader(client, fileWriter)) { - fileDownloader.download(url); - - emitter.onComplete(); - } catch (Exception e) { - emitter.onError(e); - } - } - } - }, BackpressureStrategy.BUFFER); - } -} diff --git a/app/src/main/java/com/seafile/seadroid2/framework/http/SafeOkHttpClient.java b/app/src/main/java/com/seafile/seadroid2/framework/http/SafeOkHttpClient.java new file mode 100644 index 000000000..f257a82b6 --- /dev/null +++ b/app/src/main/java/com/seafile/seadroid2/framework/http/SafeOkHttpClient.java @@ -0,0 +1,98 @@ +package com.seafile.seadroid2.framework.http; + +import com.blankj.utilcode.util.CollectionUtils; +import com.seafile.seadroid2.account.Account; + +import java.security.KeyStore; +import java.security.KeyStoreException; +import java.security.NoSuchAlgorithmException; +import java.security.SecureRandom; +import java.util.Arrays; +import java.util.List; +import java.util.concurrent.TimeUnit; + +import javax.net.ssl.SSLContext; +import javax.net.ssl.SSLSocketFactory; +import javax.net.ssl.TrustManager; +import javax.net.ssl.TrustManagerFactory; +import javax.net.ssl.X509TrustManager; + +import okhttp3.ConnectionSpec; +import okhttp3.Interceptor; +import okhttp3.OkHttpClient; +import okhttp3.Request; + +public class SafeOkHttpClient extends BaseOkHttpClient { + public SafeOkHttpClient(Account account) { + super(account); + } + + private TrustManager[] getTrustManagers() { + try { + TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm()); + trustManagerFactory.init((KeyStore) null); + TrustManager[] trustManagers = trustManagerFactory.getTrustManagers(); + if (trustManagers.length != 1 || !(trustManagers[0] instanceof X509TrustManager)) { + throw new IllegalStateException("Unexpected default trust managers:" + Arrays.toString(trustManagers)); + } + return trustManagers; + } catch (NoSuchAlgorithmException | KeyStoreException e) { + throw new RuntimeException(e); + } + } + + public SSLSocketFactory getTLSSocketFactory(TrustManager[] trustManagers) { + try { + X509TrustManager trustManager = (X509TrustManager) trustManagers[0]; + + SSLContext sslContext = SSLContext.getInstance("TLS"); + sslContext.init(null, new TrustManager[]{trustManager}, new SecureRandom()); + return sslContext.getSocketFactory(); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + + @Override + public OkHttpClient getOkClient() { + OkHttpClient.Builder builder = new OkHttpClient.Builder(); + + TrustManager[] trustManagers = getTrustManagers(); + X509TrustManager trustManager = (X509TrustManager) trustManagers[0]; + SSLSocketFactory sslSocketFactory = getTLSSocketFactory(trustManagers); + + // Add an interceptor to set SSL only for HTTPS + builder.addInterceptor(chain -> { + Request request = chain.request(); + if (request.isHttps()) { + builder.sslSocketFactory(sslSocketFactory, trustManager); + } + return chain.proceed(request); + }); + + builder.connectionSpecs(Arrays.asList( + ConnectionSpec.MODERN_TLS, + ConnectionSpec.COMPATIBLE_TLS, + ConnectionSpec.CLEARTEXT)); + builder.cache(cache); + //cache control + builder.interceptors().add(REWRITE_CACHE_CONTROL_INTERCEPTOR); + builder.networkInterceptors().add(REWRITE_CACHE_CONTROL_INTERCEPTOR); + + //add interceptors + List interceptors = getInterceptors(); + if (!CollectionUtils.isEmpty(interceptors)) { + for (Interceptor i : interceptors) { + builder.interceptors().add(i); + } + } + + //timeout + builder.writeTimeout(DEFAULT_TIME_OUT, TimeUnit.MILLISECONDS); + builder.readTimeout(DEFAULT_TIME_OUT, TimeUnit.MILLISECONDS); + builder.connectTimeout(DEFAULT_TIME_OUT, TimeUnit.MILLISECONDS); + + return builder.build(); + } +} diff --git a/app/src/main/java/com/seafile/seadroid2/framework/http/UnsafeOkHttpClient.java b/app/src/main/java/com/seafile/seadroid2/framework/http/UnsafeOkHttpClient.java new file mode 100644 index 000000000..82997a0a6 --- /dev/null +++ b/app/src/main/java/com/seafile/seadroid2/framework/http/UnsafeOkHttpClient.java @@ -0,0 +1,93 @@ +package com.seafile.seadroid2.framework.http; + +import com.blankj.utilcode.util.CollectionUtils; +import com.seafile.seadroid2.account.Account; + +import java.security.cert.CertificateException; +import java.security.cert.X509Certificate; +import java.util.Arrays; +import java.util.List; +import java.util.concurrent.TimeUnit; + +import javax.net.ssl.HostnameVerifier; +import javax.net.ssl.SSLContext; +import javax.net.ssl.SSLSession; +import javax.net.ssl.SSLSocketFactory; +import javax.net.ssl.TrustManager; +import javax.net.ssl.X509TrustManager; + +import okhttp3.ConnectionSpec; +import okhttp3.Interceptor; +import okhttp3.OkHttpClient; + +public class UnsafeOkHttpClient extends BaseOkHttpClient { + public UnsafeOkHttpClient(Account account) { + super(account); + } + + private final TrustManager[] trustAllCerts = new TrustManager[]{new X509TrustManager() { + @Override + public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException { + } + + @Override + public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException { + } + + @Override + public X509Certificate[] getAcceptedIssuers() { + return new X509Certificate[]{}; + } + } + }; + + @Override + public OkHttpClient getOkClient() { + OkHttpClient.Builder builder = new OkHttpClient.Builder(); + + + try { + // Install the all-trusting trust manager + final SSLContext sslContext = SSLContext.getInstance("TLS"); + sslContext.init(null, trustAllCerts, new java.security.SecureRandom()); + // Create an ssl socket factory with our all-trusting manager + final SSLSocketFactory sslSocketFactory = sslContext.getSocketFactory(); + + builder.sslSocketFactory(sslSocketFactory, (X509TrustManager) trustAllCerts[0]); + } catch (Exception e) { + e.printStackTrace(); + } + + builder.connectionSpecs(Arrays.asList( + ConnectionSpec.MODERN_TLS, + ConnectionSpec.COMPATIBLE_TLS, + ConnectionSpec.CLEARTEXT)); + builder.cache(cache); + builder.hostnameVerifier(new HostnameVerifier() { + @Override + public boolean verify(String hostname, SSLSession session) { + return true; + } + }); + //cache control + builder.interceptors().add(REWRITE_CACHE_CONTROL_INTERCEPTOR); + builder.networkInterceptors().add(REWRITE_CACHE_CONTROL_INTERCEPTOR); + + //add interceptors + List interceptors = getInterceptors(); + if (!CollectionUtils.isEmpty(interceptors)) { + for (Interceptor i : interceptors) { + builder.interceptors().add(i); + } + } + + //timeout + builder.writeTimeout(DEFAULT_TIME_OUT, TimeUnit.MILLISECONDS); + builder.readTimeout(DEFAULT_TIME_OUT, TimeUnit.MILLISECONDS); + builder.connectTimeout(DEFAULT_TIME_OUT, TimeUnit.MILLISECONDS); + + return builder.build(); + } + + +} diff --git a/app/src/main/java/com/seafile/seadroid2/framework/http/interceptor/CurrentTokenInterceptor.java b/app/src/main/java/com/seafile/seadroid2/framework/http/interceptor/CurrentTokenInterceptor.java new file mode 100644 index 000000000..5745aabe5 --- /dev/null +++ b/app/src/main/java/com/seafile/seadroid2/framework/http/interceptor/CurrentTokenInterceptor.java @@ -0,0 +1,32 @@ +package com.seafile.seadroid2.framework.http.interceptor; + +import android.text.TextUtils; + +import com.seafile.seadroid2.framework.util.TokenManager; + +import java.io.IOException; + +import okhttp3.Interceptor; +import okhttp3.Request; +import okhttp3.Response; + +public class CurrentTokenInterceptor implements Interceptor { + @Override + public Response intercept(Chain chain) throws IOException { + return chain.proceed(initBuilder(chain.request().newBuilder()).build()); + } + + private Request.Builder initBuilder(Request.Builder builder) { + builder.addHeader("Content-Type", "application/json"); + builder.addHeader("Accept", "application/json"); + builder.addHeader("charset", "utf-8"); + builder.addHeader("timestamp", String.valueOf(System.currentTimeMillis())); + + String authToken = TokenManager.getInstance().getToken(); + if (!TextUtils.isEmpty(authToken)) { + builder.addHeader("Authorization", "Token " + authToken); + } + + return builder; + } +} diff --git a/app/src/main/java/com/seafile/seadroid2/framework/util/AccountUtils.java b/app/src/main/java/com/seafile/seadroid2/framework/util/AccountUtils.java index 71994d827..557b1f87e 100644 --- a/app/src/main/java/com/seafile/seadroid2/framework/util/AccountUtils.java +++ b/app/src/main/java/com/seafile/seadroid2/framework/util/AccountUtils.java @@ -9,7 +9,7 @@ import com.seafile.seadroid2.framework.datastore.sp.AlbumBackupManager; import com.seafile.seadroid2.framework.datastore.sp.FolderBackupManager; import com.seafile.seadroid2.framework.datastore.sp.GestureLockManager; -import com.seafile.seadroid2.framework.http.IO; +import com.seafile.seadroid2.framework.http.HttpIO; import com.seafile.seadroid2.framework.worker.BackgroundJobManagerImpl; import com.seafile.seadroid2.ui.camera_upload.CameraUploadManager; @@ -30,7 +30,7 @@ public static void logout(Account account) { BackgroundJobManagerImpl.getInstance().cancelAllJobs(); //reset IO instance for new account - IO.resetLoggedInInstance(); + HttpIO.resetLoggedInInstance(); //clear instance DataStoreManager.resetUserInstance(); @@ -63,7 +63,7 @@ public static void switchAccount(Account account) { BackgroundJobManagerImpl.getInstance().cancelAllJobs(); //reset IO instance for new account - IO.resetLoggedInInstance(); + HttpIO.resetLoggedInInstance(); //clear instance DataStoreManager.resetUserInstance(); diff --git a/app/src/main/java/com/seafile/seadroid2/framework/util/GlideCache.java b/app/src/main/java/com/seafile/seadroid2/framework/util/GlideCache.java index bdba0002c..ac59f0f01 100644 --- a/app/src/main/java/com/seafile/seadroid2/framework/util/GlideCache.java +++ b/app/src/main/java/com/seafile/seadroid2/framework/util/GlideCache.java @@ -1,15 +1,30 @@ package com.seafile.seadroid2.framework.util; import android.content.Context; + import androidx.annotation.NonNull; +import com.bumptech.glide.Glide; import com.bumptech.glide.GlideBuilder; +import com.bumptech.glide.Registry; import com.bumptech.glide.annotation.GlideModule; +import com.bumptech.glide.integration.okhttp3.OkHttpUrlLoader; import com.bumptech.glide.load.engine.cache.DiskLruCacheFactory; +import com.bumptech.glide.load.model.GlideUrl; import com.bumptech.glide.module.AppGlideModule; import com.seafile.seadroid2.SeadroidApplication; +import com.seafile.seadroid2.framework.http.HttpIO; +import com.seafile.seadroid2.framework.http.interceptor.CurrentTokenInterceptor; +import com.seafile.seadroid2.framework.http.interceptor.HeaderInterceptor; import java.io.File; +import java.io.IOException; +import java.io.InputStream; + +import okhttp3.Interceptor; +import okhttp3.OkHttpClient; +import okhttp3.Request; +import okhttp3.Response; @GlideModule public class GlideCache extends AppGlideModule { @@ -21,5 +36,25 @@ public void applyOptions(@NonNull Context context, @NonNull GlideBuilder builder String rootPath = externalMediaDirs[0].getAbsolutePath(); File dirPath = new File(rootPath + "/GlideCache/"); builder.setDiskCache(new DiskLruCacheFactory(dirPath.getAbsolutePath(), 1024 * 1024 * 100)); + GlideApp.tearDown(); + } + + @Override + public boolean isManifestParsingEnabled() { + return false; + } + + @Override + public void registerComponents(@NonNull Context context, @NonNull Glide glide, @NonNull Registry registry) { + try { + OkHttpClient client = getClient(); + registry.replace(GlideUrl.class, InputStream.class, new OkHttpUrlLoader.Factory(client)); + } catch (IllegalStateException e) { + SLogs.d("No current account?"); + } + } + + private OkHttpClient getClient() { + return new OkHttpClient.Builder().addInterceptor(new CurrentTokenInterceptor()).build(); } } diff --git a/app/src/main/java/com/seafile/seadroid2/framework/util/Objs.java b/app/src/main/java/com/seafile/seadroid2/framework/util/Objs.java index c4659c2e0..1d8f8f9e0 100644 --- a/app/src/main/java/com/seafile/seadroid2/framework/util/Objs.java +++ b/app/src/main/java/com/seafile/seadroid2/framework/util/Objs.java @@ -29,14 +29,13 @@ import com.seafile.seadroid2.framework.data.db.entities.StarredModel; import com.seafile.seadroid2.framework.data.model.BaseModel; import com.seafile.seadroid2.framework.data.model.GroupItemModel; -import com.seafile.seadroid2.framework.data.model.enums.TransferAction; import com.seafile.seadroid2.framework.data.model.enums.TransferStatus; import com.seafile.seadroid2.framework.data.model.objs.DirentShareLinkModel; import com.seafile.seadroid2.framework.data.model.repo.DirentWrapperModel; import com.seafile.seadroid2.framework.data.model.repo.RepoWrapperModel; import com.seafile.seadroid2.framework.data.model.star.StarredWrapperModel; import com.seafile.seadroid2.framework.datastore.sp.Sorts; -import com.seafile.seadroid2.framework.http.IO; +import com.seafile.seadroid2.framework.http.HttpIO; import com.seafile.seadroid2.listener.OnCreateDirentShareLinkListener; import com.seafile.seadroid2.ui.dialog_fragment.AppChoiceDialogFragment; import com.seafile.seadroid2.ui.dialog_fragment.GetShareLinkPasswordDialogFragment; @@ -51,14 +50,11 @@ import java.util.Iterator; import java.util.List; import java.util.Map; -import java.util.Optional; import java.util.TreeMap; import java.util.stream.Collectors; import io.reactivex.Completable; import io.reactivex.Single; -import io.reactivex.SingleEmitter; -import io.reactivex.SingleOnSubscribe; import io.reactivex.SingleSource; import io.reactivex.functions.BiFunction; import io.reactivex.functions.Function; @@ -72,7 +68,7 @@ public class Objs { //////////////////////////// public static Single> getStarredSingleFromServer(Account account) { - Single netSingle = IO.getInstanceByAccount(account).execute(StarredService.class).getStarItems(); + Single netSingle = HttpIO.getInstanceByAccount(account).execute(StarredService.class).getStarItems(); Completable completable = AppDatabase.getInstance().starredDirentDAO().deleteAllByAccount(account.getSignature()); Single deleteSingle = completable.toSingleDefault(0); return Single.zip(netSingle, deleteSingle, new BiFunction>() { @@ -102,7 +98,7 @@ public SingleSource> apply(List starredModels) //////repo //////////////////////////// public static Single> getReposSingleFromServer(Account account) { - Single netSingle = IO.getInstanceByAccount(account).execute(RepoService.class).getRepos(); + Single netSingle = HttpIO.getInstanceByAccount(account).execute(RepoService.class).getRepos(); Single> dbListSingle = AppDatabase.getInstance().repoDao().getListByAccount(account.getSignature()); //load net data and load local data @@ -389,7 +385,7 @@ private static TreeMap> groupRepos(List repos //////////////////////////// public static Single> getDirentsSingleFromServer(Account account, String repoId, String repoName, String parentDir) { - Single netSingle = IO.getInstanceByAccount(account).execute(RepoService.class).getDirents(repoId, parentDir); + Single netSingle = HttpIO.getInstanceByAccount(account).execute(RepoService.class).getDirents(repoId, parentDir); Single> dbSingle = AppDatabase.getInstance().direntDao().getListByParentPath(repoId, parentDir); return Single.zip(netSingle, dbSingle, new BiFunction, List>() { diff --git a/app/src/main/java/com/seafile/seadroid2/framework/util/TokenManager.java b/app/src/main/java/com/seafile/seadroid2/framework/util/TokenManager.java new file mode 100644 index 000000000..0629aefa0 --- /dev/null +++ b/app/src/main/java/com/seafile/seadroid2/framework/util/TokenManager.java @@ -0,0 +1,24 @@ +package com.seafile.seadroid2.framework.util; + +public class TokenManager { + private static TokenManager instance; + private String token; + + private TokenManager() { + } + + public static synchronized TokenManager getInstance() { + if (instance == null) { + instance = new TokenManager(); + } + return instance; + } + + public String getToken() { + return token; + } + + public void setToken(String token) { + this.token = token; + } +} diff --git a/app/src/main/java/com/seafile/seadroid2/framework/worker/TransferWorker.java b/app/src/main/java/com/seafile/seadroid2/framework/worker/TransferWorker.java index 2bf0ac825..1689bcd58 100644 --- a/app/src/main/java/com/seafile/seadroid2/framework/worker/TransferWorker.java +++ b/app/src/main/java/com/seafile/seadroid2/framework/worker/TransferWorker.java @@ -10,7 +10,6 @@ import com.google.common.util.concurrent.ListenableFuture; import com.seafile.seadroid2.SeafException; import com.seafile.seadroid2.account.Account; -import com.seafile.seadroid2.framework.data.BlockInfoBean; import com.seafile.seadroid2.framework.data.db.AppDatabase; import com.seafile.seadroid2.framework.data.db.entities.DirentModel; import com.seafile.seadroid2.framework.data.db.entities.FileTransferEntity; @@ -19,7 +18,7 @@ import com.seafile.seadroid2.framework.data.model.dirents.DirentFileModel; import com.seafile.seadroid2.framework.data.model.enums.TransferDataSource; import com.seafile.seadroid2.framework.data.model.repo.DirentWrapperModel; -import com.seafile.seadroid2.framework.http.IO; +import com.seafile.seadroid2.framework.http.HttpIO; import com.seafile.seadroid2.framework.notification.GeneralNotificationHelper; import com.seafile.seadroid2.framework.util.HttpUtils; import com.seafile.seadroid2.framework.util.Objs; @@ -27,12 +26,8 @@ import com.seafile.seadroid2.ui.file.FileService; import com.seafile.seadroid2.ui.repo.RepoService; -import org.apache.commons.lang3.StringUtils; -import org.json.JSONException; - import java.io.IOException; import java.util.HashMap; -import java.util.LinkedList; import java.util.List; import java.util.Map; @@ -78,7 +73,7 @@ public void showForegroundAsync(ForegroundInfo foregroundInfo) { } protected boolean checkRemoteDirExists(String repoId, String dirPath) throws IOException { - retrofit2.Response response = IO.getInstanceWithLoggedIn() + retrofit2.Response response = HttpIO.getCurrentInstance() .execute(FileService.class) .getDirDetailCall(repoId, dirPath) .execute(); @@ -93,7 +88,7 @@ protected void mkdirRemote(String repoId, String path) throws IOException { Map requestBodyMap = HttpUtils.generateRequestBody(requestDataMap); - retrofit2.Response response = IO.getInstanceWithLoggedIn() + retrofit2.Response response = HttpIO.getCurrentInstance() .execute(FileService.class) .mkDirCall(repoId, path, requestBodyMap) .execute(); @@ -106,7 +101,7 @@ protected void mkdirRemote(String repoId, String path) throws IOException { } protected DirentFileModel getRemoteFile(String repoId, String remotePath) throws IOException, SeafException { - retrofit2.Response fileDetailRes = IO.getInstanceWithLoggedIn() + retrofit2.Response fileDetailRes = HttpIO.getCurrentInstance() .execute(FileService.class) .getFileDetailCall(repoId, remotePath) .execute(); @@ -125,7 +120,7 @@ protected DirentFileModel getRemoteFile(String repoId, String remotePath) throws protected boolean getRemoteDirentList(Account account, RepoModel repoModel, String parentDir) throws IOException, SeafException { - retrofit2.Response wrapperRes = IO.getInstanceWithLoggedIn() + retrofit2.Response wrapperRes = HttpIO.getCurrentInstance() .execute(RepoService.class) .getDirentsCall(repoModel.repo_id, parentDir) .execute(); diff --git a/app/src/main/java/com/seafile/seadroid2/framework/worker/download/DownloadFileScanWorker.java b/app/src/main/java/com/seafile/seadroid2/framework/worker/download/DownloadFileScanWorker.java index 5775a7558..ef74f185e 100644 --- a/app/src/main/java/com/seafile/seadroid2/framework/worker/download/DownloadFileScanWorker.java +++ b/app/src/main/java/com/seafile/seadroid2/framework/worker/download/DownloadFileScanWorker.java @@ -24,7 +24,7 @@ import com.seafile.seadroid2.framework.data.model.enums.TransferAction; import com.seafile.seadroid2.framework.data.model.enums.TransferResult; import com.seafile.seadroid2.framework.data.model.enums.TransferStatus; -import com.seafile.seadroid2.framework.http.IO; +import com.seafile.seadroid2.framework.http.HttpIO; import com.seafile.seadroid2.framework.notification.DownloadNotificationHelper; import com.seafile.seadroid2.framework.util.SLogs; import com.seafile.seadroid2.framework.worker.BackgroundJobManagerImpl; @@ -263,7 +263,7 @@ private void insertIntoDbWhenDirentIsDir(Account account, DirentModel direntMode * fetch file detail from server */ private DirentFileModel fetchFile(DirentModel direntModel) throws IOException { - retrofit2.Response res = IO.getInstanceWithLoggedIn().execute(FileService.class).getFileDetailCall(direntModel.repo_id, direntModel.full_path).execute(); + retrofit2.Response res = HttpIO.getCurrentInstance().execute(FileService.class).getFileDetailCall(direntModel.repo_id, direntModel.full_path).execute(); if (!res.isSuccessful()) { return null; } @@ -275,7 +275,7 @@ private DirentFileModel fetchFile(DirentModel direntModel) throws IOException { * get recursive files from server */ private List fetchRecursiveFiles(DirentModel direntModel) throws IOException { - retrofit2.Response> res = IO.getInstanceWithLoggedIn().execute(FileService.class).getDirRecursiveFileCall(direntModel.repo_id, direntModel.full_path).execute(); + retrofit2.Response> res = HttpIO.getCurrentInstance().execute(FileService.class).getDirRecursiveFileCall(direntModel.repo_id, direntModel.full_path).execute(); if (!res.isSuccessful()) { return Collections.emptyList(); } diff --git a/app/src/main/java/com/seafile/seadroid2/framework/worker/download/DownloadWorker.java b/app/src/main/java/com/seafile/seadroid2/framework/worker/download/DownloadWorker.java index 06c6e8dd3..381d5bee3 100644 --- a/app/src/main/java/com/seafile/seadroid2/framework/worker/download/DownloadWorker.java +++ b/app/src/main/java/com/seafile/seadroid2/framework/worker/download/DownloadWorker.java @@ -28,7 +28,7 @@ import com.seafile.seadroid2.framework.data.db.entities.FileTransferEntity; import com.seafile.seadroid2.framework.data.model.enums.TransferResult; import com.seafile.seadroid2.framework.data.model.enums.TransferStatus; -import com.seafile.seadroid2.framework.http.IO; +import com.seafile.seadroid2.framework.http.HttpIO; import com.seafile.seadroid2.framework.notification.base.BaseNotification; import com.seafile.seadroid2.framework.util.SLogs; import com.seafile.seadroid2.framework.worker.BackgroundJobManagerImpl; @@ -97,7 +97,7 @@ public Result doWork() { //count int pendingCount = AppDatabase.getInstance().fileTransferDAO().countPendingDownloadListSync(account.getSignature()); if (pendingCount <= 0) { - return Result.success(getFinishData()); + return Result.success(getFinishData(false)); } ForegroundInfo foregroundInfo = notificationHelper.getForegroundNotification(); @@ -156,13 +156,14 @@ public Result doWork() { ToastUtils.showLong(R.string.download_finished); } - return Result.success(getFinishData()); + return Result.success(getFinishData(isDownloaded)); } - private Data getFinishData() { + private Data getFinishData(boolean isDownloaded) { return new Data.Builder() .putString(TransferWorker.KEY_DATA_EVENT, TransferEvent.EVENT_FINISH) - .putString(TransferWorker.KEY_DATA_TYPE, String.valueOf(TransferDataSource.FOLDER_BACKUP)) + .putBoolean(TransferWorker.KEY_DATA_PARAM, isDownloaded) + .putString(TransferWorker.KEY_DATA_TYPE, String.valueOf(TransferDataSource.DOWNLOAD)) .build(); } @@ -202,6 +203,10 @@ private void transferFile(Account account, FileTransferEntity transferEntity) th return; } + //update modified_at field + transferEntity.modified_at = System.currentTimeMillis(); + AppDatabase.getInstance().fileTransferDAO().update(transferEntity); + if (repoModels.get(0).canLocalDecrypt()) { downloadFileByBlock(account, transferEntity); } else { @@ -228,7 +233,7 @@ private void downloadFile(Account account, FileTransferEntity transferEntity) th } private Pair getDownloadLink(FileTransferEntity transferEntity, boolean isReUsed) throws SeafException, IOException { - retrofit2.Response res = IO.getInstanceWithLoggedIn() + retrofit2.Response res = HttpIO.getCurrentInstance() .execute(FileService.class) .getFileDownloadLink(transferEntity.repo_id, transferEntity.full_path) .execute(); @@ -273,7 +278,7 @@ private void download(FileTransferEntity fileTransferEntity, String dlink, File .get() .build(); - Call newCall = IO.getInstanceWithLoggedIn().getClient().newCall(request); + Call newCall = HttpIO.getCurrentInstance().getOkHttpClient().getOkClient().newCall(request); try (Response response = newCall.execute()) { if (!response.isSuccessful()) { @@ -366,7 +371,7 @@ private void updateEntitySuccessState(FileTransferEntity fileTransferEntity, Fil ///////////////block/////////////// private FileBlocks getDownloadBlockList(FileTransferEntity transferEntity) throws Exception { - retrofit2.Response res = IO.getInstanceWithLoggedIn() + retrofit2.Response res = HttpIO.getCurrentInstance() .execute(FileService.class) .getFileBlockDownloadLink(transferEntity.repo_id, transferEntity.full_path) .execute(); @@ -423,7 +428,7 @@ private void downloadFileByBlock(Account account, FileTransferEntity transferEnt for (Block blk : fileBlocks.getBlocks()) { File tempBlock = new File(StorageManager.getInstance().getTempDir(), blk.blockId); - retrofit2.Response blockRes = IO.getInstanceWithLoggedIn() + retrofit2.Response blockRes = HttpIO.getCurrentInstance() .execute(FileService.class) .getBlockDownloadLink(transferEntity.repo_id, fileBlocks.getFileId(), blk.blockId) .execute(); @@ -461,7 +466,7 @@ private void downloadBlock(FileBlocks fileBlocks, String blockId, String dlink, .url(dlink) .get() .build(); - Call newCall = IO.getInstanceWithLoggedIn().getClient().newCall(request); + Call newCall = HttpIO.getCurrentInstance().getOkHttpClient().getOkClient().newCall(request); Response response = newCall.execute(); diff --git a/app/src/main/java/com/seafile/seadroid2/framework/worker/upload/BaseUploadWorker.java b/app/src/main/java/com/seafile/seadroid2/framework/worker/upload/BaseUploadWorker.java index 3d2ae3afb..83fbe7803 100644 --- a/app/src/main/java/com/seafile/seadroid2/framework/worker/upload/BaseUploadWorker.java +++ b/app/src/main/java/com/seafile/seadroid2/framework/worker/upload/BaseUploadWorker.java @@ -27,11 +27,7 @@ import com.seafile.seadroid2.framework.data.model.enums.TransferResult; import com.seafile.seadroid2.framework.data.model.enums.TransferStatus; import com.seafile.seadroid2.framework.datastore.StorageManager; -import com.seafile.seadroid2.framework.http.IO; -import com.seafile.seadroid2.framework.notification.AlbumBackupNotificationHelper; -import com.seafile.seadroid2.framework.notification.FileBackupNotificationHelper; -import com.seafile.seadroid2.framework.notification.FolderBackupNotificationHelper; -import com.seafile.seadroid2.framework.notification.base.BaseNotification; +import com.seafile.seadroid2.framework.http.HttpIO; import com.seafile.seadroid2.framework.notification.base.BaseTransferNotificationHelper; import com.seafile.seadroid2.framework.util.HttpUtils; import com.seafile.seadroid2.framework.util.SLogs; @@ -163,8 +159,9 @@ public String isInterrupt(TransferResult result) { protected boolean calcQuota(List list) throws SeafException, IOException { + // Account account = SupportAccountManager.getInstance().getCurrentAccount(); - if (account != null && account.isQuotaNoLimit()) { + if (account != null && account.isQuotaUnlimited()) { return true; } @@ -184,7 +181,7 @@ protected boolean calcQuota(List list) throws SeafException, } protected AccountInfo getAccountInfo() throws IOException, SeafException { - retrofit2.Response response = IO.getInstanceWithLoggedIn() + retrofit2.Response response = HttpIO.getCurrentInstance() .execute(AccountService.class) .getAccountInfoCall() .execute(); @@ -391,6 +388,10 @@ public void transferFile(Account account, FileTransferEntity transferEntity) thr return; } + //update modified_at field + transferEntity.modified_at = System.currentTimeMillis(); + AppDatabase.getInstance().fileTransferDAO().update(transferEntity); + RepoModel repo = repoModels.get(0); if (repo.canLocalDecrypt()) { uploadBlockFile(account, repo, transferEntity); @@ -477,7 +478,7 @@ private void uploadFile(Account account, RepoModel repoModel, FileTransferEntity .url(uploadUrl) .post(requestBody) .build(); - newCall = IO.getInstanceWithLoggedIn().getClient().newCall(request); + newCall = HttpIO.getCurrentInstance().getOkHttpClient().getOkClient().newCall(request); Response response = newCall.execute(); @@ -580,7 +581,7 @@ private void uploadBlockFile(Account account, RepoModel repoModel, FileTransferE private String getFileUploadUrl(String repoId, String target_dir, boolean isUpdate) throws IOException, SeafException { retrofit2.Response res; if (isUpdate) { - res = IO.getInstanceWithLoggedIn() + res = HttpIO.getCurrentInstance() .execute(FileService.class) .getFileUpdateLink(repoId) .execute(); @@ -588,7 +589,7 @@ private String getFileUploadUrl(String repoId, String target_dir, boolean isUpda // target_dir = StringUtils.removeEnd(target_dir, "/"); - res = IO.getInstanceWithLoggedIn() + res = HttpIO.getCurrentInstance() .execute(FileService.class) .getFileUploadLink(repoId, "/") .execute(); @@ -613,7 +614,7 @@ private BlockInfoBean getFileBlockUploadUrl(Account account, String repoId, Link Map requestBodyMap = HttpUtils.generateRequestBody(requestDataMap); - retrofit2.Response res = IO.getInstanceWithLoggedIn() + retrofit2.Response res = HttpIO.getCurrentInstance() .execute(FileService.class) .getFileBlockUploadLink(repoId, requestBodyMap) .execute(); @@ -660,7 +661,7 @@ private String uploadBlocksCommon(String link, List needUploadId, String Request request = new Request.Builder().url(link) .post(body) .build(); - Response response = IO.getInstanceWithLoggedIn().getClient().newCall(request).execute(); + Response response = HttpIO.getCurrentInstance().getOkHttpClient().getOkClient().newCall(request).execute(); if (!response.isSuccessful()) { throw SeafException.networkException; @@ -712,7 +713,7 @@ private void commitUpload(String link, List blkIds, FileTransferEntity t SLogs.d("Folder upload block: newCall has executed()"); } try { - newCall = IO.getInstanceWithLoggedIn().getClient().newCall(request); + newCall = HttpIO.getCurrentInstance().getOkHttpClient().getOkClient().newCall(request); Response response = newCall.execute(); if (!response.isSuccessful()) { diff --git a/app/src/main/java/com/seafile/seadroid2/framework/worker/upload/MediaBackupScannerWorker.java b/app/src/main/java/com/seafile/seadroid2/framework/worker/upload/MediaBackupScannerWorker.java index 985479ba2..67f761f9f 100644 --- a/app/src/main/java/com/seafile/seadroid2/framework/worker/upload/MediaBackupScannerWorker.java +++ b/app/src/main/java/com/seafile/seadroid2/framework/worker/upload/MediaBackupScannerWorker.java @@ -15,7 +15,6 @@ import androidx.work.WorkerParameters; import com.blankj.utilcode.util.CollectionUtils; -import com.blankj.utilcode.util.FileUtils; import com.google.common.base.Joiner; import com.seafile.seadroid2.R; import com.seafile.seadroid2.SeadroidApplication; @@ -23,7 +22,6 @@ import com.seafile.seadroid2.account.Account; import com.seafile.seadroid2.account.SupportAccountManager; import com.seafile.seadroid2.framework.data.model.enums.TransferDataSource; -import com.seafile.seadroid2.framework.datastore.DataManager; import com.seafile.seadroid2.framework.datastore.StorageManager; import com.seafile.seadroid2.framework.data.db.AppDatabase; import com.seafile.seadroid2.framework.data.db.entities.DirentModel; @@ -31,10 +29,9 @@ import com.seafile.seadroid2.framework.data.model.enums.TransferAction; import com.seafile.seadroid2.framework.data.model.repo.DirentWrapperModel; import com.seafile.seadroid2.framework.datastore.sp.AlbumBackupManager; -import com.seafile.seadroid2.framework.http.IO; +import com.seafile.seadroid2.framework.http.HttpIO; import com.seafile.seadroid2.framework.notification.AlbumBackupScanNotificationHelper; import com.seafile.seadroid2.framework.util.SLogs; -import com.seafile.seadroid2.framework.notification.AlbumBackupNotificationHelper; import com.seafile.seadroid2.framework.worker.BackgroundJobManagerImpl; import com.seafile.seadroid2.framework.worker.TransferEvent; import com.seafile.seadroid2.framework.worker.TransferWorker; @@ -443,7 +440,7 @@ private void forceCreateDirectory(String parent, String dirIsBucketName, List direntWrapperModelCall = IO.getInstanceWithLoggedIn().execute(RepoService.class).getDirentsSync(repoConfig.getRepoID(), parent); + Call direntWrapperModelCall = HttpIO.getCurrentInstance().execute(RepoService.class).getDirentsSync(repoConfig.getRepoID(), parent); retrofit2.Response res = direntWrapperModelCall.execute(); if (!res.isSuccessful()) { throw SeafException.networkException; @@ -493,7 +490,7 @@ private void forceCreateDirectory(String parent, String dirIsBucketName, List> filePathList) throws SeafException, IOException { - Call direntWrapperModelCall = IO.getInstanceWithLoggedIn().execute(RepoService.class).getDirentsSync(repoConfig.getRepoID(), parent); + Call direntWrapperModelCall = HttpIO.getCurrentInstance().execute(RepoService.class).getDirentsSync(repoConfig.getRepoID(), parent); retrofit2.Response res = direntWrapperModelCall.execute(); if (!res.isSuccessful()) { throw SeafException.networkException; @@ -550,7 +547,7 @@ private void renameRemoteFile(String name, String fullPath) throws IOException { renameMap.put("newname", newFilename); Map requestBodyMap = HttpUtils.generateRequestBody(renameMap); - retrofit2.Response renameRes = IO.getInstanceWithLoggedIn() + retrofit2.Response renameRes = HttpIO.getCurrentInstance() .execute(FileService.class) .renameFileCall(repoConfig.getRepoID(), fullPath, requestBodyMap) .execute(); diff --git a/app/src/main/java/com/seafile/seadroid2/framework/worker/upload/UploadFileManuallyWorker.java b/app/src/main/java/com/seafile/seadroid2/framework/worker/upload/UploadFileManuallyWorker.java index 6766df87e..46397e9cc 100644 --- a/app/src/main/java/com/seafile/seadroid2/framework/worker/upload/UploadFileManuallyWorker.java +++ b/app/src/main/java/com/seafile/seadroid2/framework/worker/upload/UploadFileManuallyWorker.java @@ -121,6 +121,10 @@ private ListenableWorker.Result start() { throw SeafException.notLoggedInException; } + //update modified_at field + transferEntity.modified_at = System.currentTimeMillis(); + AppDatabase.getInstance().fileTransferDAO().update(transferEntity); + transferFile(account, transferEntity); sendTransferEvent(transferEntity, true); @@ -168,7 +172,7 @@ private ListenableWorker.Result start() { Data data = new Data.Builder() .putString(TransferWorker.KEY_DATA_EVENT, finishFlagEvent) -// .putInt(TransferWorker.KEY_DATA_PARAM, pendingCount) + .putBoolean(TransferWorker.KEY_DATA_PARAM, isUploaded) .putString(TransferWorker.KEY_DATA_TYPE, String.valueOf(TransferDataSource.FILE_BACKUP)) .build(); return ListenableWorker.Result.success(data); diff --git a/app/src/main/java/com/seafile/seadroid2/framework/worker/upload/UploadFolderFileAutomaticallyWorker.java b/app/src/main/java/com/seafile/seadroid2/framework/worker/upload/UploadFolderFileAutomaticallyWorker.java index ff8693d33..121ac1a8b 100644 --- a/app/src/main/java/com/seafile/seadroid2/framework/worker/upload/UploadFolderFileAutomaticallyWorker.java +++ b/app/src/main/java/com/seafile/seadroid2/framework/worker/upload/UploadFolderFileAutomaticallyWorker.java @@ -165,7 +165,7 @@ private Result start() { Data data = new Data.Builder() .putString(TransferWorker.KEY_DATA_EVENT, finishFlagEvent) -// .putInt(TransferWorker.KEY_DATA_PARAM, pendingCount) + .putBoolean(TransferWorker.KEY_DATA_PARAM, isUploaded) .putString(TransferWorker.KEY_DATA_TYPE, String.valueOf(TransferDataSource.FOLDER_BACKUP)) .build(); return Result.success(data); diff --git a/app/src/main/java/com/seafile/seadroid2/framework/worker/upload/UploadMediaFileAutomaticallyWorker.java b/app/src/main/java/com/seafile/seadroid2/framework/worker/upload/UploadMediaFileAutomaticallyWorker.java index 831585a0e..ebcd1c774 100644 --- a/app/src/main/java/com/seafile/seadroid2/framework/worker/upload/UploadMediaFileAutomaticallyWorker.java +++ b/app/src/main/java/com/seafile/seadroid2/framework/worker/upload/UploadMediaFileAutomaticallyWorker.java @@ -152,7 +152,7 @@ public ListenableWorker.Result doWork() { Data data = new Data.Builder() .putString(TransferWorker.KEY_DATA_EVENT, finishFlagEvent) -// .putInt(TransferWorker.KEY_DATA_PARAM, pendingCount) + .putBoolean(TransferWorker.KEY_DATA_PARAM, isUploaded) .putString(TransferWorker.KEY_DATA_TYPE, String.valueOf(TransferDataSource.ALBUM_BACKUP)) .build(); return Result.success(data); diff --git a/app/src/main/java/com/seafile/seadroid2/provider/SeafileProvider.java b/app/src/main/java/com/seafile/seadroid2/provider/SeafileProvider.java index fb560918f..275cac4d9 100644 --- a/app/src/main/java/com/seafile/seadroid2/provider/SeafileProvider.java +++ b/app/src/main/java/com/seafile/seadroid2/provider/SeafileProvider.java @@ -40,24 +40,21 @@ import android.util.Log; import com.blankj.utilcode.util.CollectionUtils; -import com.bumptech.glide.Glide; import com.bumptech.glide.load.engine.DiskCacheStrategy; -import com.bumptech.glide.load.model.GlideUrl; import com.bumptech.glide.request.RequestOptions; import com.seafile.seadroid2.R; import com.seafile.seadroid2.SeadroidApplication; import com.seafile.seadroid2.account.Account; import com.seafile.seadroid2.account.SupportAccountManager; -import com.seafile.seadroid2.config.GlideLoadConfig; -import com.seafile.seadroid2.config.RepoType; import com.seafile.seadroid2.framework.data.db.AppDatabase; import com.seafile.seadroid2.framework.data.db.entities.DirentModel; import com.seafile.seadroid2.framework.data.db.entities.RepoModel; import com.seafile.seadroid2.framework.data.db.entities.StarredModel; import com.seafile.seadroid2.framework.data.model.BaseModel; import com.seafile.seadroid2.framework.datastore.DataManager; -import com.seafile.seadroid2.framework.http.IO; +import com.seafile.seadroid2.framework.http.HttpIO; import com.seafile.seadroid2.framework.util.ConcurrentAsyncTask; +import com.seafile.seadroid2.framework.util.GlideApp; import com.seafile.seadroid2.framework.util.Objs; import com.seafile.seadroid2.framework.util.SLogs; import com.seafile.seadroid2.framework.util.Utils; @@ -507,7 +504,12 @@ private File getFile(final CancellationSignal signal, Account account, RepoModel try { //get download url - Call urlCall = IO.getInstanceByAccount(account).execute(FileService.class).getFileDownloadLinkSync(repo.repo_id, path); + HttpIO httpIo = HttpIO.getInstanceByAccount(account); + if (httpIo == null) { + throw new FileNotFoundException(); + } + Call urlCall = httpIo.execute(FileService.class).getFileDownloadLinkSync(repo.repo_id, path); + Response res = urlCall.execute(); if (!res.isSuccessful()) { throw new FileNotFoundException(); @@ -520,7 +522,7 @@ private File getFile(final CancellationSignal signal, Account account, RepoModel SLogs.d("SeafileProvider: start download"); SLogs.d("targetFile = " + targetFile); - IO.getInstanceByAccount(account).downloadBinarySync(url, targetFile, new ProgressListener() { + httpIo.downloadBinarySync(url, targetFile, new ProgressListener() { @Override public void onProgress(String fileName, long cur, long total) { SLogs.d("fileName = " + fileName + ", cur = " + ", total = " + total); @@ -590,25 +592,23 @@ public Bitmap call() throws Exception { try (FileOutputStream fileStream = new FileOutputStream(pair[1].getFileDescriptor())) { File localFile = DataManager.getLocalRepoFile(account, repoId, repoModel.repo_name, path); - GlideUrl glideUrl; + String urlPath; if (localFile.exists()) { - String urlPath = "file://" + localFile.getAbsolutePath(); + urlPath = "file://" + localFile.getAbsolutePath(); SLogs.d("urlPath = " + urlPath); - glideUrl = new GlideUrl(urlPath); } else { String pathEnc = URLEncoder.encode(path, "UTF-8"); - String urlPath = account.getServer() + String.format("api2/repos/%s/thumbnail/?p=%s&size=%s", repoId, pathEnc, sizeHint.x); + urlPath = account.getServer() + String.format("api2/repos/%s/thumbnail/?p=%s&size=%s", repoId, pathEnc, sizeHint.x); SLogs.d("urlPath = " + urlPath); - glideUrl = GlideLoadConfig.getGlideUrl(urlPath, account.token); } RequestOptions requestOptions = new RequestOptions() .diskCacheStrategy(DiskCacheStrategy.ALL); - bitmap = Glide.with(getContext()) + bitmap = GlideApp.with(getContext()) .asBitmap() .apply(requestOptions) - .load(glideUrl) + .load(urlPath) .centerCrop() .submit(sizeHint.x, sizeHint.y) .get(); diff --git a/app/src/main/java/com/seafile/seadroid2/ssl/CertsDBHelper.java b/app/src/main/java/com/seafile/seadroid2/ssl/CertsDBHelper.java index 581a168c2..3ffceb286 100644 --- a/app/src/main/java/com/seafile/seadroid2/ssl/CertsDBHelper.java +++ b/app/src/main/java/com/seafile/seadroid2/ssl/CertsDBHelper.java @@ -6,15 +6,18 @@ import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.security.cert.X509Certificate; +import java.util.List; -import android.content.ContentValues; import android.content.Context; import android.database.Cursor; import android.database.sqlite.SQLiteDatabase; import android.database.sqlite.SQLiteOpenHelper; import android.util.Base64; +import com.blankj.utilcode.util.CollectionUtils; import com.seafile.seadroid2.SeadroidApplication; +import com.seafile.seadroid2.framework.data.db.AppDatabase; +import com.seafile.seadroid2.framework.data.db.entities.CertEntity; public class CertsDBHelper extends SQLiteOpenHelper { // If you change the database schema, you must increment the database version. @@ -50,99 +53,10 @@ public void onCreate(SQLiteDatabase db) { } @Override - public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {} - - @Override - public void onDowngrade(SQLiteDatabase db, int oldVersion, int newVersion) {} - - public X509Certificate getCertificate(String url) { - String[] projection = {COLUMN_CERT}; - - Cursor c = database.query(TABLE_NAME, - projection, - "url=?", - new String[] {url}, - null, // don't group the rows - null, // don't filter by row groups - null); // The sort order - - if (!c.moveToFirst()) { - c.close(); - return null; - } - - X509Certificate cert = cursorToCert(c); - - c.close(); - return cert; - } - - private X509Certificate cursorToCert(Cursor cursor) { - X509Certificate cert = null; - String text = cursor.getString(0); - - ByteArrayInputStream bis = null; - ObjectInputStream ois = null; - byte[] data = null; - - data = Base64.decode(text, Base64.DEFAULT); - - try { - bis = new ByteArrayInputStream(data); - ois = new ObjectInputStream(bis); - cert = (X509Certificate) ois.readObject(); - return cert; - } catch (ClassNotFoundException e) { - return null; - } catch (IOException e) { - return null; - } finally { - if (bis != null) { - try { - bis.close(); - } catch (IOException e) { - } - } - - if (ois != null) { - try { - ois.close(); - } catch (IOException e) { - } - } - } + public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { } - public void saveCertificate(String url, X509Certificate cert) { - ByteArrayOutputStream bos = new ByteArrayOutputStream(); - ObjectOutputStream out = null; - String text = null; - try { - out = new ObjectOutputStream(bos); - out.writeObject(cert); - byte[] data = bos.toByteArray(); - text = Base64.encodeToString(data, Base64.DEFAULT); - } catch (IOException e) { - return; - } finally { - try { - if (out != null) { - out.close(); - } - } catch (IOException ex) { - // ignore close exception - } - try { - bos.close(); - } catch (IOException ex) { - // ignore close exception - } - } - - ContentValues values = new ContentValues(); - values.put(COLUMN_URL, url); - values.put(COLUMN_CERT, text); - - database.replace(TABLE_NAME, null, values); + @Override + public void onDowngrade(SQLiteDatabase db, int oldVersion, int newVersion) { } } diff --git a/app/src/main/java/com/seafile/seadroid2/ssl/CertsHelper.java b/app/src/main/java/com/seafile/seadroid2/ssl/CertsHelper.java new file mode 100644 index 000000000..2991825a2 --- /dev/null +++ b/app/src/main/java/com/seafile/seadroid2/ssl/CertsHelper.java @@ -0,0 +1,129 @@ +package com.seafile.seadroid2.ssl; + +import android.database.Cursor; +import android.util.Base64; + +import com.blankj.utilcode.util.CollectionUtils; +import com.seafile.seadroid2.framework.data.db.AppDatabase; +import com.seafile.seadroid2.framework.data.db.entities.CertEntity; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.security.cert.X509Certificate; +import java.util.List; + +public class CertsHelper { + + public static X509Certificate getCertificate(String url) { + List list = AppDatabase.getInstance().certDAO().getListByUrl(url); + if (CollectionUtils.isEmpty(list)) { + return null; + } + + return convertToCert(list.get(0).cert); + +// String[] projection = {COLUMN_CERT}; +// +// Cursor c = database.query(TABLE_NAME, +// projection, +// "url=?", +// new String[]{url}, +// null, // don't group the rows +// null, // don't filter by row groups +// null); // The sort order +// +// if (!c.moveToFirst()) { +// c.close(); +// return null; +// } +// +// X509Certificate cert = cursorToCert(c); +// +// c.close(); +// return cert; + } + + private static X509Certificate convertToCert(String text) { + X509Certificate cert = null; + + ByteArrayInputStream bis = null; + ObjectInputStream ois = null; + byte[] data = null; + + data = Base64.decode(text, Base64.DEFAULT); + + try { + bis = new ByteArrayInputStream(data); + ois = new ObjectInputStream(bis); + cert = (X509Certificate) ois.readObject(); + return cert; + } catch (ClassNotFoundException e) { + return null; + } catch (IOException e) { + return null; + } finally { + if (bis != null) { + try { + bis.close(); + } catch (IOException e) { + } + } + + if (ois != null) { + try { + ois.close(); + } catch (IOException e) { + } + } + } + } + + private static X509Certificate cursorToCert(Cursor cursor) { + X509Certificate cert = null; + String text = cursor.getString(0); + return convertToCert(text); + } + + public static void saveCertificate(String url, X509Certificate cert) { + ByteArrayOutputStream bos = new ByteArrayOutputStream(); + ObjectOutputStream out = null; + String text = null; + try { + out = new ObjectOutputStream(bos); + out.writeObject(cert); + byte[] data = bos.toByteArray(); + text = Base64.encodeToString(data, Base64.DEFAULT); + } catch (IOException e) { + return; + } finally { + try { + if (out != null) { + out.close(); + } + } catch (IOException ex) { + // ignore close exception + } + try { + bos.close(); + } catch (IOException ex) { + // ignore close exception + } + } + +// ContentValues values = new ContentValues(); +// values.put(COLUMN_URL, url); +// values.put(COLUMN_CERT, text); +// database.replace(TABLE_NAME, null, values); + + CertEntity certEntity = new CertEntity(); + certEntity.cert = text; + certEntity.url = url; + + AppDatabase.getInstance().certDAO().deleteByUrl(url); + AppDatabase.getInstance().certDAO().insert(certEntity); + + } +} diff --git a/app/src/main/java/com/seafile/seadroid2/ssl/CertsManager.java b/app/src/main/java/com/seafile/seadroid2/ssl/CertsManager.java index badc238c3..458724162 100644 --- a/app/src/main/java/com/seafile/seadroid2/ssl/CertsManager.java +++ b/app/src/main/java/com/seafile/seadroid2/ssl/CertsManager.java @@ -7,6 +7,8 @@ import android.util.Log; import com.google.common.collect.Maps; +import com.seafile.seadroid2.framework.data.db.AppDatabase; +import com.seafile.seadroid2.framework.data.db.entities.CertEntity; import com.seafile.seadroid2.framework.util.ConcurrentAsyncTask; import com.seafile.seadroid2.account.Account; @@ -32,7 +34,7 @@ public static synchronized CertsManager instance() { public void saveCertForAccount(final Account account, boolean rememberChoice) { List certs = SSLTrustManager.instance().getCertsChainForAccount(account); - if (certs == null || certs.size() == 0) { + if (certs == null || certs.isEmpty()) { return; } @@ -43,7 +45,8 @@ public void saveCertForAccount(final Account account, boolean rememberChoice) { ConcurrentAsyncTask.submit(new Runnable() { @Override public void run() { - db.saveCertificate(account.server, cert); + CertsHelper.saveCertificate(account.server,cert); +// db.saveCertificate(account.server, cert); } }); } @@ -57,7 +60,8 @@ public X509Certificate getCertificate(Account account) { return cert; } - cert = db.getCertificate(account.server); +// cert = db.getCertificate(account.server); + cert = CertsHelper.getCertificate(account.server); if (cert != null) { cachedCerts.put(account, cert); } diff --git a/app/src/main/java/com/seafile/seadroid2/ssl/SSLTrustManager.java b/app/src/main/java/com/seafile/seadroid2/ssl/SSLTrustManager.java index 29ea2dbf5..697cff9ca 100644 --- a/app/src/main/java/com/seafile/seadroid2/ssl/SSLTrustManager.java +++ b/app/src/main/java/com/seafile/seadroid2/ssl/SSLTrustManager.java @@ -7,6 +7,7 @@ import com.google.common.collect.Maps; import com.google.common.collect.Sets; import com.seafile.seadroid2.account.Account; +import com.seafile.seadroid2.framework.util.ConcurrentAsyncTask; import org.apache.http.conn.ssl.BrowserCompatHostnameVerifier; import org.apache.http.conn.ssl.X509HostnameVerifier; @@ -29,8 +30,9 @@ import javax.net.ssl.TrustManager; import javax.net.ssl.TrustManagerFactory; import javax.net.ssl.X509TrustManager; -@Deprecated + public final class SSLTrustManager { + public enum SslFailureReason { CERT_NOT_TRUSTED, CERT_CHANGED, @@ -60,11 +62,9 @@ public static synchronized SSLTrustManager instance() { private void init() { try { - javax.net.ssl.TrustManagerFactory tmf; - TrustManager[] tms; - tmf = javax.net.ssl.TrustManagerFactory.getInstance("X509"); + TrustManagerFactory tmf = TrustManagerFactory.getInstance("X509"); tmf.init((KeyStore) null); - tms = tmf.getTrustManagers(); + TrustManager[] tms = tmf.getTrustManagers(); if (tms != null) { for (TrustManager tm : tms) { if (tm instanceof X509TrustManager) { @@ -231,13 +231,13 @@ public void checkServerTrusted(X509Certificate[] chain, String authType) } } - public String getCeritificateInfo() throws CertificateParsingException { - X509Certificate cert = CertsManager.instance().getCertificate(account); - return "sigalgName:" + cert.getSigAlgName() + " Type: " - + cert.getType() + " Version: " + cert.getVersion() - + " IssuerAlternative: " + cert.getIssuerAlternativeNames() - + " NotAfter: " + cert.getNotAfter(); - } +// public String getCeritificateInfo() throws CertificateParsingException { +// X509Certificate cert = CertsManager.instance().getCertificate(account); +// return "sigalgName:" + cert.getSigAlgName() + " Type: " +// + cert.getType() + " Version: " + cert.getVersion() +// + " IssuerAlternative: " + cert.getIssuerAlternativeNames() +// + " NotAfter: " + cert.getNotAfter(); +// } /** * Interface for checking if a hostname matches the names stored inside the server's X.509 certificate @@ -259,26 +259,38 @@ private void customCheck(List chain, String authType) certsChain = ImmutableList.copyOf(chain); - X509Certificate cert = chain.get(0); - X509Certificate savedCert = CertsManager.instance().getCertificate(account); - if (savedCert == null) { - Log.d(DEBUG_TAG, "no saved cert for " + account.server); - reason = SslFailureReason.CERT_NOT_TRUSTED; - throw new CertificateException(); - } else if (savedCert.equals(cert)) { - // The user has confirmed to trust this certificate - Log.d(DEBUG_TAG, "the cert of " + account.server + " is trusted"); - return; - } else { - // The certificate is different from the one user confirmed to trust, - // This may be either: - // 1. The server admin has changed its cert - // 2. The user is under security attack - Log.d(DEBUG_TAG, "the cert of " + account.server + " has changed"); - reason = SslFailureReason.CERT_CHANGED; - throw new CertificateException(); - } + ConcurrentAsyncTask.submit(new Runnable() { + @Override + public void run() { + + try { + X509Certificate cert = chain.get(0); + + X509Certificate savedCert = CertsManager.instance().getCertificate(account); + if (savedCert == null) { + Log.d(DEBUG_TAG, "no saved cert for " + account.server); + reason = SslFailureReason.CERT_NOT_TRUSTED; + throw new CertificateException(); + } else if (savedCert.equals(cert)) { + // The user has confirmed to trust this certificate + Log.d(DEBUG_TAG, "the cert of " + account.server + " is trusted"); + return; + } else { + // The certificate is different from the one user confirmed to trust, + // This may be either: + // 1. The server admin has changed its cert + // 2. The user is under security attack + Log.d(DEBUG_TAG, "the cert of " + account.server + " has changed"); + reason = SslFailureReason.CERT_CHANGED; + throw new CertificateException(); + } + } catch (CertificateException e) { + throw new RuntimeException(e); + } + } + }); + } public X509Certificate[] getAcceptedIssuers() { diff --git a/app/src/main/java/com/seafile/seadroid2/ui/account/AccountDetailActivity.java b/app/src/main/java/com/seafile/seadroid2/ui/account/AccountDetailActivity.java index 32a896408..8ec108b8e 100644 --- a/app/src/main/java/com/seafile/seadroid2/ui/account/AccountDetailActivity.java +++ b/app/src/main/java/com/seafile/seadroid2/ui/account/AccountDetailActivity.java @@ -2,6 +2,7 @@ import android.app.Dialog; import android.content.Context; +import android.content.DialogInterface; import android.content.Intent; import android.os.Bundle; import android.text.Editable; @@ -20,6 +21,7 @@ import android.widget.ImageView; import android.widget.TextView; +import androidx.appcompat.app.AlertDialog; import androidx.appcompat.widget.Toolbar; import androidx.core.app.NavUtils; import androidx.core.app.TaskStackBuilder; @@ -229,21 +231,33 @@ private void onLoginException(Account account, SeafException err) { if (err == SeafException.sslException) { mAuthTokenInputLayout.setVisibility(View.GONE); mRemDeviceCheckBox.setVisibility(View.GONE); - SslConfirmDialog sslConfirmDialog = new SslConfirmDialog(account, - new SslConfirmDialog.Listener() { - @Override - public void onAccepted(boolean rememberChoice) { - CertsManager.instance().saveCertForAccount(account, rememberChoice); - login(); - } - - @Override - public void onRejected() { - mStatusTv.setText(R.string.ssl_error); - mLoginBtn.setEnabled(true); - } - }); - sslConfirmDialog.show(getSupportFragmentManager(), SslConfirmDialog.FRAGMENT_TAG); + + MaterialAlertDialogBuilder builder = new MaterialAlertDialogBuilder(this); + builder.setTitle(R.string.ssl_confirm_title); + builder.setMessage(getString(R.string.ssl_not_trusted,account.getServerHost())); + builder.setPositiveButton(R.string.ok, new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + dialog.dismiss(); + } + }); + builder.create().show(); + +// SslConfirmDialog sslConfirmDialog = new SslConfirmDialog(account, +// new SslConfirmDialog.Listener() { +// @Override +// public void onAccepted(boolean rememberChoice) { +// CertsManager.instance().saveCertForAccount(account, rememberChoice); +// login(); +// } +// +// @Override +// public void onRejected() { +// mStatusTv.setText(R.string.ssl_error); +// mLoginBtn.setEnabled(true); +// } +// }); +// sslConfirmDialog.show(getSupportFragmentManager(), SslConfirmDialog.FRAGMENT_TAG); } else if (err == SeafException.twoFactorAuthTokenMissing) { // show auth token input box mAuthTokenInputLayout.setVisibility(View.VISIBLE); diff --git a/app/src/main/java/com/seafile/seadroid2/ui/account/AccountViewModel.java b/app/src/main/java/com/seafile/seadroid2/ui/account/AccountViewModel.java index 8185c7b6f..0a0aa3b02 100644 --- a/app/src/main/java/com/seafile/seadroid2/ui/account/AccountViewModel.java +++ b/app/src/main/java/com/seafile/seadroid2/ui/account/AccountViewModel.java @@ -14,7 +14,7 @@ import com.seafile.seadroid2.framework.data.ServerInfo; import com.seafile.seadroid2.framework.data.model.TokenModel; import com.seafile.seadroid2.framework.data.model.server.ServerInfoModel; -import com.seafile.seadroid2.framework.http.IO; +import com.seafile.seadroid2.framework.http.HttpIO; import com.seafile.seadroid2.framework.util.AccountUtils; import com.seafile.seadroid2.framework.util.DeviceIdManager; import com.seafile.seadroid2.ui.base.viewmodel.BaseViewModel; @@ -59,7 +59,7 @@ public void loadAccountInfo(Account loginAccount, String authToken) { getRefreshLiveData().setValue(true); loginAccount.token = authToken; - Single single = IO.getInstanceByAccount(loginAccount).execute(AccountService.class).getAccountInfo(); + Single single = HttpIO.getInstanceByAccount(loginAccount).execute(AccountService.class).getAccountInfo(); addSingleDisposable(single, new Consumer() { @Override public void accept(AccountInfo accountInfo) throws Exception { @@ -108,14 +108,8 @@ public void subscribe(SingleEmitter emitter) throws Exception { tempAccount.token = tokenModel.token; } - //note this: - //the token to be updated here, because the user is logged in successfully - //but don't use "IO.getLoggedInAccountInstance();" - //because not set up an Android Account yet. - IO.updateInstanceByAccount(tempAccount); - // - retrofit2.Response accountInfoResponse = IO + retrofit2.Response accountInfoResponse = HttpIO .getInstanceByAccount(tempAccount) //Still use it that way .execute(AccountService.class) .getAccountInfoCall() @@ -189,19 +183,16 @@ private Call getLoginCall(Account tempAccount, String pwd, String to Map requestBody = generateRequestBody(body); - // - IO.removeInstanceByAccount(tempAccount); - - return IO.newInstanceByAccount(tempAccount).execute(AccountService.class).login(headers, requestBody); + return HttpIO.getInstanceByAccount(tempAccount).execute(AccountService.class).login(headers, requestBody); } public void getServerInfo() { getRefreshLiveData().setValue(true); - Single serverSingle = IO.getInstanceWithLoggedIn().execute(MainService.class).getServerInfo(); + Single serverSingle = HttpIO.getCurrentInstance().execute(MainService.class).getServerInfo(); addSingleDisposable(serverSingle, new Consumer() { @Override - public void accept(ServerInfoModel serverInfoModel) throws Exception { + public void accept(ServerInfoModel serverInfoModel) { Account account = SupportAccountManager.getInstance().getCurrentAccount(); if (account == null) { diff --git a/app/src/main/java/com/seafile/seadroid2/ui/account/SeafileAuthenticatorActivity.java b/app/src/main/java/com/seafile/seadroid2/ui/account/SeafileAuthenticatorActivity.java index 543c8fd59..9c5c25152 100644 --- a/app/src/main/java/com/seafile/seadroid2/ui/account/SeafileAuthenticatorActivity.java +++ b/app/src/main/java/com/seafile/seadroid2/ui/account/SeafileAuthenticatorActivity.java @@ -18,25 +18,31 @@ import androidx.activity.result.ActivityResultCallback; import androidx.activity.result.ActivityResultLauncher; import androidx.activity.result.contract.ActivityResultContracts; +import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.appcompat.widget.Toolbar; import androidx.core.app.ComponentActivity; import androidx.core.app.NavUtils; import androidx.core.app.TaskStackBuilder; +import androidx.preference.Preference; import com.blankj.utilcode.util.ToastUtils; import com.google.firebase.analytics.FirebaseAnalytics; import com.seafile.seadroid2.R; import com.seafile.seadroid2.account.Authenticator; import com.seafile.seadroid2.account.SupportAccountManager; +import com.seafile.seadroid2.framework.datastore.sp.SettingsManager; import com.seafile.seadroid2.framework.util.Objs; +import com.seafile.seadroid2.framework.util.SLogs; import com.seafile.seadroid2.ui.WidgetUtils; import com.seafile.seadroid2.ui.camera_upload.CameraUploadManager; import com.seafile.seadroid2.config.Constants; import com.seafile.seadroid2.ui.markdown.MarkdownActivity; import com.seafile.seadroid2.ui.repo.RepoQuickFragment; +import com.seafile.seadroid2.ui.webview.SeaWebViewActivity; import java.io.File; +import java.util.Locale; /** * The Authenticator activity. @@ -78,12 +84,22 @@ public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.account_create_type_select); + + String country = Locale.getDefault().getCountry(); + String language = Locale.getDefault().getLanguage(); + boolean isZH = TextUtils.equals("CN", country) || TextUtils.equals("zh", language); + String[] array = getResources().getStringArray(R.array.choose_server_array); - String[] strArray = new String[1 + array.length]; - strArray[0] = getString(R.string.server_name_top); - for (int i = 0; i < array.length; i++) { - strArray[i + 1] = array[i]; + String[] strArray; + if (isZH) { + strArray = new String[1 + array.length]; + strArray[0] = getString(R.string.server_name_top); + System.arraycopy(array, 0, strArray, 1, array.length); + } else { + strArray = new String[array.length]; + System.arraycopy(array, 0, strArray, 0, array.length); } + ArrayAdapter listAdapter = new ArrayAdapter<>(this, R.layout.list_item_authenticator, strArray); ListView listView = (ListView) findViewById(R.id.account_create_list); listView.setAdapter(listAdapter); @@ -91,11 +107,15 @@ public void onCreate(Bundle savedInstanceState) { @Override public void onItemClick(AdapterView parent, View view, int position, long id) { Intent intent = null; + + if (!isZH) { + id++; + } + if (id == SEACLOUD_CC) { intent = new Intent(SeafileAuthenticatorActivity.this, AccountDetailActivity.class); intent.putExtras(getIntent()); intent.putExtra(SeafileAuthenticatorActivity.ARG_SERVER_URI, getString(R.string.server_url_seacloud)); - } else if (id == SINGLE_SIGN_ON_LOGIN) { intent = new Intent(SeafileAuthenticatorActivity.this, SingleSignOnActivity.class); intent.putExtras(getIntent()); @@ -104,6 +124,7 @@ public void onItemClick(AdapterView parent, View view, int position, long id) intent.putExtras(getIntent()); } + if (intent != null) { activityLauncher.launch(intent); } @@ -212,7 +233,7 @@ public void onActivityResult(ActivityResult o) { }); private void finishLogin(Intent intent) { - Log.d(DEBUG_TAG, "finishLogin"); + SLogs.d(DEBUG_TAG, "finishLogin"); //firebase - event -login Bundle eventBundle = new Bundle(); diff --git a/app/src/main/java/com/seafile/seadroid2/ui/account/SingleSignOnAuthorizeActivity.java b/app/src/main/java/com/seafile/seadroid2/ui/account/SingleSignOnAuthorizeActivity.java index cad07407f..a4e616ed8 100644 --- a/app/src/main/java/com/seafile/seadroid2/ui/account/SingleSignOnAuthorizeActivity.java +++ b/app/src/main/java/com/seafile/seadroid2/ui/account/SingleSignOnAuthorizeActivity.java @@ -2,6 +2,7 @@ import android.annotation.SuppressLint; import android.content.Context; +import android.content.DialogInterface; import android.content.Intent; import android.content.pm.PackageInfo; import android.content.pm.PackageManager; @@ -26,6 +27,7 @@ import android.widget.LinearLayout; import com.blankj.utilcode.util.ToastUtils; +import com.google.android.material.dialog.MaterialAlertDialogBuilder; import com.seafile.seadroid2.R; import com.seafile.seadroid2.SeadroidApplication; import com.seafile.seadroid2.SeafException; @@ -235,33 +237,49 @@ public void onReceivedError(WebView view, int errorCode, String description, Str @Override public void onReceivedSslError(WebView view, final SslErrorHandler handler, SslError error) { Log.d(DEBUG_TAG, "onReceivedSslError " + error.getCertificate().toString()); - final Account account = new Account(serverUrl, null, null, null, null, false); - SslCertificate sslCert = error.getCertificate(); - X509Certificate savedCert = CertsManager.instance().getCertificate(account); - - if (Utils.isSameCert(sslCert, savedCert)) { - Log.d(DEBUG_TAG, "trust this cert"); - handler.proceed(); - } else { - Log.d(DEBUG_TAG, "cert is not trusted"); - SslConfirmDialog dialog = new SslConfirmDialog(account, - Utils.getX509CertFromSslCertHack(sslCert), - new SslConfirmDialog.Listener() { - @Override - public void onAccepted(boolean rememberChoice) { - CertsManager.instance().saveCertForAccount(account, rememberChoice); - // Ignore SSL certificate validate - handler.proceed(); - } - - @Override - public void onRejected() { - displaySSLError(); - handler.cancel(); - } - }); - dialog.show(getSupportFragmentManager(), SslConfirmDialog.FRAGMENT_TAG); - } + + MaterialAlertDialogBuilder builder = new MaterialAlertDialogBuilder(SingleSignOnAuthorizeActivity.this); + builder.setTitle(R.string.ssl_confirm_title); + builder.setMessage(getString(R.string.ssl_not_trusted, serverUrl)); + builder.setPositiveButton(R.string.ok, new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + handler.cancel(); + displaySSLError(); + dialog.dismiss(); + } + }); + builder.create().show(); + +// final Account account = new Account(serverUrl, null, null, null, null, false); +// SslCertificate sslCert = error.getCertificate(); +// +// //todo notice main thread +// X509Certificate savedCert = CertsManager.instance().getCertificate(account); +// +// if (Utils.isSameCert(sslCert, savedCert)) { +// Log.d(DEBUG_TAG, "trust this cert"); +// handler.proceed(); +// } else { +// Log.d(DEBUG_TAG, "cert is not trusted"); +// SslConfirmDialog dialog = new SslConfirmDialog(account, +// Utils.getX509CertFromSslCertHack(sslCert), +// new SslConfirmDialog.Listener() { +// @Override +// public void onAccepted(boolean rememberChoice) { +// CertsManager.instance().saveCertForAccount(account, rememberChoice); +// // Ignore SSL certificate validate +// handler.proceed(); +// } +// +// @Override +// public void onRejected() { +// displaySSLError(); +// handler.cancel(); +// } +// }); +// dialog.show(getSupportFragmentManager(), SslConfirmDialog.FRAGMENT_TAG); +// } } @Override diff --git a/app/src/main/java/com/seafile/seadroid2/ui/account/adapter/AccountAdapter.java b/app/src/main/java/com/seafile/seadroid2/ui/account/adapter/AccountAdapter.java index d3ba3826c..b1bdf89d0 100644 --- a/app/src/main/java/com/seafile/seadroid2/ui/account/adapter/AccountAdapter.java +++ b/app/src/main/java/com/seafile/seadroid2/ui/account/adapter/AccountAdapter.java @@ -9,11 +9,11 @@ import android.widget.ImageView; import android.widget.TextView; -import com.bumptech.glide.Glide; import com.google.common.collect.Lists; import com.seafile.seadroid2.R; import com.seafile.seadroid2.account.Account; import com.seafile.seadroid2.config.GlideLoadConfig; +import com.seafile.seadroid2.framework.util.GlideApp; import java.util.ArrayList; import java.util.List; @@ -95,9 +95,9 @@ public View getView(int position, View convertView, ViewGroup parent) { if (TextUtils.isEmpty(account.avatar_url)) { viewHolder.icon.setImageResource(com.seafile.seadroid2.R.drawable.default_avatar); } else { - Glide.with(context) - .load(GlideLoadConfig.getGlideUrl(account.avatar_url)) - .apply(GlideLoadConfig.getDefaultAvatarOptions()) + GlideApp.with(context) + .load(account.avatar_url) + .apply(GlideLoadConfig.getAvatarOptions()) .into(viewHolder.icon); } diff --git a/app/src/main/java/com/seafile/seadroid2/ui/activities/ActivityAdapter.java b/app/src/main/java/com/seafile/seadroid2/ui/activities/ActivityAdapter.java index fe0a7572b..e40fcaabe 100644 --- a/app/src/main/java/com/seafile/seadroid2/ui/activities/ActivityAdapter.java +++ b/app/src/main/java/com/seafile/seadroid2/ui/activities/ActivityAdapter.java @@ -10,7 +10,6 @@ import androidx.recyclerview.widget.RecyclerView; import com.blankj.utilcode.util.SpanUtils; -import com.bumptech.glide.Glide; import com.seafile.seadroid2.R; import com.seafile.seadroid2.config.AbsLayoutItemType; import com.seafile.seadroid2.config.GlideLoadConfig; @@ -19,6 +18,7 @@ import com.seafile.seadroid2.framework.data.model.BaseModel; import com.seafile.seadroid2.framework.data.model.GroupItemModel; import com.seafile.seadroid2.framework.data.model.activities.ActivityModel; +import com.seafile.seadroid2.framework.util.GlideApp; import com.seafile.seadroid2.framework.util.SystemSwitchUtils; import com.seafile.seadroid2.ui.base.adapter.BaseMultiAdapter; import com.seafile.seadroid2.ui.viewholder.GroupItemViewHolder; @@ -95,9 +95,9 @@ private void onBindActivity(ActivityViewHolder holder, ActivityModel model) { } } - Glide.with(getContext()) - .load(GlideLoadConfig.getGlideUrl(model.avatar_url)) - .apply(GlideLoadConfig.getOptions()) + GlideApp.with(getContext()) + .load(model.avatar_url) + .apply(GlideLoadConfig.getAvatarOptions()) .into(holder.binding.itemAvatar); } } diff --git a/app/src/main/java/com/seafile/seadroid2/ui/activities/ActivityViewModel.java b/app/src/main/java/com/seafile/seadroid2/ui/activities/ActivityViewModel.java index 8e85f1021..c657b7e08 100644 --- a/app/src/main/java/com/seafile/seadroid2/ui/activities/ActivityViewModel.java +++ b/app/src/main/java/com/seafile/seadroid2/ui/activities/ActivityViewModel.java @@ -3,15 +3,12 @@ import androidx.lifecycle.MutableLiveData; import com.blankj.utilcode.util.CollectionUtils; -import com.blankj.utilcode.util.NetworkUtils; -import com.blankj.utilcode.util.ToastUtils; -import com.seafile.seadroid2.R; import com.seafile.seadroid2.SeafException; import com.seafile.seadroid2.framework.data.db.AppDatabase; import com.seafile.seadroid2.framework.data.db.entities.RepoModel; import com.seafile.seadroid2.ui.base.viewmodel.BaseViewModel; import com.seafile.seadroid2.enums.OpType; -import com.seafile.seadroid2.framework.http.IO; +import com.seafile.seadroid2.framework.http.HttpIO; import com.seafile.seadroid2.framework.data.model.activities.ActivityModel; import com.seafile.seadroid2.framework.data.model.activities.ActivityWrapperModel; import com.seafile.seadroid2.framework.util.SLogs; @@ -20,7 +17,6 @@ import io.reactivex.Single; import io.reactivex.functions.Consumer; -import kotlin.Pair; public class ActivityViewModel extends BaseViewModel { private final MutableLiveData> listLiveData = new MutableLiveData<>(); @@ -52,7 +48,7 @@ public void accept(Throwable throwable) throws Exception { public void loadAllData(int page) { getRefreshLiveData().setValue(true); - Single flowable = IO.getInstanceWithLoggedIn().execute(ActivityService.class).getActivities(page); + Single flowable = HttpIO.getCurrentInstance().execute(ActivityService.class).getActivities(page); addSingleDisposable(flowable, new Consumer() { @Override public void accept(ActivityWrapperModel wrapperModel) throws Exception { diff --git a/app/src/main/java/com/seafile/seadroid2/ui/base/fragment/BaseFragment.java b/app/src/main/java/com/seafile/seadroid2/ui/base/fragment/BaseFragment.java index fc1de61a4..3de25e4de 100644 --- a/app/src/main/java/com/seafile/seadroid2/ui/base/fragment/BaseFragment.java +++ b/app/src/main/java/com/seafile/seadroid2/ui/base/fragment/BaseFragment.java @@ -18,7 +18,7 @@ public void onResume() { isFirstLoadData = false; onFirstResume(); } else { - onNonFirstResume(); + onOtherResume(); } } @@ -26,7 +26,7 @@ public void onFirstResume() { } - public void onNonFirstResume() { + public void onOtherResume() { } } diff --git a/app/src/main/java/com/seafile/seadroid2/ui/base/viewmodel/BaseViewModel.java b/app/src/main/java/com/seafile/seadroid2/ui/base/viewmodel/BaseViewModel.java index ce5dee2b2..c9e086bac 100644 --- a/app/src/main/java/com/seafile/seadroid2/ui/base/viewmodel/BaseViewModel.java +++ b/app/src/main/java/com/seafile/seadroid2/ui/base/viewmodel/BaseViewModel.java @@ -14,7 +14,7 @@ import com.seafile.seadroid2.SeadroidApplication; import com.seafile.seadroid2.SeafException; import com.seafile.seadroid2.framework.data.model.ResultModel; -import com.seafile.seadroid2.framework.http.IO; +import com.seafile.seadroid2.framework.http.HttpIO; import com.seafile.seadroid2.framework.util.SLogs; import com.seafile.seadroid2.framework.util.Utils; import com.seafile.seadroid2.ui.account.AccountService; @@ -23,13 +23,14 @@ import java.io.IOException; import java.io.UnsupportedEncodingException; -import java.net.ConnectException; import java.net.HttpURLConnection; import java.net.SocketTimeoutException; import java.util.HashMap; import java.util.Map; +import javax.net.ssl.SSLException; import javax.net.ssl.SSLHandshakeException; +import javax.net.ssl.SSLPeerUnverifiedException; import io.reactivex.Completable; import io.reactivex.Flowable; @@ -41,7 +42,6 @@ import io.reactivex.functions.Consumer; import io.reactivex.schedulers.Schedulers; import kotlin.Pair; -import okhttp3.Headers; import okhttp3.MediaType; import okhttp3.RequestBody; import okhttp3.ResponseBody; @@ -77,7 +77,7 @@ public void closeRefresh() { private final CompositeDisposable compositeDisposable = new CompositeDisposable(); public void completeRemoteWipe() { - Single single = IO.getInstanceWithLoggedIn().execute(AccountService.class).deviceWiped(); + Single single = HttpIO.getCurrentInstance().execute(AccountService.class).deviceWiped(); addSingleDisposable(single, new Consumer() { @Override public void accept(Object o) throws Exception { @@ -300,6 +300,10 @@ public SeafException getExceptionByThrowable(Throwable throwable) throws IOExcep SocketTimeoutException socketTimeoutException = (SocketTimeoutException) throwable; SLogs.e(socketTimeoutException.getMessage()); return SeafException.networkException; + } else if (throwable instanceof SSLPeerUnverifiedException) { + return SeafException.sslException; + } else if (throwable instanceof SSLException) { + return SeafException.sslException; } return new SeafException(SeafException.CODE_ERROR, throwable.getMessage()); diff --git a/app/src/main/java/com/seafile/seadroid2/ui/camera_upload/config_fragment/BucketsFragment.java b/app/src/main/java/com/seafile/seadroid2/ui/camera_upload/config_fragment/BucketsFragment.java index 16956cc6d..2726a2e60 100644 --- a/app/src/main/java/com/seafile/seadroid2/ui/camera_upload/config_fragment/BucketsFragment.java +++ b/app/src/main/java/com/seafile/seadroid2/ui/camera_upload/config_fragment/BucketsFragment.java @@ -2,9 +2,6 @@ import android.content.Context; import android.os.Bundle; - -import androidx.fragment.app.Fragment; - import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; @@ -19,9 +16,11 @@ import android.widget.RadioGroup.OnCheckedChangeListener; import android.widget.TextView; -import com.bumptech.glide.Glide; +import androidx.fragment.app.Fragment; + import com.seafile.seadroid2.R; import com.seafile.seadroid2.framework.datastore.sp.AlbumBackupManager; +import com.seafile.seadroid2.framework.util.GlideApp; import com.seafile.seadroid2.ui.camera_upload.CameraUploadConfigActivity; import com.seafile.seadroid2.ui.camera_upload.GalleryBucketUtils; @@ -213,7 +212,7 @@ public void onClick(View v) { } }); - Glide.with(requireContext()).load(buckets.get(position).uri).into(holder.imageview); + GlideApp.with(requireContext()).load(buckets.get(position).uri).into(holder.imageview); if (selectedBuckets[position]) holder.marking.setBackgroundResource(R.drawable.checkbox_checked); diff --git a/app/src/main/java/com/seafile/seadroid2/ui/data_migrate/DataMigrationActivity.java b/app/src/main/java/com/seafile/seadroid2/ui/data_migrate/DataMigrationActivity.java index 965424903..2a49504d1 100644 --- a/app/src/main/java/com/seafile/seadroid2/ui/data_migrate/DataMigrationActivity.java +++ b/app/src/main/java/com/seafile/seadroid2/ui/data_migrate/DataMigrationActivity.java @@ -45,7 +45,7 @@ import com.seafile.seadroid2.framework.datastore.sp.AppDataManager; import com.seafile.seadroid2.framework.datastore.sp.FolderBackupManager; import com.seafile.seadroid2.framework.datastore.sp.SettingsManager; -import com.seafile.seadroid2.framework.http.IO; +import com.seafile.seadroid2.framework.http.HttpIO; import com.seafile.seadroid2.framework.util.SLogs; import com.seafile.seadroid2.framework.util.Utils; import com.seafile.seadroid2.framework.worker.ExistingFileStrategy; @@ -153,7 +153,7 @@ private void syncAccount() throws IOException { continue; } - Call accountInfoCall = IO.getInstanceByAccount(account).execute(AccountService.class).getAccountInfoCall(); + Call accountInfoCall = HttpIO.getInstanceByAccount(account).execute(AccountService.class).getAccountInfoCall(); Response accountRes = accountInfoCall.execute(); if (accountRes.isSuccessful()) { AccountInfo accountInfo = accountRes.body(); @@ -165,7 +165,7 @@ private void syncAccount() throws IOException { } - Response response = IO.getInstanceByAccount(account) + Response response = HttpIO.getInstanceByAccount(account) .execute(RepoService.class) .getReposCall() .execute(); diff --git a/app/src/main/java/com/seafile/seadroid2/ui/dialog_fragment/SignOutDialogFragment.java b/app/src/main/java/com/seafile/seadroid2/ui/dialog_fragment/SignOutDialogFragment.java index b9736e74e..14ef706e6 100644 --- a/app/src/main/java/com/seafile/seadroid2/ui/dialog_fragment/SignOutDialogFragment.java +++ b/app/src/main/java/com/seafile/seadroid2/ui/dialog_fragment/SignOutDialogFragment.java @@ -9,17 +9,8 @@ import com.seafile.seadroid2.account.Account; import com.seafile.seadroid2.account.SupportAccountManager; import com.seafile.seadroid2.config.AnalyticsEvent; -import com.seafile.seadroid2.framework.datastore.DataStoreManager; -import com.seafile.seadroid2.framework.datastore.sp.AlbumBackupManager; -import com.seafile.seadroid2.framework.datastore.sp.FolderBackupManager; -import com.seafile.seadroid2.framework.datastore.sp.GestureLockManager; -import com.seafile.seadroid2.framework.http.IO; import com.seafile.seadroid2.framework.util.AccountUtils; -import com.seafile.seadroid2.framework.worker.BackgroundJobManagerImpl; import com.seafile.seadroid2.ui.base.fragment.CustomDialogFragment; -import com.seafile.seadroid2.ui.camera_upload.CameraUploadManager; - -import java.util.List; public class SignOutDialogFragment extends CustomDialogFragment { public static SignOutDialogFragment newInstance() { diff --git a/app/src/main/java/com/seafile/seadroid2/ui/dialog_fragment/viewmodel/CopyMoveViewModel.java b/app/src/main/java/com/seafile/seadroid2/ui/dialog_fragment/viewmodel/CopyMoveViewModel.java index 14e48ff77..74d1652ff 100644 --- a/app/src/main/java/com/seafile/seadroid2/ui/dialog_fragment/viewmodel/CopyMoveViewModel.java +++ b/app/src/main/java/com/seafile/seadroid2/ui/dialog_fragment/viewmodel/CopyMoveViewModel.java @@ -5,7 +5,7 @@ import com.seafile.seadroid2.ui.base.viewmodel.BaseViewModel; import com.seafile.seadroid2.framework.data.model.ResultModel; import com.seafile.seadroid2.framework.data.db.entities.DirentModel; -import com.seafile.seadroid2.framework.http.IO; +import com.seafile.seadroid2.framework.http.HttpIO; import com.seafile.seadroid2.ui.dialog_fragment.DialogService; import java.util.HashMap; @@ -35,7 +35,7 @@ public void move(String dstParentDir, String dstRepoId, String srcParentDir, Str requestDataMap.put("src_repo_id", srcRepoId); requestDataMap.put("src_dirents", nameList); - Single single = IO.getInstanceWithLoggedIn().execute(DialogService.class).moveDirents(requestDataMap); + Single single = HttpIO.getCurrentInstance().execute(DialogService.class).moveDirents(requestDataMap); addSingleDisposable(single, new Consumer() { @Override public void accept(ResultModel resultModel) throws Exception { @@ -57,7 +57,7 @@ public void copy(String dstParentDir, String dstRepoId, String srcParentDir, Str requestDataMap.put("src_repo_id", srcRepoId); requestDataMap.put("src_dirents", nameList); - Single single = IO.getInstanceWithLoggedIn().execute(DialogService.class).copyDirents(requestDataMap); + Single single = HttpIO.getCurrentInstance().execute(DialogService.class).copyDirents(requestDataMap); addSingleDisposable(single, new Consumer() { @Override public void accept(ResultModel resultModel) throws Exception { diff --git a/app/src/main/java/com/seafile/seadroid2/ui/dialog_fragment/viewmodel/DeleteDirsViewModel.java b/app/src/main/java/com/seafile/seadroid2/ui/dialog_fragment/viewmodel/DeleteDirsViewModel.java index 10bd5b38b..b9a937d73 100644 --- a/app/src/main/java/com/seafile/seadroid2/ui/dialog_fragment/viewmodel/DeleteDirsViewModel.java +++ b/app/src/main/java/com/seafile/seadroid2/ui/dialog_fragment/viewmodel/DeleteDirsViewModel.java @@ -10,7 +10,7 @@ import com.seafile.seadroid2.framework.data.db.entities.FileTransferEntity; import com.seafile.seadroid2.framework.data.model.dirents.DeleteDirentModel; import com.seafile.seadroid2.framework.data.model.enums.TransferAction; -import com.seafile.seadroid2.framework.http.IO; +import com.seafile.seadroid2.framework.http.HttpIO; import com.seafile.seadroid2.framework.util.SLogs; import com.seafile.seadroid2.ui.base.viewmodel.BaseViewModel; import com.seafile.seadroid2.ui.dialog_fragment.DialogService; @@ -44,7 +44,7 @@ public void deleteDirents(String related_account, List dirents, boo @Override public SingleSource apply(DirentModel dirent) throws Exception { String obj = dirent.isDir() ? "dir" : "file"; - return IO.getInstanceWithLoggedIn().execute(DialogService.class).deleteDirent(dirent.repo_id, obj, dirent.full_path); + return HttpIO.getCurrentInstance().execute(DialogService.class).deleteDirent(dirent.repo_id, obj, dirent.full_path); } }); } else { @@ -90,7 +90,7 @@ public SingleSource apply(String filePath) throws Exception { } String obj = dirent.isDir() ? "dir" : "file"; - return IO.getInstanceWithLoggedIn().execute(DialogService.class).deleteDirent(dirent.repo_id, obj, dirent.full_path); + return HttpIO.getCurrentInstance().execute(DialogService.class).deleteDirent(dirent.repo_id, obj, dirent.full_path); } }); } diff --git a/app/src/main/java/com/seafile/seadroid2/ui/dialog_fragment/viewmodel/DeleteRepoViewModel.java b/app/src/main/java/com/seafile/seadroid2/ui/dialog_fragment/viewmodel/DeleteRepoViewModel.java index 6e4b05c2b..cdb6c3e21 100644 --- a/app/src/main/java/com/seafile/seadroid2/ui/dialog_fragment/viewmodel/DeleteRepoViewModel.java +++ b/app/src/main/java/com/seafile/seadroid2/ui/dialog_fragment/viewmodel/DeleteRepoViewModel.java @@ -6,7 +6,7 @@ import com.seafile.seadroid2.ui.base.viewmodel.BaseViewModel; import com.seafile.seadroid2.framework.data.model.ResultModel; -import com.seafile.seadroid2.framework.http.IO; +import com.seafile.seadroid2.framework.http.HttpIO; import com.seafile.seadroid2.ui.dialog_fragment.DialogService; import io.reactivex.Single; @@ -22,7 +22,7 @@ public MutableLiveData getActionLiveData() { public void deleteRepo(String repoId) { getRefreshLiveData().setValue(true); - Single single = IO.getInstanceWithLoggedIn().execute(DialogService.class).deleteRepo(repoId); + Single single = HttpIO.getCurrentInstance().execute(DialogService.class).deleteRepo(repoId); addSingleDisposable(single, new Consumer() { @Override public void accept(String resultModel) throws Exception { diff --git a/app/src/main/java/com/seafile/seadroid2/ui/dialog_fragment/viewmodel/GetShareLinkPasswordViewModel.java b/app/src/main/java/com/seafile/seadroid2/ui/dialog_fragment/viewmodel/GetShareLinkPasswordViewModel.java index d7f143228..f4f3e12bf 100644 --- a/app/src/main/java/com/seafile/seadroid2/ui/dialog_fragment/viewmodel/GetShareLinkPasswordViewModel.java +++ b/app/src/main/java/com/seafile/seadroid2/ui/dialog_fragment/viewmodel/GetShareLinkPasswordViewModel.java @@ -10,7 +10,7 @@ import com.seafile.seadroid2.config.DateFormatType; import com.seafile.seadroid2.framework.data.model.dirents.DirentPermissionModel; import com.seafile.seadroid2.framework.data.model.objs.DirentShareLinkModel; -import com.seafile.seadroid2.framework.http.IO; +import com.seafile.seadroid2.framework.http.HttpIO; import com.seafile.seadroid2.ui.base.viewmodel.BaseViewModel; import com.seafile.seadroid2.ui.dialog_fragment.DialogService; @@ -32,7 +32,7 @@ public MutableLiveData getLinkLiveData() { public void getFirstShareLink(String repoId, String path, String password, String expire_days) { getRefreshLiveData().setValue(true); - Single> single = IO.getInstanceWithLoggedIn().execute(DialogService.class).listAllShareLink(repoId, path); + Single> single = HttpIO.getCurrentInstance().execute(DialogService.class).listAllShareLink(repoId, path); addSingleDisposable(single, new Consumer>() { @Override public void accept(List models) throws Exception { @@ -81,9 +81,9 @@ public void createShareLink(String repoId, String path, String password, String Single single; if (permissions != null) { requestDataMap.put("permissions", permissions); - single = IO.getInstanceWithLoggedIn().execute(DialogService.class).createMultiShareLink(requestDataMap); + single = HttpIO.getCurrentInstance().execute(DialogService.class).createMultiShareLink(requestDataMap); } else { - single = IO.getInstanceWithLoggedIn().execute(DialogService.class).createShareLink(requestDataMap); + single = HttpIO.getCurrentInstance().execute(DialogService.class).createShareLink(requestDataMap); } addSingleDisposable(single, new Consumer() { diff --git a/app/src/main/java/com/seafile/seadroid2/ui/dialog_fragment/viewmodel/NewDirViewModel.java b/app/src/main/java/com/seafile/seadroid2/ui/dialog_fragment/viewmodel/NewDirViewModel.java index 6eeb9e4b2..4fb497a1c 100644 --- a/app/src/main/java/com/seafile/seadroid2/ui/dialog_fragment/viewmodel/NewDirViewModel.java +++ b/app/src/main/java/com/seafile/seadroid2/ui/dialog_fragment/viewmodel/NewDirViewModel.java @@ -7,7 +7,7 @@ import com.seafile.seadroid2.ui.base.viewmodel.BaseViewModel; import com.seafile.seadroid2.framework.data.model.ResultModel; import com.seafile.seadroid2.framework.data.model.dirents.FileCreateModel; -import com.seafile.seadroid2.framework.http.IO; +import com.seafile.seadroid2.framework.http.HttpIO; import com.seafile.seadroid2.ui.dialog_fragment.DialogService; import java.util.HashMap; @@ -41,7 +41,7 @@ public void createNewDir(String p, String repo_id) { Map bodyMap = generateRequestBody(requestDataMap); - Single single = IO.getInstanceWithLoggedIn().execute(DialogService.class).createDir(repo_id, p, bodyMap); + Single single = HttpIO.getCurrentInstance().execute(DialogService.class).createDir(repo_id, p, bodyMap); addSingleDisposable(single, new Consumer() { @Override public void accept(String resultModel) throws Exception { @@ -79,7 +79,7 @@ public void createNewFile(String filePathName, String repo_id) { Map bodyMap = generateRequestBody(requestDataMap); - Single single = IO.getInstanceWithLoggedIn().execute(DialogService.class).createFile(repo_id, filePathName, bodyMap); + Single single = HttpIO.getCurrentInstance().execute(DialogService.class).createFile(repo_id, filePathName, bodyMap); addSingleDisposable(single, new Consumer() { @Override public void accept(FileCreateModel resultModel) throws Exception { diff --git a/app/src/main/java/com/seafile/seadroid2/ui/dialog_fragment/viewmodel/NewRepoViewModel.java b/app/src/main/java/com/seafile/seadroid2/ui/dialog_fragment/viewmodel/NewRepoViewModel.java index b2334366a..09958fa5c 100644 --- a/app/src/main/java/com/seafile/seadroid2/ui/dialog_fragment/viewmodel/NewRepoViewModel.java +++ b/app/src/main/java/com/seafile/seadroid2/ui/dialog_fragment/viewmodel/NewRepoViewModel.java @@ -6,7 +6,7 @@ import com.seafile.seadroid2.ui.base.viewmodel.BaseViewModel; import com.seafile.seadroid2.framework.data.db.entities.RepoModel; -import com.seafile.seadroid2.framework.http.IO; +import com.seafile.seadroid2.framework.http.HttpIO; import com.seafile.seadroid2.ui.dialog_fragment.DialogService; import java.util.HashMap; @@ -41,7 +41,7 @@ public void createNewRepo(String repoName, String description, String password) requestDataMap.put("passwd", password); } Map bodyMap = generateRequestBody(requestDataMap); - Single single = IO.getInstanceWithLoggedIn().execute(DialogService.class).createRepo(bodyMap); + Single single = HttpIO.getCurrentInstance().execute(DialogService.class).createRepo(bodyMap); addSingleDisposable(single, new Consumer() { @Override public void accept(RepoModel repoModel) throws Exception { diff --git a/app/src/main/java/com/seafile/seadroid2/ui/dialog_fragment/viewmodel/PasswordViewModel.java b/app/src/main/java/com/seafile/seadroid2/ui/dialog_fragment/viewmodel/PasswordViewModel.java index 523620a9d..61642b09c 100644 --- a/app/src/main/java/com/seafile/seadroid2/ui/dialog_fragment/viewmodel/PasswordViewModel.java +++ b/app/src/main/java/com/seafile/seadroid2/ui/dialog_fragment/viewmodel/PasswordViewModel.java @@ -16,7 +16,7 @@ import com.seafile.seadroid2.framework.data.model.TResultModel; import com.seafile.seadroid2.framework.datastore.DataManager; import com.seafile.seadroid2.framework.datastore.sp.SettingsManager; -import com.seafile.seadroid2.framework.http.IO; +import com.seafile.seadroid2.framework.http.HttpIO; import com.seafile.seadroid2.ui.base.viewmodel.BaseViewModel; import com.seafile.seadroid2.ui.dialog_fragment.DialogService; import com.seafile.seadroid2.ui.repo.RepoService; @@ -70,7 +70,7 @@ public void accept(RepoModel uRepoModel) throws Exception { } public void getRepoModel(String repoId, Consumer consumer) { - Single singleNet = IO.getInstanceWithLoggedIn().execute(RepoService.class).getRepoInfo(repoId); + Single singleNet = HttpIO.getCurrentInstance().execute(RepoService.class).getRepoInfo(repoId); Single> singleDb = AppDatabase.getInstance().repoDao().getByIdAsync(repoId); @@ -137,7 +137,7 @@ private void remoteVerify(RepoModel repoModel, String password) { requestDataMap.put("password", password); Map bodyMap = generateRequestBody(requestDataMap); - Single netSingle = IO.getInstanceWithLoggedIn().execute(DialogService.class).setPassword(repoModel.repo_id, bodyMap); + Single netSingle = HttpIO.getCurrentInstance().execute(DialogService.class).setPassword(repoModel.repo_id, bodyMap); Single insertEncSingle = Single.create(new SingleOnSubscribe() { @Override diff --git a/app/src/main/java/com/seafile/seadroid2/ui/dialog_fragment/viewmodel/RenameRepoViewModel.java b/app/src/main/java/com/seafile/seadroid2/ui/dialog_fragment/viewmodel/RenameRepoViewModel.java index ab3476cf4..13300d1d5 100644 --- a/app/src/main/java/com/seafile/seadroid2/ui/dialog_fragment/viewmodel/RenameRepoViewModel.java +++ b/app/src/main/java/com/seafile/seadroid2/ui/dialog_fragment/viewmodel/RenameRepoViewModel.java @@ -6,7 +6,7 @@ import com.seafile.seadroid2.ui.base.viewmodel.BaseViewModel; import com.seafile.seadroid2.framework.data.model.dirents.FileCreateModel; -import com.seafile.seadroid2.framework.http.IO; +import com.seafile.seadroid2.framework.http.HttpIO; import com.seafile.seadroid2.ui.dialog_fragment.DialogService; import java.util.HashMap; @@ -40,7 +40,7 @@ public void renameRepo(String repoName, String repoId) { requestDataMap.put("repo_name", repoName); Map bodyMap = generateRequestBody(requestDataMap); - Single single = IO.getInstanceWithLoggedIn().execute(DialogService.class).renameRepo(repoId, bodyMap); + Single single = HttpIO.getCurrentInstance().execute(DialogService.class).renameRepo(repoId, bodyMap); addSingleDisposable(single, new Consumer() { @Override @@ -66,7 +66,7 @@ public void renameDir(String repoId, String curPath, String newName) { requestDataMap.put("newname", newName); Map bodyMap = generateRequestBody(requestDataMap); - Single single = IO.getInstanceWithLoggedIn().execute(DialogService.class).renameDir(repoId, curPath, bodyMap); + Single single = HttpIO.getCurrentInstance().execute(DialogService.class).renameDir(repoId, curPath, bodyMap); addSingleDisposable(single, new Consumer() { @Override @@ -92,7 +92,7 @@ public void renameFile(String repoId, String curPath, String newName) { requestDataMap.put("newname", newName); Map bodyMap = generateRequestBody(requestDataMap); - Single single = IO.getInstanceWithLoggedIn().execute(DialogService.class).renameFile(repoId, curPath, bodyMap); + Single single = HttpIO.getCurrentInstance().execute(DialogService.class).renameFile(repoId, curPath, bodyMap); addSingleDisposable(single, new Consumer() { @Override diff --git a/app/src/main/java/com/seafile/seadroid2/ui/file/FileViewModel.java b/app/src/main/java/com/seafile/seadroid2/ui/file/FileViewModel.java index c291fb865..9811cb677 100644 --- a/app/src/main/java/com/seafile/seadroid2/ui/file/FileViewModel.java +++ b/app/src/main/java/com/seafile/seadroid2/ui/file/FileViewModel.java @@ -1,8 +1,5 @@ package com.seafile.seadroid2.ui.file; -import android.text.TextUtils; -import android.util.Pair; - import androidx.lifecycle.MutableLiveData; import com.blankj.utilcode.util.CollectionUtils; @@ -19,8 +16,7 @@ import com.seafile.seadroid2.framework.data.model.enums.TransferResult; import com.seafile.seadroid2.framework.data.model.enums.TransferStatus; import com.seafile.seadroid2.framework.datastore.DataManager; -import com.seafile.seadroid2.framework.http.IO; -import com.seafile.seadroid2.framework.worker.ExistingFileStrategy; +import com.seafile.seadroid2.framework.http.HttpIO; import com.seafile.seadroid2.ui.base.viewmodel.BaseViewModel; import com.seafile.seadroid2.framework.util.TransferUtils; import com.seafile.seadroid2.framework.worker.BackgroundJobManagerImpl; @@ -33,7 +29,6 @@ import io.reactivex.SingleEmitter; import io.reactivex.SingleOnSubscribe; import io.reactivex.functions.Action; -import io.reactivex.functions.BiFunction; import io.reactivex.functions.Consumer; import io.reactivex.functions.Function3; import kotlin.Triple; @@ -58,7 +53,7 @@ public MutableLiveData getCancelLiveData() { public void loadFileDetail(String repoId, String path, Consumer> consumer) { // get file detail - Single single = IO.getInstanceWithLoggedIn().execute(FileService.class).getFileDetail(repoId, path); + Single single = HttpIO.getCurrentInstance().execute(FileService.class).getFileDetail(repoId, path); // Single> repoListSingle = AppDatabase.getInstance().repoDao().getByIdAsync(repoId); @@ -158,7 +153,7 @@ public void accept(Boolean abool) throws Exception { public void download(Account account, DirentModel direntModel, File destinationFile) { - Single urlSingle = IO.getInstanceWithLoggedIn().execute(FileService.class).getFileDownloadLinkAsync(direntModel.repo_id, direntModel.full_path); + Single urlSingle = HttpIO.getCurrentInstance().execute(FileService.class).getFileDownloadLinkAsync(direntModel.repo_id, direntModel.full_path); addSingleDisposable(urlSingle, new Consumer() { @Override @@ -167,7 +162,7 @@ public void accept(String url) throws Exception { File tempFile = DataManager.createTempFile(); //start download - Flowable flowable = IO.getInstanceWithLoggedIn().downloadBinary(url, tempFile); + Flowable flowable = HttpIO.getCurrentInstance().downloadBinary(url, tempFile); addFlowableDisposable(flowable, new Consumer() { @Override public void accept(Long[] longs) throws Exception { diff --git a/app/src/main/java/com/seafile/seadroid2/ui/main/MainActivity.java b/app/src/main/java/com/seafile/seadroid2/ui/main/MainActivity.java index 1abc23681..96f5fe8d3 100644 --- a/app/src/main/java/com/seafile/seadroid2/ui/main/MainActivity.java +++ b/app/src/main/java/com/seafile/seadroid2/ui/main/MainActivity.java @@ -1,6 +1,7 @@ package com.seafile.seadroid2.ui.main; import android.Manifest; +import android.app.Dialog; import android.content.ComponentName; import android.content.Context; import android.content.DialogInterface; @@ -49,6 +50,7 @@ import com.seafile.seadroid2.framework.data.db.entities.EncKeyCacheEntity; import com.seafile.seadroid2.framework.data.db.entities.FileTransferEntity; import com.seafile.seadroid2.framework.data.db.entities.RepoModel; +import com.seafile.seadroid2.framework.data.model.dirents.DirentFileModel; import com.seafile.seadroid2.framework.util.PermissionUtil; import com.seafile.seadroid2.framework.util.SLogs; import com.seafile.seadroid2.framework.util.TakeCameras; @@ -60,7 +62,6 @@ import com.seafile.seadroid2.framework.worker.TransferEvent; import com.seafile.seadroid2.framework.worker.TransferWorker; import com.seafile.seadroid2.ui.account.AccountsActivity; -import com.seafile.seadroid2.ui.activities.AllActivitiesFragment; import com.seafile.seadroid2.ui.adapter.ViewPager2Adapter; import com.seafile.seadroid2.ui.base.BaseActivity; import com.seafile.seadroid2.ui.dialog_fragment.NewDirFileDialogFragment; @@ -75,6 +76,7 @@ import com.seafile.seadroid2.ui.transfer_list.TransferActivity; import java.io.File; +import java.io.IOException; import java.util.List; import java.util.Map; @@ -517,11 +519,7 @@ private void requestServerInfo(boolean loadFromNet) { } /** - * - * - * - * - * +|+++}}}}}}++}""""""""""""""""""""' ]\ * check if server is pro edition + * check if server is pro edition * * @return true, if server is pro edition * false, otherwise. @@ -857,7 +855,7 @@ private void pickFile() { return; } - takeFile(true); + takeFile(false); } ////////////////Launcher/////////////// @@ -956,9 +954,11 @@ public void onActivityResult(List o) { if (CollectionUtils.isEmpty(o)) { return; } - - Uri[] uris = o.toArray(new Uri[0]); - + if (o.size() == 1) { + doSelectSingleFile(o.get(0)); + } else { + doSelectedMultiFile(o); + } } }); @@ -998,32 +998,75 @@ public void onActivityResult(Boolean o) { } }); + private Dialog dialog; + + private void showProgressDialog() { + if (dialog == null) { + MaterialAlertDialogBuilder builder = new MaterialAlertDialogBuilder(this); + builder.setView(R.layout.layout_dialog_progress_bar); + dialog = builder.create(); + } + + if (dialog.isShowing()) { + dialog.dismiss(); + } + dialog.show(); + } + + private void dismissProgressDialog() { + if (dialog != null) { + dialog.dismiss(); + } + } ///////////////////////////// - private void doSelectSingleFile(Uri o) { + private void doSelectedMultiFile(List uriList) { + showProgressDialog(); + try { - new Thread(new Runnable() { + RepoModel repoModel = getNavContext().getRepoModel(); + String parent_dir = getNavContext().getNavPath(); + + mainViewModel.checkLocalDirent(curAccount, this, repoModel, parent_dir, uriList, new Consumer>() { @Override - public void run() { - RepoModel repoModel = getNavContext().getRepoModel(); + public void accept(List newUris) throws Exception { - String fileName = Utils.getFilenameFromUri(MainActivity.this, o); + dismissProgressDialog(); - mainViewModel.getDirentsFromServer(repoModel.repo_id, getNavContext().getNavPath(), new Consumer>() { - @Override - public void accept(List list) { - boolean duplicate = list.stream().anyMatch(p -> p.name.equals(fileName)); + if (!CollectionUtils.isEmpty(newUris)) { + ToastUtils.showLong(R.string.added_to_upload_tasks); - if (duplicate) { - showFileExistDialog(o, fileName); - } else { - addUploadTask(repoModel, getNavContext().getNavPath(), o, false); - } - } - }); + //start worker + BackgroundJobManagerImpl.getInstance().startFileUploadWorker(); + } + } + }); + + } catch (Exception e) { + SLogs.e("Could not open requested document", e); + } + } + + private void doSelectSingleFile(Uri uri) { + showProgressDialog(); + try { + String fileName = Utils.getFilenameFromUri(this, uri); + String parent_dir = getNavContext().getNavPath(); + String fullPath = Utils.pathJoin(parent_dir, fileName); + + RepoModel repoModel = getNavContext().getRepoModel(); + mainViewModel.checkRemoteDirent(this, repoModel.repo_id, fullPath, new Consumer() { + @Override + public void accept(DirentFileModel direntFileModel) throws Exception { + if (direntFileModel != null) { + showFileExistDialog(uri, fileName); + } else { + addUploadTask(repoModel, getNavContext().getNavPath(), uri, false); + } + dismissProgressDialog(); } - }).start(); + }); } catch (Exception e) { SLogs.e("Could not open requested document", e); } diff --git a/app/src/main/java/com/seafile/seadroid2/ui/main/MainViewModel.java b/app/src/main/java/com/seafile/seadroid2/ui/main/MainViewModel.java index 9b6f68ad3..fe5aa0cbf 100644 --- a/app/src/main/java/com/seafile/seadroid2/ui/main/MainViewModel.java +++ b/app/src/main/java/com/seafile/seadroid2/ui/main/MainViewModel.java @@ -16,11 +16,11 @@ import com.seafile.seadroid2.account.SupportAccountManager; import com.seafile.seadroid2.framework.data.db.entities.EncKeyCacheEntity; import com.seafile.seadroid2.framework.data.db.entities.FileTransferEntity; +import com.seafile.seadroid2.framework.data.model.dirents.DirentFileModel; import com.seafile.seadroid2.framework.data.model.enums.TransferAction; import com.seafile.seadroid2.framework.data.model.enums.TransferDataSource; import com.seafile.seadroid2.framework.data.model.enums.TransferResult; import com.seafile.seadroid2.framework.data.model.enums.TransferStatus; -import com.seafile.seadroid2.framework.data.model.repo.DirentWrapperModel; import com.seafile.seadroid2.framework.datastore.DataManager; import com.seafile.seadroid2.framework.util.Utils; import com.seafile.seadroid2.framework.worker.ExistingFileStrategy; @@ -32,18 +32,19 @@ import com.seafile.seadroid2.framework.data.db.entities.RepoModel; import com.seafile.seadroid2.framework.data.model.repo.RepoWrapperModel; import com.seafile.seadroid2.framework.data.model.server.ServerInfoModel; +import com.seafile.seadroid2.ui.file.FileService; import com.seafile.seadroid2.ui.repo.RepoService; -import com.seafile.seadroid2.framework.http.IO; +import com.seafile.seadroid2.framework.http.HttpIO; import com.seafile.seadroid2.ui.activities.AllActivitiesFragment; import com.seafile.seadroid2.ui.repo.RepoQuickFragment; import com.seafile.seadroid2.ui.star.StarredQuickFragment; -import com.seafile.seadroid2.framework.util.FileTools; import com.seafile.seadroid2.framework.util.SLogs; import com.seafile.seadroid2.framework.worker.BackgroundJobManagerImpl; import org.apache.commons.io.IOUtils; import java.io.File; +import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.nio.file.Files; @@ -131,7 +132,7 @@ public MainViewModel() { } public void getServerInfo() { - Single single = IO.getInstanceWithLoggedIn().execute(MainService.class).getServerInfo(); + Single single = HttpIO.getCurrentInstance().execute(MainService.class).getServerInfo(); addSingleDisposable(single, new Consumer() { @Override public void accept(ServerInfoModel serverInfo) throws Exception { @@ -179,7 +180,7 @@ public void accept(Throwable throwable) throws Exception { private void requestRepoModelFromServer(String repoId, Consumer consumer) { //from net - Single singleNet = IO.getInstanceWithLoggedIn().execute(RepoService.class).getRepos(); + Single singleNet = HttpIO.getCurrentInstance().execute(RepoService.class).getRepos(); addSingleDisposable(singleNet, new Consumer() { @Override public void accept(RepoWrapperModel repoWrapperModel) throws Exception { @@ -227,89 +228,151 @@ public void accept(List list) throws Exception { } - public void getDirentsFromServer(String repoId, String parent_dir, Consumer> consumer) { - Single singleServer = IO.getInstanceWithLoggedIn().execute(RepoService.class).getDirents(repoId, parent_dir); - addSingleDisposable(singleServer, new Consumer() { + public void checkLocalDirent(Account account, Context context, RepoModel repoModel, String parentDir, List uriList, Consumer> consumer) { + if (CollectionUtils.isEmpty(uriList)) { + return; + } + + Single> single = Single.create(new SingleOnSubscribe>() { @Override - public void accept(DirentWrapperModel direntWrapperModel) throws Exception { + public void subscribe(SingleEmitter> emitter) throws Exception { + + List newUriList = new ArrayList<>(); + + for (Uri uri : uriList) { + + String fileName = Utils.getFilenameFromUri(context, uri); + String fullPath = Utils.pathJoin(parentDir, fileName); + + List dirents = AppDatabase.getInstance().direntDao().getListByFullPathSync(repoModel.repo_id, fullPath); + if (!CollectionUtils.isEmpty(dirents)) { + continue; + } + + File destinationFile = copyFile(account, context, uri, repoModel.repo_id, repoModel.repo_name, false); + FileTransferEntity transferEntity = getUploadTransferEntity(account, repoModel, parentDir, destinationFile.getAbsolutePath(), false); + AppDatabase.getInstance().fileTransferDAO().insert(transferEntity); + + newUriList.add(uri); + } + + emitter.onSuccess(newUriList); + } + }); + + addSingleDisposable(single, new Consumer>() { + @Override + public void accept(List uris) throws Exception { if (consumer != null) { - consumer.accept(direntWrapperModel.dirent_list); + consumer.accept(uris); } } }); } - private Single copyFile(Account account, Context context, Uri o, String repo_id, String repo_name, boolean isUpdate) { - return Single.create(new SingleOnSubscribe() { + public void checkRemoteDirent(Context context, String repoId, String fullPath, Consumer consumer) throws IOException { + Single detailSingle = HttpIO.getCurrentInstance() + .execute(FileService.class) + .getFileDetail(repoId, fullPath); + addSingleDisposable(detailSingle, new Consumer() { @Override - public void subscribe(SingleEmitter emitter) throws Exception { - String fileName = Utils.getFilenameFromUri(context, o); - String p = getNavContext().getNavPath() + fileName; - - File destinationFile = DataManager.getLocalRepoFile(account, repo_id, repo_name, p); - if (destinationFile.exists() && isUpdate) { - destinationFile.delete(); + public void accept(DirentFileModel direntFileModel) throws Exception { + if (consumer != null) { + consumer.accept(direntFileModel); } - - try (InputStream in = context.getContentResolver().openInputStream(o); - OutputStream out = Files.newOutputStream(destinationFile.toPath())) { - IOUtils.copy(in, out); + } + }, new Consumer() { + @Override + public void accept(Throwable throwable) throws Exception { + if (consumer != null) { + consumer.accept(null); } + } + }); + } + + private File copyFile(Account account, Context context, Uri o, String repo_id, String repo_name, boolean isUpdate) throws IOException { + String fileName = Utils.getFilenameFromUri(context, o); + String p = getNavContext().getNavPath() + fileName; + + File destinationFile = DataManager.getLocalRepoFile(account, repo_id, repo_name, p); + if (destinationFile.exists() && isUpdate) { + destinationFile.delete(); + } + + try (InputStream in = context.getContentResolver().openInputStream(o); + OutputStream out = Files.newOutputStream(destinationFile.toPath())) { + IOUtils.copy(in, out); + } + + return destinationFile; + } + + private Single getCopyFileSingle(Account account, Context context, Uri o, String repo_id, String repo_name, boolean isUpdate) { + return Single.create(new SingleOnSubscribe() { + @Override + public void subscribe(SingleEmitter emitter) throws Exception { + File destinationFile = copyFile(account, context, o, repo_id, repo_name, isUpdate); emitter.onSuccess(destinationFile); } }); } - public void addUploadTask(Account account, Context context, RepoModel repoModel, String targetDir, Uri sourceUri, boolean isReplace, Consumer consumer) { + public void addUploadTask(Account account, Context context, RepoModel repoModel, String parentDir, Uri sourceUri, boolean isReplace, Consumer consumer) { ToastUtils.showLong(R.string.upload_waiting); - Single single = copyFile(account, context, sourceUri, repoModel.repo_id, repoModel.repo_name, isReplace); + Single single = getCopyFileSingle(account, context, sourceUri, repoModel.repo_id, repoModel.repo_name, isReplace); addSingleDisposable(single, new Consumer() { @Override public void accept(File file) throws Exception { - addUploadTask(account, repoModel, targetDir, file.getAbsolutePath(), isReplace, consumer); + addUploadTask(account, repoModel, parentDir, file.getAbsolutePath(), isReplace, consumer); } }); } - public void addUploadTask(Account account, RepoModel repoModel, String targetDir, String localFilePath, boolean isReplace, Consumer consumer) { + private FileTransferEntity getUploadTransferEntity(Account account, RepoModel repoModel, String parentDir, String localFilePath, boolean isReplace) { + FileTransferEntity entity = new FileTransferEntity(); + + File file = new File(localFilePath); + if (!file.exists()) { + return null; + } + + entity.full_path = file.getAbsolutePath(); + entity.target_path = Utils.pathJoin(parentDir, file.getName()); + entity.setParent_path(parentDir); + + entity.file_name = file.getName(); + entity.file_size = file.length(); + entity.file_format = FileUtils.getFileExtension(entity.full_path); + entity.file_md5 = FileUtils.getFileMD5ToString(entity.full_path).toLowerCase(); + entity.mime_type = MimeTypeMap.getSingleton().getMimeTypeFromExtension(entity.file_format); + + entity.repo_id = repoModel.repo_id; + entity.repo_name = repoModel.repo_name; + entity.related_account = account.getSignature(); + entity.data_source = TransferDataSource.FILE_BACKUP; + entity.created_at = System.currentTimeMillis(); + entity.modified_at = entity.created_at; + entity.file_original_modified_at = file.lastModified(); + entity.action_end_at = 0; + entity.file_strategy = isReplace ? ExistingFileStrategy.REPLACE : ExistingFileStrategy.KEEP; + entity.is_copy_to_local = false; + entity.transfer_action = TransferAction.UPLOAD; + entity.transfer_result = TransferResult.NO_RESULT; + entity.transfer_status = TransferStatus.WAITING; + + entity.uid = entity.getUID(); + return entity; + } + + public void addUploadTask(Account account, RepoModel repoModel, String parentDir, String localFilePath, boolean isReplace, Consumer consumer) { Single single = Single.create(new SingleOnSubscribe() { @Override public void subscribe(SingleEmitter emitter) throws Exception { - FileTransferEntity entity = new FileTransferEntity(); - - File file = new File(localFilePath); - if (!file.exists()) { - emitter.onSuccess(entity); - return; - } - entity.full_path = file.getAbsolutePath(); - entity.target_path = Utils.pathJoin(targetDir, file.getName()); - entity.setParent_path(targetDir); - - entity.file_name = file.getName(); - entity.file_size = file.length(); - entity.file_format = FileUtils.getFileExtension(entity.full_path); - entity.file_md5 = FileUtils.getFileMD5ToString(entity.full_path).toLowerCase(); - entity.mime_type = MimeTypeMap.getSingleton().getMimeTypeFromExtension(entity.file_format); - - entity.repo_id = repoModel.repo_id; - entity.repo_name = repoModel.repo_name; - entity.related_account = account.getSignature(); - entity.data_source = TransferDataSource.FILE_BACKUP; - entity.created_at = System.currentTimeMillis(); - entity.modified_at = entity.created_at; - entity.file_original_modified_at = file.lastModified(); - entity.action_end_at = 0; - entity.file_strategy = isReplace ? ExistingFileStrategy.REPLACE : ExistingFileStrategy.KEEP; - entity.is_copy_to_local = false; - entity.transfer_action = TransferAction.UPLOAD; - entity.transfer_result = TransferResult.NO_RESULT; - entity.transfer_status = TransferStatus.WAITING; - - entity.uid = entity.getUID(); + FileTransferEntity entity = getUploadTransferEntity(account, repoModel, parentDir, localFilePath, isReplace); AppDatabase.getInstance().fileTransferDAO().insert(entity); diff --git a/app/src/main/java/com/seafile/seadroid2/ui/media/image_preview/ImagePreviewViewModel.java b/app/src/main/java/com/seafile/seadroid2/ui/media/image_preview/ImagePreviewViewModel.java index ddd200c8d..0f268b1ae 100644 --- a/app/src/main/java/com/seafile/seadroid2/ui/media/image_preview/ImagePreviewViewModel.java +++ b/app/src/main/java/com/seafile/seadroid2/ui/media/image_preview/ImagePreviewViewModel.java @@ -16,7 +16,7 @@ import com.seafile.seadroid2.framework.util.Utils; import com.seafile.seadroid2.framework.worker.BackgroundJobManagerImpl; import com.seafile.seadroid2.ui.repo.RepoService; -import com.seafile.seadroid2.framework.http.IO; +import com.seafile.seadroid2.framework.http.HttpIO; import com.seafile.seadroid2.ui.base.viewmodel.BaseViewModel; import java.util.HashMap; @@ -111,7 +111,7 @@ public void star(String repoId, String path) { requestDataMap.put("path", path); Map bodyMap = generateRequestBody(requestDataMap); - Single single = IO.getInstanceWithLoggedIn().execute(RepoService.class).star(bodyMap); + Single single = HttpIO.getCurrentInstance().execute(RepoService.class).star(bodyMap); addSingleDisposable(single, new Consumer() { @Override public void accept(Dirent2Model resultModel) throws Exception { @@ -134,7 +134,7 @@ public void accept(Throwable throwable) throws Exception { public void unStar(String repoId, String path) { getRefreshLiveData().setValue(true); - Single single = IO.getInstanceWithLoggedIn().execute(RepoService.class).unStar(repoId, path); + Single single = HttpIO.getCurrentInstance().execute(RepoService.class).unStar(repoId, path); addSingleDisposable(single, new Consumer() { @Override public void accept(ResultModel resultModel) throws Exception { diff --git a/app/src/main/java/com/seafile/seadroid2/ui/media/image_preview/PhotoFragment.java b/app/src/main/java/com/seafile/seadroid2/ui/media/image_preview/PhotoFragment.java index ba5f77cb8..c100d1439 100644 --- a/app/src/main/java/com/seafile/seadroid2/ui/media/image_preview/PhotoFragment.java +++ b/app/src/main/java/com/seafile/seadroid2/ui/media/image_preview/PhotoFragment.java @@ -12,11 +12,9 @@ import androidx.annotation.Nullable; import com.blankj.utilcode.util.SizeUtils; -import com.bumptech.glide.Glide; import com.bumptech.glide.load.DataSource; import com.bumptech.glide.load.engine.DiskCacheStrategy; import com.bumptech.glide.load.engine.GlideException; -import com.bumptech.glide.load.model.GlideUrl; import com.bumptech.glide.request.RequestListener; import com.bumptech.glide.request.RequestOptions; import com.bumptech.glide.request.target.Target; @@ -25,9 +23,9 @@ import com.seafile.seadroid2.R; import com.seafile.seadroid2.account.Account; import com.seafile.seadroid2.account.SupportAccountManager; -import com.seafile.seadroid2.config.GlideLoadConfig; import com.seafile.seadroid2.framework.data.db.entities.DirentModel; import com.seafile.seadroid2.framework.datastore.DataManager; +import com.seafile.seadroid2.framework.util.GlideApp; import com.seafile.seadroid2.framework.util.SLogs; import com.seafile.seadroid2.ui.base.fragment.BaseFragment; @@ -98,7 +96,7 @@ public void onPhotoTap(ImageView view, float x, float y) { if (file.exists()) { progressBar.setVisibility(View.GONE); - Glide.with(requireContext()) + GlideApp.with(requireContext()) .load(file) .into(photoView); return; @@ -114,10 +112,8 @@ public void onPhotoTap(ImageView view, float x, float y) { .error(R.drawable.icon_image_error_filled) .diskCacheStrategy(DiskCacheStrategy.NONE); - GlideUrl glideUrl1 = GlideLoadConfig.getGlideUrl(url); - - Glide.with(requireContext()) - .load(glideUrl1) + GlideApp.with(requireContext()) + .load(url) .apply(opt) .fitCenter() .listener(new RequestListener() { diff --git a/app/src/main/java/com/seafile/seadroid2/ui/repo/RepoQuickAdapter.java b/app/src/main/java/com/seafile/seadroid2/ui/repo/RepoQuickAdapter.java index 3ad9d21da..d6f5e1981 100644 --- a/app/src/main/java/com/seafile/seadroid2/ui/repo/RepoQuickAdapter.java +++ b/app/src/main/java/com/seafile/seadroid2/ui/repo/RepoQuickAdapter.java @@ -11,11 +11,11 @@ import androidx.annotation.Nullable; import androidx.core.content.ContextCompat; import androidx.recyclerview.widget.DiffUtil; +import androidx.recyclerview.widget.RecyclerView; import com.blankj.utilcode.util.CollectionUtils; import com.blankj.utilcode.util.FileUtils; import com.blankj.utilcode.util.SizeUtils; -import com.bumptech.glide.Glide; import com.seafile.seadroid2.R; import com.seafile.seadroid2.account.Account; import com.seafile.seadroid2.config.AbsLayoutItemType; @@ -31,18 +31,19 @@ import com.seafile.seadroid2.framework.data.model.BaseModel; import com.seafile.seadroid2.framework.data.model.GroupItemModel; import com.seafile.seadroid2.framework.data.model.enums.TransferStatus; -import com.seafile.seadroid2.framework.http.IO; +import com.seafile.seadroid2.framework.http.HttpIO; import com.seafile.seadroid2.framework.util.GlideApp; import com.seafile.seadroid2.framework.util.Utils; import com.seafile.seadroid2.ui.base.adapter.BaseMultiAdapter; import com.seafile.seadroid2.ui.viewholder.GroupItemViewHolder; +import com.simplecityapps.recyclerview_fastscroll.views.FastScrollRecyclerView; import java.util.ArrayList; import java.util.List; import java.util.Locale; -public class RepoQuickAdapter extends BaseMultiAdapter { - private final String SERVER = IO.getInstanceWithLoggedIn().getServerUrl(); +public class RepoQuickAdapter extends BaseMultiAdapter implements FastScrollRecyclerView.SectionedAdapter, FastScrollRecyclerView.MeasurableAdapter { + private final String SERVER = HttpIO.getCurrentInstance().getServerUrl(); private boolean actionModeOn; @@ -173,8 +174,8 @@ private void onBindAccount(AccountViewHolder holder, BaseModel model) { if (TextUtils.isEmpty(account.avatar_url)) { holder.binding.listItemAccountIcon.setImageResource(R.drawable.default_avatar); } else { - Glide.with(getContext()) - .load(GlideLoadConfig.getGlideUrl(account.avatar_url)) + GlideApp.with(getContext()) + .load(account.avatar_url) .apply(GlideLoadConfig.getAvatarOptions()) .into(holder.binding.listItemAccountIcon); } @@ -230,8 +231,8 @@ private void onBindDirents(DirentViewHolder holder, DirentModel model) { holder.binding.itemIcon.setImageResource(model.getIcon()); } else { String url = convertThumbnailUrl(model.repo_id, model.full_path); - Glide.with(getContext()) - .load(GlideLoadConfig.getGlideUrl(url)) + GlideApp.with(getContext()) + .load(url) .apply(GlideLoadConfig.getOptions()) .into(holder.binding.itemIcon); } @@ -544,4 +545,37 @@ public boolean areContentsTheSame(int oldItemPosition, int newItemPosition) { setItems(list); diffResult.dispatchUpdatesTo(this); } + + @NonNull + @Override + public String getSectionName(int position) { + BaseModel item = getItems().get(position); + if (item instanceof GroupItemModel) { + GroupItemModel m = (GroupItemModel) item; + return m.title; + } else if (item instanceof DirentModel) { + DirentModel m = (DirentModel) item; + return m.name; + } else if (item instanceof RepoModel) { + RepoModel m = (RepoModel) item; + return m.repo_name; + } + + return ""; + } + + @Override + public int getViewTypeHeight(RecyclerView recyclerView, @Nullable RecyclerView.ViewHolder viewHolder, int viewType) { + if (viewType == AbsLayoutItemType.ACCOUNT) { + return recyclerView.getResources().getDimensionPixelSize(R.dimen.rv_item_account_height); + } else if (viewType == AbsLayoutItemType.GROUP_ITEM) { + return recyclerView.getResources().getDimensionPixelSize(R.dimen.rv_item_group_height); + } else if (viewType == AbsLayoutItemType.REPO) { + return recyclerView.getResources().getDimensionPixelSize(R.dimen.rv_item_repo_height); + } else if (viewType == AbsLayoutItemType.DIRENT) { + return recyclerView.getResources().getDimensionPixelSize(R.dimen.rv_item_dirent_height); + } + + return 0; + } } diff --git a/app/src/main/java/com/seafile/seadroid2/ui/repo/RepoQuickFragment.java b/app/src/main/java/com/seafile/seadroid2/ui/repo/RepoQuickFragment.java index f8fec260f..0267a182d 100644 --- a/app/src/main/java/com/seafile/seadroid2/ui/repo/RepoQuickFragment.java +++ b/app/src/main/java/com/seafile/seadroid2/ui/repo/RepoQuickFragment.java @@ -26,6 +26,8 @@ import androidx.lifecycle.Observer; import androidx.lifecycle.ViewModelProvider; import androidx.media3.common.util.UnstableApi; +import androidx.recyclerview.widget.ConcatAdapter; +import androidx.recyclerview.widget.DividerItemDecoration; import androidx.recyclerview.widget.LinearLayoutManager; import androidx.work.Data; import androidx.work.WorkInfo; @@ -33,6 +35,7 @@ import com.blankj.utilcode.util.CollectionUtils; import com.blankj.utilcode.util.TimeUtils; import com.blankj.utilcode.util.ToastUtils; +import com.github.panpf.recycler.sticky.StickyItemDecoration; import com.google.android.material.dialog.MaterialAlertDialogBuilder; import com.google.common.collect.Maps; import com.seafile.seadroid2.R; @@ -41,10 +44,11 @@ import com.seafile.seadroid2.account.SupportAccountManager; import com.seafile.seadroid2.bottomsheetmenu.BottomSheetHelper; import com.seafile.seadroid2.bottomsheetmenu.BottomSheetMenuFragment; +import com.seafile.seadroid2.config.AbsLayoutItemType; import com.seafile.seadroid2.config.Constants; import com.seafile.seadroid2.context.CopyMoveContext; import com.seafile.seadroid2.context.NavContext; -import com.seafile.seadroid2.databinding.LayoutFrameSwipeRvBinding; +import com.seafile.seadroid2.databinding.LayoutFastRvBinding; import com.seafile.seadroid2.framework.data.db.entities.DirentModel; import com.seafile.seadroid2.framework.data.db.entities.EncKeyCacheEntity; import com.seafile.seadroid2.framework.data.db.entities.FileTransferEntity; @@ -95,7 +99,7 @@ public class RepoQuickFragment extends BaseFragmentWithVM { private final HashMap mRefreshStatusExpireTimeMap = new HashMap<>(); - private LayoutFrameSwipeRvBinding binding; + private LayoutFastRvBinding binding; private RepoQuickAdapter adapter; private LinearLayoutManager rvManager; @@ -130,7 +134,7 @@ public void onCreate(@Nullable Bundle savedInstanceState) { @Override public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { - binding = LayoutFrameSwipeRvBinding.inflate(inflater, container, false); + binding = LayoutFastRvBinding.inflate(inflater, container, false); binding.swipeRefreshLayout.setOnRefreshListener(() -> loadData(true)); return binding.getRoot(); } @@ -155,8 +159,8 @@ public void onFirstResume() { } @Override - public void onNonFirstResume() { - super.onNonFirstResume(); + public void onOtherResume() { + super.onOtherResume(); if (isForce()) { loadData(true); } @@ -164,6 +168,12 @@ public void onNonFirstResume() { private void init() { rvManager = (LinearLayoutManager) binding.rv.getLayoutManager(); + + StickyItemDecoration decoration = new StickyItemDecoration.Builder() + .itemType(AbsLayoutItemType.GROUP_ITEM) + .build(); + + binding.rv.addItemDecoration(decoration); } private void initAdapter() { @@ -248,7 +258,7 @@ public void onChanged(Boolean aBoolean) { } }); - getViewModel().getSeafExceptionLiveData().observe(getViewLifecycleOwner(), this::showAdapterTipView); + getViewModel().getSeafExceptionLiveData().observe(getViewLifecycleOwner(), this::showErrorView); getViewModel().getStarLiveData().observe(getViewLifecycleOwner(), aBoolean -> { if (aBoolean) { @@ -383,21 +393,26 @@ private void notifyDataChanged(List repoModels) { } private void showEmptyTip() { - showAdapterTipView(R.string.no_repo); + showErrorView(R.string.no_repo); } - private void showAdapterTipView(SeafException seafException) { + private void showErrorView(SeafException seafException) { - ToastUtils.showLong(seafException.getMessage()); + String errorMsg = seafException.getMessage(); + ToastUtils.showLong(errorMsg); int strInt = !getNavContext().isInRepo() ? R.string.error_when_load_repos : R.string.error_when_load_dirents; - showAdapterTipView(strInt); + showErrorView(strInt); + } + + private void showErrorView(int textRes) { + showErrorView(getString(textRes)); } - private void showAdapterTipView(int textRes) { + private void showErrorView(String msg) { adapter.submitList(null); TextView tipView = TipsViews.getTipTextView(requireContext()); - tipView.setText(textRes); + tipView.setText(msg); tipView.setOnClickListener(v -> loadData(true)); adapter.setStateView(tipView); adapter.setStateViewEnable(true); diff --git a/app/src/main/java/com/seafile/seadroid2/ui/repo/RepoViewModel.java b/app/src/main/java/com/seafile/seadroid2/ui/repo/RepoViewModel.java index 3da56d2d9..ae8c7ee8f 100644 --- a/app/src/main/java/com/seafile/seadroid2/ui/repo/RepoViewModel.java +++ b/app/src/main/java/com/seafile/seadroid2/ui/repo/RepoViewModel.java @@ -18,27 +18,22 @@ import com.seafile.seadroid2.framework.data.db.entities.RepoModel; import com.seafile.seadroid2.framework.data.model.BaseModel; import com.seafile.seadroid2.framework.data.model.ResultModel; -import com.seafile.seadroid2.framework.data.model.enums.TransferAction; import com.seafile.seadroid2.framework.data.model.enums.TransferStatus; import com.seafile.seadroid2.framework.data.model.repo.Dirent2Model; -import com.seafile.seadroid2.framework.http.IO; +import com.seafile.seadroid2.framework.http.HttpIO; import com.seafile.seadroid2.ui.base.viewmodel.BaseViewModel; import com.seafile.seadroid2.framework.util.Objs; import com.seafile.seadroid2.framework.util.SLogs; import java.util.ArrayList; -import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Optional; -import java.util.stream.Collectors; import io.reactivex.Single; -import io.reactivex.SingleSource; import io.reactivex.functions.BiFunction; import io.reactivex.functions.Consumer; -import io.reactivex.functions.Function; import okhttp3.RequestBody; public class RepoViewModel extends BaseViewModel { @@ -197,16 +192,11 @@ public List apply(List direntModels, List>() { @Override public void accept(List direntModels) throws Exception { - if (!CollectionUtils.isEmpty(direntModels)) { - getObjsListLiveData().setValue(Objs.parseLocalDirents(direntModels)); - - if (isForce) { - loadDirentsFromNet(account, context); - } else { - getRefreshLiveData().setValue(false); - } - } else { + if (CollectionUtils.isEmpty(direntModels) || isForce) { loadDirentsFromNet(account, context); + } else { + getObjsListLiveData().setValue(Objs.parseLocalDirents(direntModels)); + getRefreshLiveData().setValue(false); } } }); @@ -241,7 +231,6 @@ public void accept(Throwable throwable) throws Exception { completeRemoteWipe(); } getSeafExceptionLiveData().setValue(seafException); - } }); } @@ -255,7 +244,7 @@ public void star(String repoId, String path) { requestDataMap.put("path", path); Map bodyMap = generateRequestBody(requestDataMap); - Single single = IO.getInstanceWithLoggedIn().execute(RepoService.class).star(bodyMap); + Single single = HttpIO.getCurrentInstance().execute(RepoService.class).star(bodyMap); addSingleDisposable(single, new Consumer() { @Override public void accept(Dirent2Model resultModel) throws Exception { @@ -275,7 +264,7 @@ public void accept(Throwable throwable) throws Exception { public void unStar(String repoId, String path) { getRefreshLiveData().setValue(true); - Single single = IO.getInstanceWithLoggedIn().execute(RepoService.class).unStar(repoId, path); + Single single = HttpIO.getCurrentInstance().execute(RepoService.class).unStar(repoId, path); addSingleDisposable(single, new Consumer() { @Override public void accept(ResultModel resultModel) throws Exception { diff --git a/app/src/main/java/com/seafile/seadroid2/ui/search/SearchViewModel.java b/app/src/main/java/com/seafile/seadroid2/ui/search/SearchViewModel.java index a5acdd433..0901259df 100644 --- a/app/src/main/java/com/seafile/seadroid2/ui/search/SearchViewModel.java +++ b/app/src/main/java/com/seafile/seadroid2/ui/search/SearchViewModel.java @@ -7,7 +7,7 @@ import com.seafile.seadroid2.framework.data.db.entities.RepoModel; import com.seafile.seadroid2.framework.data.model.search.SearchModel; import com.seafile.seadroid2.framework.data.model.search.SearchWrapperModel; -import com.seafile.seadroid2.framework.http.IO; +import com.seafile.seadroid2.framework.http.HttpIO; import com.seafile.seadroid2.framework.util.SLogs; import com.seafile.seadroid2.ui.base.viewmodel.BaseViewModel; import com.seafile.seadroid2.ui.repo.RepoService; @@ -29,7 +29,7 @@ public MutableLiveData> getListLiveData() { public void loadNext(String q, int pageNo, int pageSize) { getRefreshLiveData().setValue(true); - Single single = IO.getInstanceWithLoggedIn().execute(SearchService.class).search("all", q, "all", pageNo, pageSize); + Single single = HttpIO.getCurrentInstance().execute(SearchService.class).search("all", q, "all", pageNo, pageSize); addSingleDisposable(single, new Consumer() { @Override public void accept(SearchWrapperModel searchWrapperModel) throws Exception { @@ -50,7 +50,7 @@ public void getRepoModel(String repoId, Consumer consumer) { //from db Single> singleDb = AppDatabase.getInstance().repoDao().getRepoById(repoId); - Single singleNet = IO.getInstanceWithLoggedIn().execute(RepoService.class).getRepoInfo(repoId); + Single singleNet = HttpIO.getCurrentInstance().execute(RepoService.class).getRepoInfo(repoId); Single single = singleDb.flatMap(new Function, SingleSource>() { @Override diff --git a/app/src/main/java/com/seafile/seadroid2/ui/selector/ObjSelectorActivity.java b/app/src/main/java/com/seafile/seadroid2/ui/selector/ObjSelectorActivity.java index 34c873236..ecfdf1285 100644 --- a/app/src/main/java/com/seafile/seadroid2/ui/selector/ObjSelectorActivity.java +++ b/app/src/main/java/com/seafile/seadroid2/ui/selector/ObjSelectorActivity.java @@ -121,17 +121,7 @@ private void initView() { binding.ok.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { - String repoName = mNavContext.getRepoModel().repo_name; - String repoID = mNavContext.getRepoModel().repo_id; - String dir = mNavContext.getNavPath(); - - Intent intent = new Intent(); - intent.putExtra(DATA_REPO_NAME, repoName); - intent.putExtra(DATA_REPO_ID, repoID); - intent.putExtra(DATA_DIR, dir); - intent.putExtra(DATA_ACCOUNT, mAccount); - setResult(RESULT_OK, intent); - finish(); + onOkClick(); } }); @@ -151,6 +141,24 @@ public void onClick(View view) { }); } + private void onOkClick() { + if (!mNavContext.isInRepo()) { + ToastUtils.showLong(R.string.choose_a_library); + return; + } + + String repoName = mNavContext.getRepoModel().repo_name; + String repoID = mNavContext.getRepoModel().repo_id; + String dir = mNavContext.getNavPath(); + + Intent intent = new Intent(); + intent.putExtra(DATA_REPO_NAME, repoName); + intent.putExtra(DATA_REPO_ID, repoID); + intent.putExtra(DATA_DIR, dir); + intent.putExtra(DATA_ACCOUNT, mAccount); + setResult(RESULT_OK, intent); + finish(); + } private void initViewModel() { viewModel.getRefreshLiveData().observe(this, new Observer() { @@ -254,10 +262,16 @@ public void onResultData(RepoModel uRepoModel) { private void showNewDirDialog() { + if (!mNavContext.isInRepo()) { + ToastUtils.showLong(R.string.choose_a_library); + return; + } + if (!mNavContext.hasWritePermissionWithRepo()) { ToastUtils.showLong(R.string.library_read_only); return; } + NewDirFileDialogFragment dialogFragment = NewDirFileDialogFragment.newInstance(); dialogFragment.initData(mNavContext.getRepoModel().repo_id, mNavContext.getNavPath(), true); dialogFragment.setRefreshListener(new OnRefreshDataListener() { diff --git a/app/src/main/java/com/seafile/seadroid2/ui/selector/ObjSelectorViewModel.java b/app/src/main/java/com/seafile/seadroid2/ui/selector/ObjSelectorViewModel.java index b07d190bc..d4642910f 100644 --- a/app/src/main/java/com/seafile/seadroid2/ui/selector/ObjSelectorViewModel.java +++ b/app/src/main/java/com/seafile/seadroid2/ui/selector/ObjSelectorViewModel.java @@ -16,7 +16,7 @@ import com.seafile.seadroid2.framework.data.model.repo.DirentWrapperModel; import com.seafile.seadroid2.framework.data.model.repo.RepoWrapperModel; import com.seafile.seadroid2.ui.repo.RepoService; -import com.seafile.seadroid2.framework.http.IO; +import com.seafile.seadroid2.framework.http.HttpIO; import com.seafile.seadroid2.framework.util.Objs; import com.seafile.seadroid2.framework.util.SLogs; @@ -57,7 +57,7 @@ public void loadAccount() { public void loadReposFromNet(Account account,boolean isFilterEncrypted) { getRefreshLiveData().setValue(true); - Single singleNet = IO.getInstanceByAccount(account).execute(RepoService.class).getRepos(); + Single singleNet = HttpIO.getInstanceByAccount(account).execute(RepoService.class).getRepos(); addSingleDisposable(singleNet, new Consumer() { @Override @@ -86,7 +86,7 @@ public void loadDirentsFromNet(Account account, NavContext context) { String repoId = context.getRepoModel().repo_id; String parentDir = context.getNavPath(); - Single singleNet = IO.getInstanceByAccount(account).execute(RepoService.class).getDirents(repoId, parentDir); + Single singleNet = HttpIO.getInstanceByAccount(account).execute(RepoService.class).getDirents(repoId, parentDir); addSingleDisposable(singleNet, new Consumer() { @Override public void accept(DirentWrapperModel direntWrapperModel) throws Exception { diff --git a/app/src/main/java/com/seafile/seadroid2/ui/settings/SettingsFragment.java b/app/src/main/java/com/seafile/seadroid2/ui/settings/SettingsFragment.java index 30102db51..45307e772 100644 --- a/app/src/main/java/com/seafile/seadroid2/ui/settings/SettingsFragment.java +++ b/app/src/main/java/com/seafile/seadroid2/ui/settings/SettingsFragment.java @@ -35,7 +35,6 @@ import com.blankj.utilcode.util.AppUtils; import com.blankj.utilcode.util.CollectionUtils; import com.blankj.utilcode.util.ToastUtils; -import com.bumptech.glide.Glide; import com.google.android.material.dialog.MaterialAlertDialogBuilder; import com.google.firebase.analytics.FirebaseAnalytics; import com.seafile.seadroid2.R; @@ -52,16 +51,17 @@ import com.seafile.seadroid2.framework.datastore.sp.FolderBackupManager; import com.seafile.seadroid2.framework.datastore.sp.GestureLockManager; import com.seafile.seadroid2.framework.datastore.sp.SettingsManager; +import com.seafile.seadroid2.framework.file_monitor.FileSyncService; +import com.seafile.seadroid2.framework.util.GlideApp; import com.seafile.seadroid2.framework.util.PermissionUtil; import com.seafile.seadroid2.framework.util.SLogs; import com.seafile.seadroid2.framework.worker.BackgroundJobManagerImpl; -import com.seafile.seadroid2.framework.file_monitor.FileSyncService; import com.seafile.seadroid2.framework.worker.SupportWorkManager; import com.seafile.seadroid2.framework.worker.TransferEvent; import com.seafile.seadroid2.framework.worker.TransferWorker; import com.seafile.seadroid2.framework.worker.upload.FolderBackupScannerWorker; -import com.seafile.seadroid2.framework.worker.upload.UploadFolderFileAutomaticallyWorker; import com.seafile.seadroid2.framework.worker.upload.MediaBackupScannerWorker; +import com.seafile.seadroid2.framework.worker.upload.UploadFolderFileAutomaticallyWorker; import com.seafile.seadroid2.framework.worker.upload.UploadMediaFileAutomaticallyWorker; import com.seafile.seadroid2.gesturelock.LockPatternUtils; import com.seafile.seadroid2.ui.camera_upload.CameraUploadConfigActivity; @@ -833,7 +833,7 @@ public void onActionStatus(boolean isDone) { if (isDone) { calculateCacheSize(); //clear Glide cache - Glide.get(SeadroidApplication.getAppContext()).clearMemory(); + GlideApp.get(SeadroidApplication.getAppContext()).clearMemory(); ToastUtils.showLong(R.string.settings_clear_cache_success); } else { ToastUtils.showLong(R.string.settings_clear_cache_failed); diff --git a/app/src/main/java/com/seafile/seadroid2/ui/settings/SettingsFragmentViewModel.java b/app/src/main/java/com/seafile/seadroid2/ui/settings/SettingsFragmentViewModel.java index 7ab1a5f4a..1d6f922f0 100644 --- a/app/src/main/java/com/seafile/seadroid2/ui/settings/SettingsFragmentViewModel.java +++ b/app/src/main/java/com/seafile/seadroid2/ui/settings/SettingsFragmentViewModel.java @@ -1,7 +1,6 @@ package com.seafile.seadroid2.ui.settings; import android.content.Context; -import android.text.TextUtils; import androidx.lifecycle.MutableLiveData; import androidx.work.WorkInfo; @@ -18,7 +17,7 @@ import com.seafile.seadroid2.framework.data.model.enums.TransferStatus; import com.seafile.seadroid2.framework.data.model.server.ServerInfoModel; import com.seafile.seadroid2.framework.datastore.StorageManager; -import com.seafile.seadroid2.framework.http.IO; +import com.seafile.seadroid2.framework.http.HttpIO; import com.seafile.seadroid2.framework.worker.BackgroundJobManagerImpl; import com.seafile.seadroid2.framework.worker.upload.UploadFolderFileAutomaticallyWorker; import com.seafile.seadroid2.framework.worker.upload.UploadMediaFileAutomaticallyWorker; @@ -59,8 +58,8 @@ public MutableLiveData getAccountInfoLiveData() { public void getAccountInfo() { getRefreshLiveData().setValue(true); - Single single1 = IO.getInstanceWithLoggedIn().execute(MainService.class).getServerInfo(); - Single single2 = IO.getInstanceWithLoggedIn().execute(AccountService.class).getAccountInfo(); + Single single1 = HttpIO.getCurrentInstance().execute(MainService.class).getServerInfo(); + Single single2 = HttpIO.getCurrentInstance().execute(AccountService.class).getAccountInfo(); Single single = Single.zip(single1, single2, new BiFunction() { @Override @@ -71,7 +70,7 @@ public AccountInfo apply(ServerInfoModel serverInfoModel, AccountInfo accountInf return accountInfo; } - accountInfo.setServer(IO.getInstanceWithLoggedIn().getServerUrl()); + accountInfo.setServer(HttpIO.getCurrentInstance().getServerUrl()); ServerInfo serverInfo1 = new ServerInfo(account.server, serverInfoModel.version, serverInfoModel.getFeaturesString(), serverInfoModel.encrypted_library_version); SupportAccountManager.getInstance().setServerInfo(account, serverInfo1); @@ -82,7 +81,7 @@ public AccountInfo apply(ServerInfoModel serverInfoModel, AccountInfo accountInf addSingleDisposable(single, new Consumer() { @Override public void accept(AccountInfo accountInfo) throws Exception { - accountInfo.setServer(IO.getInstanceWithLoggedIn().getServerUrl()); + accountInfo.setServer(HttpIO.getCurrentInstance().getServerUrl()); getRefreshLiveData().setValue(false); diff --git a/app/src/main/java/com/seafile/seadroid2/ui/share/ShareToSeafileViewModel.java b/app/src/main/java/com/seafile/seadroid2/ui/share/ShareToSeafileViewModel.java index 7a034c651..f684fe058 100644 --- a/app/src/main/java/com/seafile/seadroid2/ui/share/ShareToSeafileViewModel.java +++ b/app/src/main/java/com/seafile/seadroid2/ui/share/ShareToSeafileViewModel.java @@ -15,7 +15,7 @@ import com.seafile.seadroid2.framework.data.db.entities.FileTransferEntity; import com.seafile.seadroid2.framework.data.model.repo.DirentWrapperModel; import com.seafile.seadroid2.framework.datastore.DataManager; -import com.seafile.seadroid2.framework.http.IO; +import com.seafile.seadroid2.framework.http.HttpIO; import com.seafile.seadroid2.framework.util.Utils; import com.seafile.seadroid2.framework.worker.BackgroundJobManagerImpl; import com.seafile.seadroid2.ui.base.viewmodel.BaseViewModel; @@ -51,7 +51,7 @@ public void checkRemoteExists(Context context, Account account, String repoId, S Single, List>> single = Single.create(new SingleOnSubscribe, List>>() { @Override public void subscribe(SingleEmitter, List>> emitter) throws Exception { - Call call = IO.getInstanceByAccount(account).execute(RepoService.class).getDirentsSync(repoId, targetDir); + Call call = HttpIO.getInstanceByAccount(account).execute(RepoService.class).getDirentsSync(repoId, targetDir); Response res = call.execute(); if (!res.isSuccessful()) { emitter.onError(SeafException.networkException); diff --git a/app/src/main/java/com/seafile/seadroid2/ui/star/StarredAdapter.java b/app/src/main/java/com/seafile/seadroid2/ui/star/StarredAdapter.java index 49b1d1d09..da0dd9c96 100644 --- a/app/src/main/java/com/seafile/seadroid2/ui/star/StarredAdapter.java +++ b/app/src/main/java/com/seafile/seadroid2/ui/star/StarredAdapter.java @@ -11,19 +11,19 @@ import androidx.recyclerview.widget.DiffUtil; import com.blankj.utilcode.util.CollectionUtils; -import com.bumptech.glide.Glide; import com.seafile.seadroid2.R; import com.seafile.seadroid2.config.GlideLoadConfig; import com.seafile.seadroid2.databinding.ItemStarredBinding; import com.seafile.seadroid2.framework.data.db.entities.StarredModel; -import com.seafile.seadroid2.framework.http.IO; +import com.seafile.seadroid2.framework.http.HttpIO; +import com.seafile.seadroid2.framework.util.GlideApp; import com.seafile.seadroid2.framework.util.Icons; import com.seafile.seadroid2.ui.base.adapter.BaseAdapter; import java.util.List; public class StarredAdapter extends BaseAdapter { - private final String SERVER = IO.getInstanceWithLoggedIn().getServerUrl(); + private final String SERVER = HttpIO.getCurrentInstance().getServerUrl(); @NonNull @Override @@ -66,8 +66,8 @@ protected void onBindViewHolder(@NonNull StarredViewHolder holder, int i, @Nulla holder.binding.itemIcon.setImageResource(Icons.getFileIcon(model.obj_name)); } else { String url = convertThumbnailUrl(model.repo_id, model.path); - Glide.with(getContext()) - .load(GlideLoadConfig.getGlideUrl(url)) + GlideApp.with(getContext()) + .load(url) .apply(GlideLoadConfig.getOptions()) .into(holder.binding.itemIcon); } diff --git a/app/src/main/java/com/seafile/seadroid2/ui/star/StarredViewModel.java b/app/src/main/java/com/seafile/seadroid2/ui/star/StarredViewModel.java index 1631752e7..edbf73af9 100644 --- a/app/src/main/java/com/seafile/seadroid2/ui/star/StarredViewModel.java +++ b/app/src/main/java/com/seafile/seadroid2/ui/star/StarredViewModel.java @@ -2,14 +2,12 @@ import androidx.lifecycle.MutableLiveData; -import com.blankj.utilcode.util.ToastUtils; -import com.seafile.seadroid2.R; import com.seafile.seadroid2.SeafException; import com.seafile.seadroid2.account.Account; import com.seafile.seadroid2.account.SupportAccountManager; import com.seafile.seadroid2.framework.util.Objs; import com.seafile.seadroid2.ui.base.viewmodel.BaseViewModel; -import com.seafile.seadroid2.framework.http.IO; +import com.seafile.seadroid2.framework.http.HttpIO; import com.seafile.seadroid2.framework.data.model.ResultModel; import com.seafile.seadroid2.framework.data.db.entities.StarredModel; @@ -61,7 +59,7 @@ public void accept(Throwable throwable) throws Exception { } public void unStarItem(String repoId, String path) { - Single flowable = IO.getInstanceWithLoggedIn().execute(StarredService.class).unStarItem(repoId, path); + Single flowable = HttpIO.getCurrentInstance().execute(StarredService.class).unStarItem(repoId, path); addSingleDisposable(flowable, new Consumer() { @Override public void accept(ResultModel resultModel) throws Exception { diff --git a/app/src/main/java/com/seafile/seadroid2/ui/transfer_list/DownloadListFragment.java b/app/src/main/java/com/seafile/seadroid2/ui/transfer_list/DownloadListFragment.java index 985d9e8d5..0102b6511 100644 --- a/app/src/main/java/com/seafile/seadroid2/ui/transfer_list/DownloadListFragment.java +++ b/app/src/main/java/com/seafile/seadroid2/ui/transfer_list/DownloadListFragment.java @@ -70,7 +70,8 @@ private void doWorkInfoLiveData(TransferDataSource dataSource, WorkInfo workInfo Data outData = workInfo.getOutputData(); String outEvent = outData.getString(TransferWorker.KEY_DATA_EVENT); - if (TransferEvent.EVENT_FINISH.equals(outEvent)) { + boolean isDownloaded = outData.getBoolean(TransferWorker.KEY_DATA_PARAM, false); + if (TransferEvent.EVENT_FINISH.equals(outEvent) && isDownloaded) { refreshData(); return; } diff --git a/app/src/main/java/com/seafile/seadroid2/ui/transfer_list/TransferListFragment.java b/app/src/main/java/com/seafile/seadroid2/ui/transfer_list/TransferListFragment.java index 5c7318781..a82db45b3 100644 --- a/app/src/main/java/com/seafile/seadroid2/ui/transfer_list/TransferListFragment.java +++ b/app/src/main/java/com/seafile/seadroid2/ui/transfer_list/TransferListFragment.java @@ -23,7 +23,6 @@ import com.blankj.utilcode.util.ToastUtils; import com.chad.library.adapter4.BaseQuickAdapter; import com.chad.library.adapter4.QuickAdapterHelper; -import com.chad.library.adapter4.loadState.trailing.TrailingLoadStateAdapter; import com.google.android.material.dialog.MaterialAlertDialogBuilder; import com.seafile.seadroid2.R; import com.seafile.seadroid2.bottomsheetmenu.BottomSheetHelper; @@ -31,17 +30,18 @@ import com.seafile.seadroid2.databinding.LayoutFrameSwipeRvBinding; import com.seafile.seadroid2.framework.data.db.entities.FileTransferEntity; import com.seafile.seadroid2.framework.data.model.enums.TransferAction; -import com.seafile.seadroid2.framework.data.model.enums.TransferResult; import com.seafile.seadroid2.framework.data.model.enums.TransferStatus; import com.seafile.seadroid2.framework.worker.BackgroundJobManagerImpl; import com.seafile.seadroid2.framework.worker.TransferEvent; import com.seafile.seadroid2.framework.worker.TransferWorker; -import com.seafile.seadroid2.ui.base.adapter.LogicLoadMoreAdapter; import com.seafile.seadroid2.ui.base.fragment.BaseFragment; import com.seafile.seadroid2.view.TipsViews; +import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; import io.reactivex.functions.Consumer; @@ -52,7 +52,7 @@ public abstract class TransferListFragment extends BaseFragment { protected TransferActivity activity = null; private LinearLayoutManager layoutManager; private TransferListViewModel viewModel; - private Map positionMap; + private ConcurrentHashMap positionMap; @Override public void onAttach(@NonNull Context context) { @@ -148,11 +148,17 @@ public boolean onLongClick(@NonNull BaseQuickAdapter base private void initViewModel() { getViewModel().getRefreshLiveData().observe(getViewLifecycleOwner(), aBoolean -> binding.swipeRefreshLayout.setRefreshing(aBoolean)); - getViewModel().getFileTransferEntitiesLiveData().observe(getViewLifecycleOwner(), new Observer, List>>() { + + getViewModel().getTransferListLiveData().observe(getViewLifecycleOwner(), new Observer>() { @Override - public void onChanged(Pair, List> mapListPair) { - notifyDataChanged(mapListPair.second); - positionMap = mapListPair.first; + public void onChanged(List list) { + positionMap = new ConcurrentHashMap<>(list.size()); + + for (int i = 0; i < list.size(); i++) { + positionMap.put(list.get(i).uid, i); + } + + notifyDataChanged(list); } }); } @@ -256,8 +262,12 @@ public void notifyProgressById(String transferId, long transferredSize, int perc return; } - int position = positionMap.get(transferId).intValue(); - if (position == -1) { + if (!positionMap.containsKey(transferId)){ + return; + } + + Integer position = positionMap.get(transferId); + if (position == null || position == -1) { return; } diff --git a/app/src/main/java/com/seafile/seadroid2/ui/transfer_list/TransferListViewModel.java b/app/src/main/java/com/seafile/seadroid2/ui/transfer_list/TransferListViewModel.java index 5a0bb6edc..5c13a716b 100644 --- a/app/src/main/java/com/seafile/seadroid2/ui/transfer_list/TransferListViewModel.java +++ b/app/src/main/java/com/seafile/seadroid2/ui/transfer_list/TransferListViewModel.java @@ -1,7 +1,5 @@ package com.seafile.seadroid2.ui.transfer_list; -import android.util.Pair; - import androidx.lifecycle.MutableLiveData; import com.blankj.utilcode.util.CollectionUtils; @@ -15,7 +13,6 @@ import com.seafile.seadroid2.framework.data.model.enums.TransferResult; import com.seafile.seadroid2.framework.data.model.enums.TransferStatus; import com.seafile.seadroid2.framework.worker.BackgroundJobManagerImpl; -import com.seafile.seadroid2.framework.worker.SupportWorkManager; import com.seafile.seadroid2.framework.worker.upload.UploadFileManuallyWorker; import com.seafile.seadroid2.framework.worker.upload.UploadFolderFileAutomaticallyWorker; import com.seafile.seadroid2.framework.worker.upload.UploadMediaFileAutomaticallyWorker; @@ -23,9 +20,7 @@ import com.seafile.seadroid2.framework.util.SLogs; import java.util.ArrayList; -import java.util.HashMap; import java.util.List; -import java.util.Map; import io.reactivex.Completable; import io.reactivex.Single; @@ -38,10 +33,10 @@ public class TransferListViewModel extends BaseViewModel { - private final MutableLiveData, List>> mFileTransferEntitiesLiveData = new MutableLiveData<>(); + private final MutableLiveData> mTransferListLiveData = new MutableLiveData<>(); - public MutableLiveData, List>> getFileTransferEntitiesLiveData() { - return mFileTransferEntitiesLiveData; + public MutableLiveData> getTransferListLiveData() { + return mTransferListLiveData; } public void loadData(TransferAction transferAction, boolean isShowRefresh) { @@ -55,10 +50,11 @@ public void loadData(TransferAction transferAction, boolean isShowRefresh) { getRefreshLiveData().setValue(false); return; } - Single, List>> single = queryPageData(account, transferAction); + + Single> single = queryPageData(account, transferAction); addSingleDisposable(single, pair -> { - mFileTransferEntitiesLiveData.setValue(pair); + getTransferListLiveData().setValue(pair); if (isShowRefresh) { getRefreshLiveData().setValue(false); @@ -66,15 +62,15 @@ public void loadData(TransferAction transferAction, boolean isShowRefresh) { }); } - private Single, List>> queryPageData(Account account, TransferAction transferAction) { - return Single.create(new SingleOnSubscribe, List>>() { + private Single> queryPageData(Account account, TransferAction transferAction) { + return Single.create(new SingleOnSubscribe>() { @Override - public void subscribe(SingleEmitter, List>> emitter) throws Exception { - int page = 1; - int pageSize = 500; + public void subscribe(SingleEmitter> emitter) throws Exception { + + int pageSize = 100; List list = new ArrayList<>(); - while (true) { + for (int page = 1; page <= 10; page++) { int offset = (page - 1) * pageSize; List temp; @@ -94,18 +90,11 @@ public void subscribe(SingleEmitter, List map = new HashMap<>(); - - for (int i = 0; i < list.size(); i++) { - map.put(list.get(i).uid, i); - } - - emitter.onSuccess(new Pair<>(map, list)); + SLogs.e("本次查询的大小:" + list.size()); + emitter.onSuccess(list); } }); } diff --git a/app/src/main/java/com/seafile/seadroid2/ui/transfer_list/UploadListFragment.java b/app/src/main/java/com/seafile/seadroid2/ui/transfer_list/UploadListFragment.java index 4af807178..a312236eb 100644 --- a/app/src/main/java/com/seafile/seadroid2/ui/transfer_list/UploadListFragment.java +++ b/app/src/main/java/com/seafile/seadroid2/ui/transfer_list/UploadListFragment.java @@ -92,7 +92,9 @@ private void doWorkInfoLiveData(TransferDataSource dataSource, WorkInfo workInfo Data outData = workInfo.getOutputData(); String outEvent = outData.getString(TransferWorker.KEY_DATA_EVENT); - if (TransferEvent.EVENT_FINISH.equals(outEvent)) { + boolean isUploaded = outData.getBoolean(TransferWorker.KEY_DATA_PARAM, false); + + if (TransferEvent.EVENT_FINISH.equals(outEvent) && isUploaded) { refreshData(); return; } diff --git a/app/src/main/java/com/seafile/seadroid2/view/FastRecyclerView.java b/app/src/main/java/com/seafile/seadroid2/view/FastRecyclerView.java new file mode 100644 index 000000000..92d1abd32 --- /dev/null +++ b/app/src/main/java/com/seafile/seadroid2/view/FastRecyclerView.java @@ -0,0 +1,32 @@ +package com.seafile.seadroid2.view; + +import android.content.Context; +import android.util.AttributeSet; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.recyclerview.widget.LinearLayoutManager; + +import com.simplecityapps.recyclerview_fastscroll.views.FastScrollRecyclerView; + +public class FastRecyclerView extends FastScrollRecyclerView { + + public FastRecyclerView(@NonNull Context context) { + super(context); + init(); + } + + public FastRecyclerView(@NonNull Context context, @Nullable AttributeSet attrs) { + super(context, attrs); + init(); + } + + public FastRecyclerView(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) { + super(context, attrs, defStyleAttr); + init(); + } + + private void init() { + setLayoutManager(new LinearLayoutManager(getContext())); + } +} diff --git a/app/src/main/java/com/seafile/seadroid2/view/webview/ImageLoadWebViewClient.java b/app/src/main/java/com/seafile/seadroid2/view/webview/ImageLoadWebViewClient.java index e223ea176..ada4ff66f 100644 --- a/app/src/main/java/com/seafile/seadroid2/view/webview/ImageLoadWebViewClient.java +++ b/app/src/main/java/com/seafile/seadroid2/view/webview/ImageLoadWebViewClient.java @@ -7,7 +7,7 @@ import android.webkit.WebView; import android.webkit.WebViewClient; -import com.seafile.seadroid2.framework.http.IO; +import com.seafile.seadroid2.framework.http.HttpIO; import com.seafile.seadroid2.framework.util.URLs; import java.io.ByteArrayInputStream; @@ -16,8 +16,8 @@ import java.util.Map; public class ImageLoadWebViewClient extends WebViewClient { - private final String TOKEN = IO.getInstanceWithLoggedIn().getToken(); - private final String SERVER_URL = IO.getInstanceWithLoggedIn().getServerUrl(); + private final String TOKEN = HttpIO.getCurrentInstance().getAccount().getToken(); + private final String SERVER_URL = HttpIO.getCurrentInstance().getServerUrl(); @Override diff --git a/app/src/main/res/drawable/scrollbar_vertical.xml b/app/src/main/res/drawable/scrollbar_vertical.xml new file mode 100644 index 000000000..f98af17a0 --- /dev/null +++ b/app/src/main/res/drawable/scrollbar_vertical.xml @@ -0,0 +1,13 @@ + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/scrollbar_vertical_bg.xml b/app/src/main/res/drawable/scrollbar_vertical_bg.xml new file mode 100644 index 000000000..8ff9db793 --- /dev/null +++ b/app/src/main/res/drawable/scrollbar_vertical_bg.xml @@ -0,0 +1,12 @@ + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/activity_image_preview.xml b/app/src/main/res/layout/activity_image_preview.xml index 572bc2802..7a00ac7fe 100644 --- a/app/src/main/res/layout/activity_image_preview.xml +++ b/app/src/main/res/layout/activity_image_preview.xml @@ -3,6 +3,7 @@ xmlns:app="http://schemas.android.com/apk/res-auto" android:layout_width="match_parent" android:layout_height="match_parent" + android:background="@color/black" android:fitsSystemWindows="true"> + + + + + diff --git a/app/src/main/res/layout/fragment_photo_view.xml b/app/src/main/res/layout/fragment_photo_view.xml index 12dafb64e..d21477f9e 100644 --- a/app/src/main/res/layout/fragment_photo_view.xml +++ b/app/src/main/res/layout/fragment_photo_view.xml @@ -1,15 +1,13 @@ + android:layout_height="match_parent"> - + diff --git a/app/src/main/res/layout/item_dirent.xml b/app/src/main/res/layout/item_dirent.xml index 6506c331a..a31280d6f 100644 --- a/app/src/main/res/layout/item_dirent.xml +++ b/app/src/main/res/layout/item_dirent.xml @@ -3,7 +3,7 @@ xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" - android:layout_height="wrap_content" + android:layout_height="@dimen/rv_item_dirent_height" android:background="?android:selectableItemBackground" android:paddingStart="8dp" android:paddingEnd="16dp"> @@ -23,8 +23,8 @@ - - - - + app:layout_constraintTop_toTopOf="parent" /> @@ -68,8 +69,8 @@ + android:padding="32dp"> + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/layout_frame_swipe_rv.xml b/app/src/main/res/layout/layout_frame_swipe_rv.xml index dae87d7e3..f5f5d54a2 100644 --- a/app/src/main/res/layout/layout_frame_swipe_rv.xml +++ b/app/src/main/res/layout/layout_frame_swipe_rv.xml @@ -1,5 +1,6 @@ @@ -10,11 +11,9 @@ + android:layout_height="match_parent" /> + \ No newline at end of file diff --git a/app/src/main/res/values-zh-rCN/strings.xml b/app/src/main/res/values-zh-rCN/strings.xml index 7060f2023..c8e10c8f9 100644 --- a/app/src/main/res/values-zh-rCN/strings.xml +++ b/app/src/main/res/values-zh-rCN/strings.xml @@ -237,7 +237,8 @@ 此文件夹是空的 请选择一个文件 安全验证失败 - %s的安全证书不受信任,是否继续? + %s的安全证书不受信任 + %s的安全证书不受信任,是否继续? %s使用了非法的安全证书,这意味着该连接不安全,是否继续? 连接不受信任 签发给 @@ -553,6 +554,7 @@ Email: support@seafile.com
扫描中 上传中 网络不可用 + 网络异常 需要密码 密码太弱 diff --git a/app/src/main/res/values/dimens.xml b/app/src/main/res/values/dimens.xml index 472c28285..5bf0d0beb 100644 --- a/app/src/main/res/values/dimens.xml +++ b/app/src/main/res/values/dimens.xml @@ -1,5 +1,4 @@ - - 10dp 68dp diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index eb4c760df..39c8a7446 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -253,6 +253,7 @@ Empty folder Choose a file Security check failed + The security certificate of %s is not trusted. The security certificate of %s is not trusted. Do you want to continue? %s uses an invalid security certificate. The connection is not secure. Continue anyway? Untrusted Connection diff --git a/app/src/main/res/values/styles.xml b/app/src/main/res/values/styles.xml index f7084a599..ea34965f6 100644 --- a/app/src/main/res/values/styles.xml +++ b/app/src/main/res/values/styles.xml @@ -235,4 +235,37 @@ @dimen/lv_subtitle_txt_size @color/item_subtitle_color + + + +