diff options
author | Stefan Niedermann <info@niedermann.it> | 2020-07-04 23:09:01 +0300 |
---|---|---|
committer | Stefan Niedermann <info@niedermann.it> | 2020-07-04 23:09:01 +0300 |
commit | 4db96f47c98fc23ab07d3b278c7737be9918a98d (patch) | |
tree | f72a1eb866a05bd857fbd698b06240e6f7d7bc50 /app/src/main/java/it | |
parent | a8d8a5538fb0dddb8a0f762ca3d84919fdaa4b93 (diff) | |
parent | 6cf7a252e57bfea325101ff5d2f9bfca36099171 (diff) |
Merge branch 'master' into 289-upload-sources
# Conflicts:
# app/src/main/res/values/strings.xml
Diffstat (limited to 'app/src/main/java/it')
72 files changed, 1237 insertions, 647 deletions
diff --git a/app/src/main/java/it/niedermann/nextcloud/deck/Application.java b/app/src/main/java/it/niedermann/nextcloud/deck/DeckApplication.java index 6fbf7c8a2..ff492c226 100644 --- a/app/src/main/java/it/niedermann/nextcloud/deck/Application.java +++ b/app/src/main/java/it/niedermann/nextcloud/deck/DeckApplication.java @@ -1,23 +1,20 @@ package it.niedermann.nextcloud.deck; +import android.app.Application; import android.content.Context; import android.content.SharedPreferences; -import android.graphics.Color; -import androidx.annotation.ColorInt; import androidx.annotation.NonNull; import androidx.preference.PreferenceManager; import com.jakewharton.threetenabp.AndroidThreeTen; -import it.niedermann.nextcloud.deck.ui.branding.BrandedActivity; - import static androidx.appcompat.app.AppCompatDelegate.MODE_NIGHT_NO; import static androidx.appcompat.app.AppCompatDelegate.MODE_NIGHT_YES; import static androidx.appcompat.app.AppCompatDelegate.setDefaultNightMode; import static androidx.multidex.MultiDex.install; -public class Application extends android.app.Application { +public class DeckApplication extends Application { public static final long NO_ACCOUNT_ID = -1L; public static final long NO_BOARD_ID = -1L; @@ -25,7 +22,7 @@ public class Application extends android.app.Application { @Override public void onCreate() { - setAppTheme(getAppTheme(getApplicationContext())); + setAppTheme(isDarkTheme(getApplicationContext())); super.onCreate(); AndroidThreeTen.init(this); } @@ -48,56 +45,11 @@ public class Application extends android.app.Application { setDefaultNightMode(darkTheme ? MODE_NIGHT_YES : MODE_NIGHT_NO); } - public static boolean getAppTheme(@NonNull Context context) { + public static boolean isDarkTheme(@NonNull Context context) { SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context); return prefs.getBoolean(context.getString(R.string.pref_key_dark_theme), false); } - // -------- - // Branding - // -------- - - public static boolean isBrandingEnabled(@NonNull Context context) { - SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context); - return prefs.getBoolean(context.getString(R.string.pref_key_branding), true); - } - - @ColorInt - public static int readBrandMainColor(@NonNull Context context) { - if (Application.isBrandingEnabled(context)) { - SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(context.getApplicationContext()); - DeckLog.log("--- Read: shared_preference_theme_main"); - return sharedPreferences.getInt(context.getString(R.string.shared_preference_theme_main), context.getApplicationContext().getResources().getColor(R.color.primary)); - } else { - return context.getResources().getColor(R.color.primary); - } - } - - @ColorInt - public static int readBrandTextColor(@NonNull Context context) { - if (isBrandingEnabled(context)) { - SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(context.getApplicationContext()); - DeckLog.log("--- Read: shared_preference_theme_text"); - return sharedPreferences.getInt(context.getString(R.string.shared_preference_theme_text), context.getApplicationContext().getResources().getColor(android.R.color.white)); - } else { - return Color.WHITE; - } - } - - public static void saveBrandColors(@NonNull Context context, @ColorInt int mainColor, @ColorInt int textColor) { - if (isBrandingEnabled(context) && context instanceof BrandedActivity) { - final BrandedActivity activity = (BrandedActivity) context; - activity.applyBrand(mainColor, textColor); - BrandedActivity.applyBrandToStatusbar(activity.getWindow(), mainColor, textColor); - } - SharedPreferences.Editor editor = PreferenceManager.getDefaultSharedPreferences(context).edit(); - DeckLog.log("--- Write: shared_preference_theme_main" + " | " + mainColor); - DeckLog.log("--- Write: shared_preference_theme_text" + " | " + textColor); - editor.putInt(context.getString(R.string.shared_preference_theme_main), mainColor); - editor.putInt(context.getString(R.string.shared_preference_theme_text), textColor); - editor.apply(); - } - // -------------------------------------- // Current account / board / stack states // -------------------------------------- diff --git a/app/src/main/java/it/niedermann/nextcloud/deck/api/GsonConfig.java b/app/src/main/java/it/niedermann/nextcloud/deck/api/GsonConfig.java index f81c9f191..29b3706fa 100644 --- a/app/src/main/java/it/niedermann/nextcloud/deck/api/GsonConfig.java +++ b/app/src/main/java/it/niedermann/nextcloud/deck/api/GsonConfig.java @@ -16,6 +16,8 @@ import it.niedermann.nextcloud.deck.model.full.FullStack; import it.niedermann.nextcloud.deck.model.ocs.Activity; import it.niedermann.nextcloud.deck.model.ocs.Capabilities; import it.niedermann.nextcloud.deck.model.ocs.comment.OcsComment; +import it.niedermann.nextcloud.deck.model.ocs.user.OcsUser; +import it.niedermann.nextcloud.deck.model.ocs.user.OcsUserList; /** * Created by david on 27.06.17. @@ -37,9 +39,9 @@ public class GsonConfig { Type stackList = new TypeToken<List<FullStack>>() {}.getType(); Type stack = new TypeToken<FullStack>() {}.getType(); Type capabilities = new TypeToken<Capabilities>() {}.getType(); - Type capabilitiesList = new TypeToken<List<Capabilities>>() {}.getType(); + Type ocsUserList = new TypeToken<OcsUserList>() {}.getType(); + Type ocsUser = new TypeToken<OcsUser>() {}.getType(); Type activity = new TypeToken<Activity>() {}.getType(); - Type activityList = new TypeToken<List<Activity>>() {}.getType(); Type attachment = new TypeToken<Attachment>() {}.getType(); Type attachmentList = new TypeToken<List<Attachment>>() {}.getType(); Type comment = new TypeToken<OcsComment>() {}.getType(); @@ -56,9 +58,9 @@ public class GsonConfig { .registerTypeAdapter(label, new NextcloudDeserializer<>("label", Label.class)) .registerTypeAdapter(stackList, new NextcloudArrayDeserializer<>("stacks", FullStack.class)) .registerTypeAdapter(stack, new NextcloudDeserializer<>("stack", FullStack.class)) - .registerTypeAdapter(capabilitiesList, new NextcloudArrayDeserializer<>("capabilities", Capabilities.class)) .registerTypeAdapter(capabilities, new NextcloudDeserializer<>("capability", Capabilities.class)) - .registerTypeAdapter(activityList, new NextcloudDeserializer<>("activities", Activity.class)) + .registerTypeAdapter(ocsUserList, new NextcloudDeserializer<>("ocsUserList", OcsUserList.class)) + .registerTypeAdapter(ocsUser, new NextcloudDeserializer<>("ocsUser", OcsUser.class)) .registerTypeAdapter(activity, new NextcloudDeserializer<>("activity", Activity.class)) .registerTypeAdapter(attachmentList, new NextcloudArrayDeserializer<>("attachments", Attachment.class)) .registerTypeAdapter(attachment, new NextcloudDeserializer<>("attachment", Attachment.class)) diff --git a/app/src/main/java/it/niedermann/nextcloud/deck/api/JsonToEntityParser.java b/app/src/main/java/it/niedermann/nextcloud/deck/api/JsonToEntityParser.java index c64e5db1d..6dedefa01 100644 --- a/app/src/main/java/it/niedermann/nextcloud/deck/api/JsonToEntityParser.java +++ b/app/src/main/java/it/niedermann/nextcloud/deck/api/JsonToEntityParser.java @@ -32,6 +32,8 @@ import it.niedermann.nextcloud.deck.model.ocs.Version; import it.niedermann.nextcloud.deck.model.ocs.comment.DeckComment; import it.niedermann.nextcloud.deck.model.ocs.comment.Mention; import it.niedermann.nextcloud.deck.model.ocs.comment.OcsComment; +import it.niedermann.nextcloud.deck.model.ocs.user.OcsUser; +import it.niedermann.nextcloud.deck.model.ocs.user.OcsUserList; import static it.niedermann.nextcloud.deck.exceptions.DeckException.Hint.CAPABILITIES_VERSION_NOT_PARSABLE; @@ -50,6 +52,10 @@ public class JsonToEntityParser { return (T) parseActivity(obj); } else if (mType == Capabilities.class) { return (T) parseCapabilities(obj); + } else if (mType == OcsUserList.class) { + return (T) parseOcsUserList(obj); + } else if (mType == OcsUser.class) { + return (T) parseOcsUser(obj); } else if (mType == Attachment.class) { return (T) parseAttachment(obj); } else if (mType == OcsComment.class) { @@ -58,6 +64,43 @@ public class JsonToEntityParser { throw new IllegalArgumentException("unregistered type: " + mType.getCanonicalName()); } + private static OcsUser parseOcsUser(JsonObject obj) { + DeckLog.verbose(obj.toString()); + OcsUser ocsUser = new OcsUser(); + TraceableException.makeTraceableIfFails(() -> { + JsonElement data = obj.get("ocs").getAsJsonObject().get("data"); + if (!data.isJsonNull()) { + JsonObject jsonObject = data.getAsJsonObject(); + if (jsonObject.has("id")) { + ocsUser.setId(getNullAsEmptyString(jsonObject.get("id"))); + } + if (jsonObject.has("displayname")) { + ocsUser.setDisplayName(getNullAsEmptyString(jsonObject.get("displayname"))); + } + } + + }, obj); + return ocsUser; + } + + private static OcsUserList parseOcsUserList(JsonObject obj) { + DeckLog.verbose(obj.toString()); + OcsUserList ocsUserList = new OcsUserList(); + TraceableException.makeTraceableIfFails(() -> { + JsonElement data = obj.get("ocs").getAsJsonObject().get("data"); + if (!data.isJsonNull() && data.getAsJsonObject().has("users")) { + JsonElement users = data.getAsJsonObject().get("users"); + if (!users.isJsonNull() && users.isJsonArray()) { + for (JsonElement userElement : users.getAsJsonArray()) { + ocsUserList.add(userElement.getAsString()); + } + } + } + + }, obj); + return ocsUserList; + } + private static OcsComment parseOcsComment(JsonObject obj) { DeckLog.verbose(obj.toString()); OcsComment comment = new OcsComment(); diff --git a/app/src/main/java/it/niedermann/nextcloud/deck/api/NextcloudServerAPI.java b/app/src/main/java/it/niedermann/nextcloud/deck/api/NextcloudServerAPI.java index b3e4f4c84..d69ee8513 100644 --- a/app/src/main/java/it/niedermann/nextcloud/deck/api/NextcloudServerAPI.java +++ b/app/src/main/java/it/niedermann/nextcloud/deck/api/NextcloudServerAPI.java @@ -8,6 +8,8 @@ import it.niedermann.nextcloud.deck.model.ocs.Activity; import it.niedermann.nextcloud.deck.model.ocs.Capabilities; import it.niedermann.nextcloud.deck.model.ocs.comment.DeckComment; import it.niedermann.nextcloud.deck.model.ocs.comment.OcsComment; +import it.niedermann.nextcloud.deck.model.ocs.user.OcsUser; +import it.niedermann.nextcloud.deck.model.ocs.user.OcsUserList; import retrofit2.http.Body; import retrofit2.http.DELETE; import retrofit2.http.GET; @@ -22,6 +24,12 @@ public interface NextcloudServerAPI { @GET("cloud/capabilities?format=json") Observable<Capabilities> getCapabilities(); + @GET("cloud/users?format=json") + Observable<OcsUserList> getAllUsers(); + + @GET("cloud/users/{uid}?format=json") + Observable<OcsUser> getUserDetails(@Path("uid") String uid); + @GET("apps/activity/api/v2/activity/filter?format=json&object_type=deck_card&limit=50&since=-1&sort=asc") Observable<List<Activity>> getActivitiesForCard(@Query("object_id") long cardId); diff --git a/app/src/main/java/it/niedermann/nextcloud/deck/api/RequestHelper.java b/app/src/main/java/it/niedermann/nextcloud/deck/api/RequestHelper.java index 44ba28802..6bdfbbee1 100644 --- a/app/src/main/java/it/niedermann/nextcloud/deck/api/RequestHelper.java +++ b/app/src/main/java/it/niedermann/nextcloud/deck/api/RequestHelper.java @@ -11,9 +11,7 @@ import it.niedermann.nextcloud.deck.DeckLog; public class RequestHelper { static { - RxJavaPlugins.setErrorHandler(e -> { - DeckLog.logError(e); - }); + RxJavaPlugins.setErrorHandler(DeckLog::logError); } public static <T> void request(final ApiProvider provider, final ObservableProvider<T> call, final IResponseCallback<T> callback) { diff --git a/app/src/main/java/it/niedermann/nextcloud/deck/model/ocs/user/OcsUser.java b/app/src/main/java/it/niedermann/nextcloud/deck/model/ocs/user/OcsUser.java new file mode 100644 index 000000000..24627cb9a --- /dev/null +++ b/app/src/main/java/it/niedermann/nextcloud/deck/model/ocs/user/OcsUser.java @@ -0,0 +1,49 @@ +package it.niedermann.nextcloud.deck.model.ocs.user; + +public class OcsUser { + String id; + String displayName; + + public OcsUser() { + + } + + public OcsUser(String id, String displayName) { + this.id = id; + this.displayName = displayName; + } + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public String getDisplayName() { + return displayName; + } + + public void setDisplayName(String displayName) { + this.displayName = displayName; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + OcsUser ocsUser = (OcsUser) o; + + if (id != null ? !id.equals(ocsUser.id) : ocsUser.id != null) return false; + return displayName != null ? displayName.equals(ocsUser.displayName) : ocsUser.displayName == null; + } + + @Override + public int hashCode() { + int result = id != null ? id.hashCode() : 0; + result = 31 * result + (displayName != null ? displayName.hashCode() : 0); + return result; + } +} diff --git a/app/src/main/java/it/niedermann/nextcloud/deck/model/ocs/user/OcsUserList.java b/app/src/main/java/it/niedermann/nextcloud/deck/model/ocs/user/OcsUserList.java new file mode 100644 index 000000000..a10ae005d --- /dev/null +++ b/app/src/main/java/it/niedermann/nextcloud/deck/model/ocs/user/OcsUserList.java @@ -0,0 +1,7 @@ +package it.niedermann.nextcloud.deck.model.ocs.user; + +import java.util.ArrayList; + +public class OcsUserList extends ArrayList<String> { + // nothing. +} diff --git a/app/src/main/java/it/niedermann/nextcloud/deck/persistence/sync/SyncManager.java b/app/src/main/java/it/niedermann/nextcloud/deck/persistence/sync/SyncManager.java index 1cb7112e7..9ed9035cf 100644 --- a/app/src/main/java/it/niedermann/nextcloud/deck/persistence/sync/SyncManager.java +++ b/app/src/main/java/it/niedermann/nextcloud/deck/persistence/sync/SyncManager.java @@ -3,11 +3,13 @@ package it.niedermann.nextcloud.deck.persistence.sync; import android.content.Context; import android.database.sqlite.SQLiteConstraintException; +import androidx.annotation.AnyThread; import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.annotation.WorkerThread; import androidx.core.util.Pair; import androidx.lifecycle.LiveData; +import androidx.lifecycle.MediatorLiveData; import androidx.lifecycle.MutableLiveData; import com.nextcloud.android.sso.exceptions.NextcloudHttpRequestFailedException; @@ -44,8 +46,11 @@ import it.niedermann.nextcloud.deck.model.ocs.Capabilities; import it.niedermann.nextcloud.deck.model.ocs.comment.DeckComment; import it.niedermann.nextcloud.deck.model.ocs.comment.OcsComment; import it.niedermann.nextcloud.deck.model.ocs.comment.full.FullDeckComment; +import it.niedermann.nextcloud.deck.model.ocs.user.OcsUser; +import it.niedermann.nextcloud.deck.model.ocs.user.OcsUserList; import it.niedermann.nextcloud.deck.persistence.sync.adapters.ServerAdapter; import it.niedermann.nextcloud.deck.persistence.sync.adapters.db.DataBaseAdapter; +import it.niedermann.nextcloud.deck.persistence.sync.adapters.db.util.LiveDataHelper; import it.niedermann.nextcloud.deck.persistence.sync.adapters.db.util.WrappedLiveData; import it.niedermann.nextcloud.deck.persistence.sync.helpers.DataPropagationHelper; import it.niedermann.nextcloud.deck.persistence.sync.helpers.SyncHelper; @@ -59,6 +64,7 @@ import it.niedermann.nextcloud.deck.persistence.sync.helpers.providers.CardPropa import it.niedermann.nextcloud.deck.persistence.sync.helpers.providers.DeckCommentsDataProvider; import it.niedermann.nextcloud.deck.persistence.sync.helpers.providers.LabelDataProvider; import it.niedermann.nextcloud.deck.persistence.sync.helpers.providers.StackDataProvider; +import it.niedermann.nextcloud.deck.persistence.sync.helpers.providers.partial.BoardWitAclDownSyncDataProvider; import it.niedermann.nextcloud.deck.util.DateUtil; import static java.net.HttpURLConnection.HTTP_UNAVAILABLE; @@ -73,10 +79,12 @@ public class SyncManager { @NonNull private ServerAdapter serverAdapter; + @AnyThread public SyncManager(@NonNull Context context) { this(context, null); } + @AnyThread public SyncManager(@NonNull Context context, @Nullable String ssoAccountName) { appContext = context.getApplicationContext(); LastSyncUtil.init(appContext); @@ -84,11 +92,13 @@ public class SyncManager { this.serverAdapter = new ServerAdapter(appContext, ssoAccountName); } - private void doAsync(Runnable r) { + @AnyThread + private void doAsync(@NonNull Runnable r) { new Thread(r).start(); } - public MutableLiveData<FullCard> synchronizeCardByRemoteId(long cardRemoteId, Account account) { + @AnyThread + public MutableLiveData<FullCard> synchronizeCardByRemoteId(long cardRemoteId, @NonNull Account account) { MutableLiveData<FullCard> liveData = new MutableLiveData<>(); doAsync(() -> { Long accountId = account.getId(); @@ -114,10 +124,12 @@ public class SyncManager { } // TODO if the card does not exist yet, try to synchronize it first, instead of directly returning null. If sync failed, return null. - public LiveData<Long> getLocalBoardIdByCardRemoteIdAndAccount(long cardRemoteId, Account account) { + @AnyThread + public LiveData<Long> getLocalBoardIdByCardRemoteIdAndAccount(long cardRemoteId, @NonNull Account account) { return dataBaseAdapter.getLocalBoardIdByCardRemoteIdAndAccountId(cardRemoteId, account.getId()); } + @AnyThread public boolean synchronizeEverything() { List<Account> accounts = dataBaseAdapter.getAllAccountsDirectly(); if (accounts.size() > 0) { @@ -150,11 +162,13 @@ public class SyncManager { return true; } - public void synchronize(IResponseCallback<Boolean> responseCallback) { - if (responseCallback == null || - responseCallback.getAccount() == null || - responseCallback.getAccount().getId() == null) { - throw new IllegalArgumentException("please provide an account ID."); + @AnyThread + public void synchronize(@NonNull IResponseCallback<Boolean> responseCallback) { + if(responseCallback.getAccount() == null) { + throw new IllegalArgumentException(Account.class.getSimpleName() + " object in given " + IResponseCallback.class.getSimpleName() + " must not be null."); + } + if(responseCallback.getAccount().getId() == null) { + throw new IllegalArgumentException(Account.class.getSimpleName() + " object in given " + IResponseCallback.class.getSimpleName() + " must contain a valid id, but given id was null."); } doAsync(() -> refreshCapabilities(new IResponseCallback<Capabilities>(responseCallback.getAccount()) { @Override @@ -254,11 +268,13 @@ public class SyncManager { // return remoteEntity; // } + @AnyThread public LiveData<Boolean> hasAccounts() { return dataBaseAdapter.hasAccounts(); } - public WrappedLiveData<Account> createAccount(Account accout) { + @AnyThread + public WrappedLiveData<Account> createAccount(@NonNull Account accout) { return dataBaseAdapter.createAccount(accout); } @@ -277,19 +293,56 @@ public class SyncManager { dataBaseAdapter.updateAccount(account); } + @AnyThread public LiveData<Account> readAccount(long id) { return dataBaseAdapter.readAccount(id); } - public LiveData<Account> readAccount(String name) { + @AnyThread + public LiveData<Account> readAccount(@Nullable String name) { return dataBaseAdapter.readAccount(name); } + @AnyThread public LiveData<List<Account>> readAccounts() { return dataBaseAdapter.readAccounts(); } - public void refreshCapabilities(IResponseCallback<Capabilities> callback) { + /** + * <p> + * Since the return value is a {@link LiveData}, it should immediately return the available values from the database + * and then perform a synchronization (not full but only for the needed data) to update the return value. + * <p> + * See https://github.com/stefan-niedermann/nextcloud-deck/issues/498#issuecomment-631615680 + * + * @param host e. g. "example.com:4711" + * @return a {@link List<Account>} of {@link Account}s which are + * - located at the given {@param host} + * - and have the permission to read the board with the given {@param boardRemoteId} (aka the {@link Board} is shared with this {@link User}). + */ + @AnyThread + public LiveData<List<Account>> readAccountsForHostWithReadAccessToBoard(String host, long boardRemoteId) { + MediatorLiveData<List<Account>> liveData = new MediatorLiveData<>(); + liveData.addSource(dataBaseAdapter.readAccountsForHostWithReadAccessToBoard(host, boardRemoteId), accounts -> { + liveData.postValue(accounts); + doAsync(() -> { + for (Account account : accounts) { + new SyncHelper(serverAdapter, dataBaseAdapter, null) + .setResponseCallback(new IResponseCallback<Boolean>(account) { + @Override + public void onResponse(Boolean response) { + liveData.postValue(dataBaseAdapter.readAccountsForHostWithReadAccessToBoardDirectly(host, boardRemoteId)); + } + }).doSyncFor(new BoardWitAclDownSyncDataProvider()); + } + }); + }); + + return liveData; + } + + @AnyThread + public void refreshCapabilities(@NonNull IResponseCallback<Capabilities> callback) { doAsync(() -> { try { serverAdapter.getCapabilities(new IResponseCallback<Capabilities>(callback.getAccount()) { @@ -324,6 +377,34 @@ public class SyncManager { } catch (OfflineException e) { callback.onError(e); } + + try { + serverAdapter.getAllOcsUsers(new IResponseCallback<OcsUserList>(callback.getAccount()) { + @Override + public void onResponse(OcsUserList response) { + Long accountId = callback.getAccount().getId(); + for (String ocsUserName : response) { + User existingUser = dataBaseAdapter.getUserByUidDirectly(accountId, ocsUserName); + if (existingUser == null) { + // we don't know this user, lets get some details... + serverAdapter.getOcsUserDetails(ocsUserName, new IResponseCallback<OcsUser>(callback.getAccount()) { + @Override + public void onResponse(OcsUser response) { + User newUser = new User(); + newUser.setStatus(DBStatus.UP_TO_DATE.getId()); + newUser.setPrimaryKey(ocsUserName); + newUser.setUid(ocsUserName); + newUser.setDisplayname(response.getDisplayName()); + dataBaseAdapter.createUser(accountId, newUser); + } + }); + } + } + } + }); + } catch (OfflineException ignored) { + // Nothing to do here... + } }); } @@ -332,6 +413,7 @@ public class SyncManager { * @param archived Decides whether only archived or not-archived boards for the specified account will be returned * @return all archived or non-archived <code>Board</code>s depending on <code>archived</code> parameter */ + @AnyThread public LiveData<List<Board>> getBoards(long accountId, boolean archived) { return dataBaseAdapter.getBoards(accountId, archived); } @@ -341,6 +423,7 @@ public class SyncManager { * @param archived Decides whether only archived or not-archived boards for the specified account will be returned * @return all archived or non-archived <code>FullBoard</code>s depending on <code>archived</code> parameter */ + @AnyThread public LiveData<List<FullBoard>> getFullBoards(long accountId, boolean archived) { return dataBaseAdapter.getFullBoards(accountId, archived); } @@ -351,15 +434,18 @@ public class SyncManager { * @param accountId ID of the account * @return all non-archived <code>Board</code>s with edit permission */ + @AnyThread public LiveData<List<Board>> getBoardsWithEditPermission(long accountId) { return dataBaseAdapter.getBoardsWithEditPermission(accountId); } + @AnyThread public LiveData<Boolean> hasArchivedBoards(long accountId) { return dataBaseAdapter.hasArchivedBoards(accountId); } - public LiveData<FullBoard> createBoard(long accountId, Board board) { + @AnyThread + public LiveData<FullBoard> createBoard(long accountId, @NonNull Board board) { MutableLiveData<FullBoard> liveData = new MutableLiveData<>(); doAsync(() -> { Account account = dataBaseAdapter.getAccountByIdDirectly(accountId); @@ -390,11 +476,62 @@ public class SyncManager { * Owner of the target {@link Board} will be the {@link User} with the {@link Account} of {@param targetAccountId}. * Does <strong>not</strong> clone any {@link Card} or {@link AccessControl} from the origin {@link Board}. */ - public LiveData<FullBoard> cloneBoard(long originAccountId, long originBoardLocalId, long targetAccountId, String targetBoardTitle, String targetBoardColor) { - throw new UnsupportedOperationException("Not yet implemented"); + @AnyThread + public WrappedLiveData<FullBoard> cloneBoard(long originAccountId, long originBoardLocalId, long targetAccountId, String targetBoardTitle, String targetBoardColor) { + WrappedLiveData<FullBoard> liveData = new WrappedLiveData<>(); + + doAsync(() -> { + Account originAccount = dataBaseAdapter.getAccountByIdDirectly(originAccountId); + User newOwner = dataBaseAdapter.getUserByUidDirectly(originAccountId, originAccount.getUserName()); + FullBoard originalBoard = dataBaseAdapter.getFullBoardByLocalIdDirectly(originAccountId, originBoardLocalId); + originalBoard.setAccountId(targetAccountId); + originalBoard.getBoard().setTitle(targetBoardTitle); + originalBoard.getBoard().setColor(targetBoardColor); + originalBoard.getBoard().setOwnerId(newOwner.getId()); + originalBoard.setStatusEnum(DBStatus.LOCAL_EDITED); + originalBoard.setOwner(newOwner); + originalBoard.setId(null); + originalBoard.setLocalId(null); + long newBoardId = dataBaseAdapter.createBoardDirectly(originAccountId, originalBoard.getBoard()); + originalBoard.setLocalId(newBoardId); + + for (Stack stack : originalBoard.getStacks()) { + stack.setLocalId(null); + stack.setId(null); + stack.setStatusEnum(DBStatus.LOCAL_EDITED); + stack.setAccountId(targetAccountId); + stack.setBoardId(newBoardId); + dataBaseAdapter.createStack(targetAccountId, stack); + } + for (Label label : originalBoard.getLabels()) { + label.setLocalId(null); + label.setId(null); + label.setAccountId(targetAccountId); + label.setStatusEnum(DBStatus.LOCAL_EDITED); + label.setBoardId(newBoardId); + dataBaseAdapter.createLabel(targetAccountId, label); + } + Account targetAccount = dataBaseAdapter.getAccountByIdDirectly(targetAccountId); + new SyncHelper(serverAdapter, dataBaseAdapter, null) + .setResponseCallback(new IResponseCallback<Boolean>(targetAccount) { + @Override + public void onResponse(Boolean response) { + liveData.postValue(dataBaseAdapter.getFullBoardByLocalIdDirectly(targetAccountId, newBoardId)); + } + + @Override + public void onError(Throwable throwable) { + super.onError(throwable); + liveData.postError(throwable); + } + }).doSyncFor(new BoardDataProvider()); + + }); + return liveData; } - public LiveData<List<it.niedermann.nextcloud.deck.model.ocs.Activity>> syncActivitiesForCard(Card card) { + @AnyThread + public LiveData<List<it.niedermann.nextcloud.deck.model.ocs.Activity>> syncActivitiesForCard(@NonNull Card card) { doAsync(() -> { if (serverAdapter.hasInternetConnection()) { if (card.getId() != null) { @@ -413,7 +550,8 @@ public class SyncManager { return dataBaseAdapter.getActivitiesForCard(card.getLocalId()); } - public void addCommentToCard(long accountId, long cardId, DeckComment comment) { + @AnyThread + public void addCommentToCard(long accountId, long cardId, @NonNull DeckComment comment) { doAsync(() -> { Account account = dataBaseAdapter.getAccountByIdDirectly(accountId); Card card = dataBaseAdapter.getCardByLocalIdDirectly(accountId, cardId); @@ -427,6 +565,7 @@ public class SyncManager { }); } + @AnyThread public void updateComment(long accountId, long localCardId, long localCommentId, String comment) { doAsync(() -> { Account account = dataBaseAdapter.getAccountByIdDirectly(accountId); @@ -443,6 +582,7 @@ public class SyncManager { }); } + @AnyThread public WrappedLiveData<Void> deleteComment(long accountId, long localCardId, long localCommentId) { WrappedLiveData<Void> liveData = new WrappedLiveData<>(); doAsync(() -> { @@ -460,7 +600,8 @@ public class SyncManager { return dataBaseAdapter.getFullCommentsForLocalCardId(localCardId); } - public WrappedLiveData<Void> deleteBoard(Board board) { + @AnyThread + public WrappedLiveData<Void> deleteBoard(@NonNull Board board) { WrappedLiveData<Void> liveData = new WrappedLiveData<>(); doAsync(() -> { long accountId = board.getAccountId(); @@ -471,7 +612,8 @@ public class SyncManager { return liveData; } - public WrappedLiveData<FullBoard> updateBoard(FullBoard board) { + @AnyThread + public WrappedLiveData<FullBoard> updateBoard(@NonNull FullBoard board) { WrappedLiveData<FullBoard> liveData = new WrappedLiveData<>(); long accountId = board.getAccountId(); doAsync(() -> { @@ -499,6 +641,7 @@ public class SyncManager { return dataBaseAdapter.getStack(accountId, localStackId); } + @AnyThread public WrappedLiveData<AccessControl> createAccessControl(long accountId, AccessControl entity) { WrappedLiveData<AccessControl> liveData = new WrappedLiveData<>(); doAsync(() -> { @@ -515,6 +658,7 @@ public class SyncManager { return liveData; } + @WorkerThread public AccessControl getAccessControlByRemoteIdDirectly(long accountId, Long id) { return dataBaseAdapter.getAccessControlByRemoteIdDirectly(accountId, id); } @@ -523,7 +667,8 @@ public class SyncManager { return dataBaseAdapter.getAccessControlByLocalBoardId(accountId, id); } - public WrappedLiveData<AccessControl> updateAccessControl(AccessControl entity) { + @AnyThread + public WrappedLiveData<AccessControl> updateAccessControl(@NonNull AccessControl entity) { WrappedLiveData<AccessControl> liveData = new WrappedLiveData<>(); doAsync(() -> { Account account = dataBaseAdapter.getAccountByIdDirectly(entity.getAccountId()); @@ -534,7 +679,8 @@ public class SyncManager { return liveData; } - private <T> IResponseCallback<T> getCallbackToLiveDataConverter(Account account, WrappedLiveData<T> liveData) { + @AnyThread + private <T> IResponseCallback<T> getCallbackToLiveDataConverter(Account account, @NonNull WrappedLiveData<T> liveData) { return new IResponseCallback<T>(account) { @Override public void onResponse(T response) { @@ -548,7 +694,8 @@ public class SyncManager { }; } - public WrappedLiveData<Void> deleteAccessControl(AccessControl entity) { + @AnyThread + public WrappedLiveData<Void> deleteAccessControl(@NonNull AccessControl entity) { WrappedLiveData<Void> liveData = new WrappedLiveData<>(); doAsync(() -> { Account account = dataBaseAdapter.getAccountByIdDirectly(entity.getAccountId()); @@ -577,8 +724,8 @@ public class SyncManager { return dataBaseAdapter.getFullBoardById(accountId, localId); } - - public WrappedLiveData<FullStack> createStack(long accountId, Stack stack) { + @AnyThread + public WrappedLiveData<FullStack> createStack(long accountId, @NonNull Stack stack) { WrappedLiveData<FullStack> liveData = new WrappedLiveData<>(); doAsync(() -> { Account account = dataBaseAdapter.getAccountByIdDirectly(accountId); @@ -594,6 +741,7 @@ public class SyncManager { return liveData; } + @AnyThread public WrappedLiveData<Void> deleteStack(long accountId, long stackLocalId, long boardLocalId) { WrappedLiveData<Void> liveData = new WrappedLiveData<>(); doAsync(() -> { @@ -605,7 +753,8 @@ public class SyncManager { return liveData; } - public WrappedLiveData<FullStack> updateStack(FullStack stack) { + @AnyThread + public WrappedLiveData<FullStack> updateStack(@NonNull FullStack stack) { WrappedLiveData<FullStack> liveData = new WrappedLiveData<>(); doAsync(() -> { Account account = dataBaseAdapter.getAccountByIdDirectly(stack.getAccountId()); @@ -616,6 +765,7 @@ public class SyncManager { } + @AnyThread private void updateStack(@NonNull Account account, @NonNull FullBoard board, @NonNull FullStack stack, @Nullable WrappedLiveData<FullStack> liveData) { doAsync(() -> { new DataPropagationHelper(serverAdapter, dataBaseAdapter).updateEntity(new StackDataProvider(null, board), stack, new IResponseCallback<FullStack>(account) { @@ -641,6 +791,7 @@ public class SyncManager { * * @param stackLocalIds The first item of the pair will be updated first */ + @AnyThread public void swapStackOrder(long accountId, long boardLocalId, @NonNull Pair<Long, Long> stackLocalIds) { if (stackLocalIds.first == null || stackLocalIds.second == null) { throw new IllegalArgumentException("Given stackLocalIds must not be null"); @@ -711,8 +862,8 @@ public class SyncManager { // return liveData; // } - public LiveData<FullCard> createFullCard(long accountId, long localBoardId, long localStackId, FullCard card) { - + @AnyThread + public LiveData<FullCard> createFullCard(long accountId, long localBoardId, long localStackId, @NonNull FullCard card) { MutableLiveData<FullCard> liveData = new MutableLiveData<>(); doAsync(() -> { Account account = dataBaseAdapter.getAccountByIdDirectly(accountId); @@ -760,7 +911,8 @@ public class SyncManager { return liveData; } - public WrappedLiveData<Void> deleteCard(Card card) { + @AnyThread + public WrappedLiveData<Void> deleteCard(@NonNull Card card) { WrappedLiveData<Void> liveData = new WrappedLiveData<>(); doAsync(() -> { FullCard fullCard = dataBaseAdapter.getFullCardByLocalIdDirectly(card.getAccountId(), card.getLocalId()); @@ -775,7 +927,8 @@ public class SyncManager { return liveData; } - public WrappedLiveData<FullCard> archiveCard(FullCard card) { + @AnyThread + public WrappedLiveData<FullCard> archiveCard(@NonNull FullCard card) { WrappedLiveData<FullCard> liveData = new WrappedLiveData<>(); doAsync(() -> { Account account = dataBaseAdapter.getAccountByIdDirectly(card.getAccountId()); @@ -787,11 +940,12 @@ public class SyncManager { return liveData; } - private void updateCardForArchive(Account account, FullStack stack, Board board, FullCard card, IResponseCallback<FullCard> callback) { + private void updateCardForArchive(Account account, FullStack stack, Board board, FullCard card, @NonNull IResponseCallback<FullCard> callback) { new DataPropagationHelper(serverAdapter, dataBaseAdapter).updateEntity(new CardDataProvider(null, board, stack), card, callback); } - public WrappedLiveData<FullCard> dearchiveCard(FullCard card) { + @AnyThread + public WrappedLiveData<FullCard> dearchiveCard(@NonNull FullCard card) { WrappedLiveData<FullCard> liveData = new WrappedLiveData<>(); doAsync(() -> { Account account = dataBaseAdapter.getAccountByIdDirectly(card.getAccountId()); @@ -803,6 +957,7 @@ public class SyncManager { return liveData; } + @AnyThread public WrappedLiveData<Void> archiveCardsInStack(long accountId, long stackLocalId) { WrappedLiveData<Void> liveData = new WrappedLiveData<>(); doAsync(() -> { @@ -840,7 +995,8 @@ public class SyncManager { return liveData; } - public void archiveBoard(Board board) { + @AnyThread + public void archiveBoard(@NonNull Board board) { doAsync(() -> { FullBoard b = dataBaseAdapter.getFullBoardByLocalIdDirectly(board.getAccountId(), board.getLocalId()); b.getBoard().setArchived(true); @@ -848,7 +1004,8 @@ public class SyncManager { }); } - public void dearchiveBoard(Board board) { + @AnyThread + public void dearchiveBoard(@NonNull Board board) { doAsync(() -> { FullBoard b = dataBaseAdapter.getFullBoardByLocalIdDirectly(board.getAccountId(), board.getLocalId()); b.getBoard().setArchived(false); @@ -856,7 +1013,8 @@ public class SyncManager { }); } - public WrappedLiveData<FullCard> updateCard(FullCard card) { + @AnyThread + public WrappedLiveData<FullCard> updateCard(@NonNull FullCard card) { WrappedLiveData<FullCard> liveData = new WrappedLiveData<>(); doAsync(() -> { FullCard fullCardFromDB = dataBaseAdapter.getFullCardByLocalIdDirectly(card.getAccountId(), card.getLocalId()); @@ -912,6 +1070,7 @@ public class SyncManager { /** * Moves the given {@param originCardLocalId} to the new target coordinates specified by {@param targetAccountId}, {@param targetBoardLocalId} and {@param targetStackLocalId}. * If the {@param targetBoardLocalId} changes, this will apply some logic to make sure that we migrate as much data as possible without the risk of getting an illegal state. + * Attachments are not copied or anything. * <p> * 1) {@link FullCard#labels} * <p> @@ -925,14 +1084,134 @@ public class SyncManager { * a) If the {@link User} has at least view permission at the target {@link Board}, keep it (<strong>can</strong> be the case if the target {@link Account} is the same as the origin {@link Account} <strong>or</strong> the target {@link Account} is on the same Nextcloud instance as the origin {@link Account} * b) Else {@link #unassignUserFromCard(User, Card)} (will always be the case if the target {@link Account} is on another Nextcloud isntance as the origin {@link Account}) * <p> - * + * <p> * https://github.com/stefan-niedermann/nextcloud-deck/issues/453 */ @SuppressWarnings("JavadocReference") + @AnyThread public WrappedLiveData<Void> moveCard(long originAccountId, long originCardLocalId, long targetAccountId, long targetBoardLocalId, long targetStackLocalId) { - throw new UnsupportedOperationException("Not yet implemented"); + return LiveDataHelper.wrapInLiveData(() -> { + + FullCard originalCard = dataBaseAdapter.getFullCardByLocalIdDirectly(originAccountId, originCardLocalId); + int newIndex = dataBaseAdapter.getHighestCardOrderInStack(targetStackLocalId) + 1; + FullBoard originalBoard = dataBaseAdapter.getFullBoardByLocalCardIdDirectly(originCardLocalId); + // ### maybe shortcut possible? (just moved to another stack) + if (targetBoardLocalId == originalBoard.getLocalId()) { + reorder(originAccountId, originalCard, targetStackLocalId, newIndex); + return null; + } + // ### get rid of original card where it is now. + Card originalInnerCard = originalCard.getCard(); + deleteCard(originalInnerCard); + // ### clone card itself + Card targetCard = originalInnerCard; + targetCard.setAccountId(targetAccountId); + targetCard.setId(null); + targetCard.setLocalId(null); + targetCard.setStatusEnum(DBStatus.LOCAL_EDITED); + targetCard.setStackId(targetStackLocalId); + targetCard.setOrder(newIndex); + //TODO: this needs to propagate to server as well, since anything else propagates as well (otherwise card isn't known on server) + FullCard fullCardForServerPropagation = new FullCard(); + fullCardForServerPropagation.setCard(targetCard); + + Account targetAccount = dataBaseAdapter.getAccountByIdDirectly(targetAccountId); + FullBoard targetBoard = dataBaseAdapter.getFullBoardByLocalIdDirectly(targetAccountId, targetBoardLocalId); + FullStack targetFullStack = dataBaseAdapter.getFullStackByLocalIdDirectly(targetStackLocalId); + User userOfTargetAccount = dataBaseAdapter.getUserByUidDirectly(targetAccountId, targetAccount.getUserName()); + CountDownLatch latch = new CountDownLatch(1); + new DataPropagationHelper(serverAdapter, dataBaseAdapter).createEntity(new CardPropagationDataProvider(null, targetBoard.getBoard(), targetFullStack), fullCardForServerPropagation, new IResponseCallback<FullCard>(targetAccount) { + @Override + public void onResponse(FullCard response) { + targetCard.setId(response.getId()); + targetCard.setLocalId(response.getLocalId()); + latch.countDown(); + } + + @Override + public void onError(Throwable throwable) { + super.onError(throwable); + throw new RuntimeException("unable to create card in moveCard target", throwable); + } + }, (FullCard entity, FullCard response) -> { + response.getCard().setUserId(entity.getCard().getUserId()); + response.getCard().setStackId(targetFullStack.getLocalId()); + }); + + try { + latch.await(); + } catch (InterruptedException e) { + DeckLog.logError(e); + throw new RuntimeException("error fulfilling countDownLatch", e); + } + + long newCardId = targetCard.getLocalId(); + + // ### clone labels, assign them + // prepare + // has user of targetaccount manage permissions? + boolean hasManagePermission = targetBoard.getBoard().getOwnerId() == userOfTargetAccount.getLocalId(); + List<AccessControl> aclOfTargetBoard = dataBaseAdapter.getAccessControlByLocalBoardIdDirectly(targetAccountId, targetBoard.getLocalId()); + if (!hasManagePermission) { + for (AccessControl accessControl : aclOfTargetBoard) { + if (accessControl.getUserId() == userOfTargetAccount.getLocalId() && accessControl.isPermissionManage()) { + hasManagePermission = true; + break; + } + } + } + + // actual doing + for (Label originalLabel : originalCard.getLabels()) { + // already exists? + Label existingMatch = null; + for (Label targetBoardLabel : targetBoard.getLabels()) { + if (originalLabel.getTitle().trim().equalsIgnoreCase(targetBoardLabel.getTitle().trim())) { + existingMatch = targetBoardLabel; + break; + } + } + if (existingMatch == null) { + if (hasManagePermission) { + originalLabel.setBoardId(targetBoardLocalId); + originalLabel.setId(null); + originalLabel.setLocalId(null); + originalLabel.setStatusEnum(DBStatus.LOCAL_EDITED); + originalLabel.setAccountId(targetBoard.getAccountId()); + createAndAssignLabelToCard(originalBoard.getAccountId(), originalLabel, newCardId); + } + } else { + assignLabelToCard(existingMatch, targetCard); + } + } + + // ### Clone assigned users + Account originalAccount = dataBaseAdapter.getAccountByIdDirectly(originAccountId); + // same instance? otherwise doesn't make sense + if (originalAccount.getUrl().equalsIgnoreCase(targetAccount.getUrl())) { + for (User assignedUser : originalCard.getAssignedUsers()) { + // has assignedUser at least view permissions? + boolean hasViewPermission = targetBoard.getBoard().getOwnerId() == assignedUser.getLocalId(); + if (!hasViewPermission) { + for (AccessControl accessControl : aclOfTargetBoard) { + if (accessControl.getUserId() == userOfTargetAccount.getLocalId()) { + // ACL exists, so viewing is granted + hasViewPermission = true; + break; + } + } + } + if (hasViewPermission) { + assignUserToCard(assignedUser, targetCard); + } + } + } + // since this is LiveData<Void> + return null; + }); } + @AnyThread public WrappedLiveData<Label> createLabel(long accountId, Label label, long localBoardId) { WrappedLiveData<Label> liveData = new WrappedLiveData<>(); doAsync(() -> { @@ -961,7 +1240,8 @@ public class SyncManager { return liveData; } - public MutableLiveData<Label> createAndAssignLabelToCard(long accountId, Label label, long localCardId) { + @AnyThread + public MutableLiveData<Label> createAndAssignLabelToCard(long accountId, @NonNull Label label, long localCardId) { MutableLiveData<Label> liveData = new MutableLiveData<>(); doAsync(() -> { Account account = dataBaseAdapter.getAccountByIdDirectly(accountId); @@ -986,7 +1266,8 @@ public class SyncManager { return liveData; } - public WrappedLiveData<Void> deleteLabel(Label label) { + @AnyThread + public WrappedLiveData<Void> deleteLabel(@NonNull Label label) { WrappedLiveData<Void> liveData = new WrappedLiveData<>(); doAsync(() -> { Account account = dataBaseAdapter.getAccountByIdDirectly(label.getAccountId()); @@ -997,7 +1278,8 @@ public class SyncManager { return liveData; } - public WrappedLiveData<Label> updateLabel(Label label) { + @AnyThread + public WrappedLiveData<Label> updateLabel(@NonNull Label label) { WrappedLiveData<Label> liveData = new WrappedLiveData<>(); doAsync(() -> { Account account = dataBaseAdapter.getAccountByIdDirectly(label.getAccountId()); @@ -1008,7 +1290,8 @@ public class SyncManager { return liveData; } - public void assignUserToCard(User user, Card card) { + @AnyThread + public void assignUserToCard(@NonNull User user, @NonNull Card card) { doAsync(() -> { final long localUserId = user.getLocalId(); final long localCardId = card.getLocalId(); @@ -1032,7 +1315,8 @@ public class SyncManager { }); } - public void assignLabelToCard(Label label, Card card) { + @AnyThread + public void assignLabelToCard(@NonNull Label label, @NonNull Card card) { doAsync(() -> { final long localLabelId = label.getLocalId(); final long localCardId = card.getLocalId(); @@ -1055,7 +1339,8 @@ public class SyncManager { }); } - public void unassignLabelFromCard(Label label, Card card) { + @AnyThread + public void unassignLabelFromCard(@NonNull Label label, @NonNull Card card) { doAsync(() -> { dataBaseAdapter.deleteJoinedLabelForCard(card.getLocalId(), label.getLocalId()); Stack stack = dataBaseAdapter.getStackByLocalIdDirectly(card.getStackId()); @@ -1072,7 +1357,8 @@ public class SyncManager { }); } - public void unassignUserFromCard(User user, Card card) { + @AnyThread + public void unassignUserFromCard(@NonNull User user, @NonNull Card card) { doAsync(() -> { dataBaseAdapter.deleteJoinedUserForCard(card.getLocalId(), user.getLocalId()); if (serverAdapter.hasInternetConnection()) { @@ -1122,12 +1408,13 @@ public class SyncManager { return dataBaseAdapter.getUserByUid(accountId, uid); } + @WorkerThread public User getUserByUidDirectly(long accountId, String uid) { return dataBaseAdapter.getUserByUidDirectly(accountId, uid); } - public LiveData<List<User>> searchUserByUidOrDisplayName(final long accountId, final long notYetAssignedToLocalCardId, final String searchTerm) { - return dataBaseAdapter.searchUserByUidOrDisplayName(accountId, notYetAssignedToLocalCardId, searchTerm); + public LiveData<List<User>> searchUserByUidOrDisplayName(final long accountId, final long boardId, final long notYetAssignedToLocalCardId, final String searchTerm) { + return dataBaseAdapter.searchUserByUidOrDisplayName(accountId, boardId, notYetAssignedToLocalCardId, searchTerm); } public LiveData<List<User>> searchUserByUidOrDisplayNameForACL(final long accountId, final long notYetAssignedInACL, final String searchTerm) { @@ -1150,11 +1437,11 @@ public class SyncManager { return dataBaseAdapter.createUser(accountId, user); } - public void updateUser(long accountId, User user) { + public void updateUser(long accountId, @NonNull User user) { dataBaseAdapter.updateUser(accountId, user, true); } - public LiveData<List<Label>> searchNotYetAssignedLabelsByTitle(final long accountId, final long boardId, final long notYetAssignedToLocalCardId, String searchTerm) { + public LiveData<List<Label>> searchNotYetAssignedLabelsByTitle(final long accountId, final long boardId, final long notYetAssignedToLocalCardId, @NonNull String searchTerm) { return dataBaseAdapter.searchNotYetAssignedLabelsByTitle(accountId, boardId, notYetAssignedToLocalCardId, searchTerm); } @@ -1165,7 +1452,8 @@ public class SyncManager { /** * @see <a href="https://github.com/stefan-niedermann/nextcloud-deck/issues/360">reenable reorder</a> */ - public void reorder(long accountId, FullCard movedCard, long newStackId, int newIndex) { + @AnyThread + public void reorder(long accountId, @NonNull FullCard movedCard, long newStackId, int newIndex) { doAsync(() -> { // read cards of new stack List<FullCard> cardsOfNewStack = dataBaseAdapter.getFullCardsForStackDirectly(accountId, newStackId); @@ -1250,7 +1538,7 @@ public class SyncManager { } - private void reorderLocally(List<FullCard> cardsOfNewStack, FullCard movedCard, long newStackId, int newOrder) { + private void reorderLocally(List<FullCard> cardsOfNewStack, @NonNull FullCard movedCard, long newStackId, int newOrder) { // set new stack and order Card movedInnerCard = movedCard.getCard(); int oldOrder = movedInnerCard.getOrder(); @@ -1308,7 +1596,7 @@ public class SyncManager { reorderAscending(movedInnerCard, changedCards, startingAtOrder); } - private void reorderAscending(Card movedCard, List<Card> cardsToReorganize, int startingAtOrder) { + private void reorderAscending(@NonNull Card movedCard, @NonNull List<Card> cardsToReorganize, int startingAtOrder) { Date now = new Date(); for (Card card : cardsToReorganize) { card.setOrder(startingAtOrder); @@ -1332,6 +1620,7 @@ public class SyncManager { * The problem is, that the attachment is still in our local database and everytime one tries to sync, the log is spammed with 500 errors * Also this leads to the attachment being present in the card forever with a DBStatus.LOCAL_EDITED */ + @AnyThread public WrappedLiveData<Attachment> addAttachmentToCard(long accountId, long localCardId, @NonNull String mimeType, @NonNull File file) { WrappedLiveData<Attachment> liveData = new WrappedLiveData<>(); doAsync(() -> { @@ -1351,7 +1640,8 @@ public class SyncManager { return liveData; } - public WrappedLiveData<Attachment> updateAttachmentForCard(long accountId, Attachment existing, @NonNull String mimeType, @NonNull File file) { + @AnyThread + public WrappedLiveData<Attachment> updateAttachmentForCard(long accountId, @NonNull Attachment existing, @NonNull String mimeType, @NonNull File file) { WrappedLiveData<Attachment> liveData = new WrappedLiveData<>(); doAsync(() -> { Attachment attachment = populateAttachmentEntityForFile(existing, existing.getCardId(), mimeType, file); @@ -1378,7 +1668,8 @@ public class SyncManager { return liveData; } - private Attachment populateAttachmentEntityForFile(Attachment target, long localCardId, @NonNull String mimeType, @NonNull File file) { + @AnyThread + private static Attachment populateAttachmentEntityForFile(@NonNull Attachment target, long localCardId, @NonNull String mimeType, @NonNull File file) { Attachment attachment = target; attachment.setCardId(localCardId); attachment.setMimetype(mimeType); @@ -1390,6 +1681,7 @@ public class SyncManager { return attachment; } + @AnyThread public WrappedLiveData<Void> deleteAttachmentOfCard(long accountId, long localCardId, long localAttachmentId) { WrappedLiveData<Void> liveData = new WrappedLiveData<>(); doAsync(() -> { @@ -1415,6 +1707,7 @@ public class SyncManager { * Can be called from a configuration screen or a picker. * Creates a new entry in the database, if row with given widgetId does not yet exist. */ + @AnyThread public void addOrUpdateSingleCardWidget(int widgetId, long accountId, long boardId, long localCardId) { doAsync(() -> dataBaseAdapter.createSingleCardWidget(widgetId, accountId, boardId, localCardId)); } @@ -1428,6 +1721,7 @@ public class SyncManager { return model; } + @AnyThread public void deleteSingleCardWidgetModel(int widgetId) { doAsync(() -> dataBaseAdapter.deleteSingleCardWidget(widgetId)); } diff --git a/app/src/main/java/it/niedermann/nextcloud/deck/persistence/sync/SyncWorker.java b/app/src/main/java/it/niedermann/nextcloud/deck/persistence/sync/SyncWorker.java index 25c8b726b..f43cbd34f 100644 --- a/app/src/main/java/it/niedermann/nextcloud/deck/persistence/sync/SyncWorker.java +++ b/app/src/main/java/it/niedermann/nextcloud/deck/persistence/sync/SyncWorker.java @@ -36,11 +36,11 @@ public class SyncWorker extends Worker { SharedPreferences.Editor sharedPreferencesEditor = sharedPreferences.edit(); SyncManager syncManager = new SyncManager(getApplicationContext(), null); if (syncManager.hasInternetConnection()) { - DeckLog.log("Starting background synchronization"); + DeckLog.info("Starting background synchronization"); sharedPreferencesEditor.putLong(getApplicationContext().getString(R.string.shared_preference_last_background_sync), System.currentTimeMillis()); sharedPreferencesEditor.apply(); boolean success = syncManager.synchronizeEverything(); - DeckLog.log("Finishing background synchronization with result " + success); + DeckLog.info("Finishing background synchronization with result " + success); return success ? Result.failure() : Result.success(); } return Result.success(); @@ -53,25 +53,30 @@ public class SyncWorker extends Worker { public static void update(@NonNull Context context, String preferenceValue) { deregister(context); - if (!context.getString(R.string.pref_value_background_sync_off).equals(preferenceValue)) { - int repeatInterval = 15; - TimeUnit unit = TimeUnit.MINUTES; - if (context.getString(R.string.pref_value_background_1_hour).equals(preferenceValue)) { - repeatInterval = 1; - unit = TimeUnit.HOURS; - } else if (context.getString(R.string.pref_value_background_6_hours).equals(preferenceValue)) { - repeatInterval = 6; - unit = TimeUnit.HOURS; - } + int repeatInterval = -1; + TimeUnit unit = null; + if (context.getString(R.string.pref_value_background_15_minutes).equals(preferenceValue)) { + repeatInterval = 15; + unit = TimeUnit.MINUTES; + } else if (context.getString(R.string.pref_value_background_1_hour).equals(preferenceValue)) { + repeatInterval = 1; + unit = TimeUnit.HOURS; + } else if (context.getString(R.string.pref_value_background_6_hours).equals(preferenceValue)) { + repeatInterval = 6; + unit = TimeUnit.HOURS; + } + if (unit == null) { + DeckLog.info("Do not register a new " + SyncWorker.class.getSimpleName() + " because setting " + preferenceValue + " is not a valid time frame"); + } else { final PeriodicWorkRequest work = new PeriodicWorkRequest.Builder(SyncWorker.class, repeatInterval, unit) .setConstraints(constraints).build(); - DeckLog.log("Registering worker running each " + repeatInterval + " " + unit); + DeckLog.info("Registering " + SyncWorker.class.getSimpleName() + " running each " + repeatInterval + " " + unit); WorkManager.getInstance(context.getApplicationContext()).enqueueUniquePeriodicWork(SyncWorker.WORKER_TAG, ExistingPeriodicWorkPolicy.REPLACE, work); } } private static void deregister(@NonNull Context context) { - DeckLog.log("Deregistering all workers with tag \"" + WORKER_TAG + "\""); + DeckLog.info("Deregistering all " + SyncWorker.class.getSimpleName() + " with tag \"" + WORKER_TAG + "\""); WorkManager.getInstance(context.getApplicationContext()).cancelAllWorkByTag(WORKER_TAG); } } diff --git a/app/src/main/java/it/niedermann/nextcloud/deck/persistence/sync/adapters/ServerAdapter.java b/app/src/main/java/it/niedermann/nextcloud/deck/persistence/sync/adapters/ServerAdapter.java index 1e2465eb4..4f41173e9 100644 --- a/app/src/main/java/it/niedermann/nextcloud/deck/persistence/sync/adapters/ServerAdapter.java +++ b/app/src/main/java/it/niedermann/nextcloud/deck/persistence/sync/adapters/ServerAdapter.java @@ -39,6 +39,8 @@ import it.niedermann.nextcloud.deck.model.full.FullStack; import it.niedermann.nextcloud.deck.model.ocs.Capabilities; import it.niedermann.nextcloud.deck.model.ocs.comment.DeckComment; import it.niedermann.nextcloud.deck.model.ocs.comment.OcsComment; +import it.niedermann.nextcloud.deck.model.ocs.user.OcsUser; +import it.niedermann.nextcloud.deck.model.ocs.user.OcsUserList; import it.niedermann.nextcloud.deck.model.propagation.CardUpdate; import it.niedermann.nextcloud.deck.model.propagation.Reorder; import it.niedermann.nextcloud.deck.util.DateUtil; @@ -151,6 +153,15 @@ public class ServerAdapter { ensureInternetConnection(); RequestHelper.request(provider, () -> provider.getNextcloudAPI().getCapabilities(), responseCallback); } + public void getAllOcsUsers(IResponseCallback<OcsUserList> responseCallback) { + ensureInternetConnection(); + RequestHelper.request(provider, () -> provider.getNextcloudAPI().getAllUsers(), responseCallback); + } + + public void getOcsUserDetails(String ocsUserName, IResponseCallback<OcsUser> responseCallback) { + ensureInternetConnection(); + RequestHelper.request(provider, () -> provider.getNextcloudAPI().getUserDetails(ocsUserName), responseCallback); + } public void getActivitiesForCard(long cardId, IResponseCallback<List<it.niedermann.nextcloud.deck.model.ocs.Activity>> responseCallback) { ensureInternetConnection(); diff --git a/app/src/main/java/it/niedermann/nextcloud/deck/persistence/sync/adapters/db/DataBaseAdapter.java b/app/src/main/java/it/niedermann/nextcloud/deck/persistence/sync/adapters/db/DataBaseAdapter.java index 8ef7e876d..c643289bd 100644 --- a/app/src/main/java/it/niedermann/nextcloud/deck/persistence/sync/adapters/db/DataBaseAdapter.java +++ b/app/src/main/java/it/niedermann/nextcloud/deck/persistence/sync/adapters/db/DataBaseAdapter.java @@ -3,6 +3,8 @@ package it.niedermann.nextcloud.deck.persistence.sync.adapters.db; import android.content.Context; import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.annotation.WorkerThread; import androidx.lifecycle.LiveData; import androidx.sqlite.db.SimpleSQLiteQuery; @@ -36,8 +38,8 @@ import it.niedermann.nextcloud.deck.model.internal.FilterInformation; import it.niedermann.nextcloud.deck.model.ocs.Activity; import it.niedermann.nextcloud.deck.model.ocs.comment.DeckComment; import it.niedermann.nextcloud.deck.model.ocs.comment.Mention; -import it.niedermann.nextcloud.deck.model.widget.singlecard.SingleCardWidgetModel; import it.niedermann.nextcloud.deck.model.ocs.comment.full.FullDeckComment; +import it.niedermann.nextcloud.deck.model.widget.singlecard.SingleCardWidgetModel; import it.niedermann.nextcloud.deck.persistence.sync.adapters.db.util.LiveDataHelper; import it.niedermann.nextcloud.deck.persistence.sync.adapters.db.util.WrappedLiveData; import it.niedermann.nextcloud.deck.ui.widget.singlecard.SingleCardWidget; @@ -123,7 +125,7 @@ public class DataBaseAdapter { return db.getCardDao().getFullCardByLocalIdDirectly(accountId, localId); } - public void filterRelationsForCard(FullCard card) { + public void filterRelationsForCard(@Nullable FullCard card) { if (card != null) { if (card.getLabels() != null && !card.getLabels().isEmpty()) { List<Long> filteredIDs = db.getJoinCardWithLabelDao().filterDeleted(card.getLocalId(), getLocalIDs(card.getLabels())); @@ -136,7 +138,7 @@ public class DataBaseAdapter { } } - private <T> List<Long> getLocalIDs(List<? extends AbstractRemoteEntity> remoteEntityList) { + private <T> List<Long> getLocalIDs(@NonNull List<? extends AbstractRemoteEntity> remoteEntityList) { ArrayList<Long> ids = new ArrayList<>(remoteEntityList.size()); for (AbstractRemoteEntity entity : remoteEntityList) { ids.add(entity.getLocalId()); @@ -144,7 +146,7 @@ public class DataBaseAdapter { return ids; } - public void readRelationsForACL(List<AccessControl> acl) { + public void readRelationsForACL(@Nullable List<AccessControl> acl) { if (acl != null) { for (AccessControl accessControl : acl) { readRelationsForACL(accessControl); @@ -152,7 +154,7 @@ public class DataBaseAdapter { } } - public void readRelationsForACL(AccessControl acl) { + public void readRelationsForACL(@Nullable AccessControl acl) { if (acl != null) { if (acl.getUserId() != null) { acl.setUser(db.getUserDao().getUserByLocalIdDirectly(acl.getUserId())); @@ -160,7 +162,7 @@ public class DataBaseAdapter { } } - private void filterRelationsForCard(List<FullCard> card) { + private void filterRelationsForCard(@Nullable List<FullCard> card) { if (card == null) { return; } @@ -169,6 +171,7 @@ public class DataBaseAdapter { } } + @WorkerThread public Card getCardByRemoteIdDirectly(long accountId, long remoteId) { return db.getCardDao().getCardByRemoteIdDirectly(accountId, remoteId); } @@ -221,7 +224,7 @@ public class DataBaseAdapter { } - private void fillSqlWithListValues(StringBuilder query, List<Object> args, List<? extends IRemoteEntity> entities) { + private void fillSqlWithListValues(StringBuilder query, List<Object> args, @NonNull List<? extends IRemoteEntity> entities) { for (int i = 0; i < entities.size(); i++) { if (i > 0) { query.append(", "); @@ -231,10 +234,12 @@ public class DataBaseAdapter { } } + @WorkerThread public List<FullCard> getFullCardsForStackDirectly(long accountId, long localStackId) { return db.getCardDao().getFullCardsForStackDirectly(accountId, localStackId); } + @WorkerThread public User getUserByUidDirectly(long accountId, String uid) { return db.getUserDao().getUserByUidDirectly(accountId, uid); } @@ -254,11 +259,12 @@ public class DataBaseAdapter { return distinctUntilChanged(db.getLabelDao().getLabelByRemoteId(accountId, remoteId)); } + @WorkerThread public Label getLabelByRemoteIdDirectly(long accountId, long remoteId) { return db.getLabelDao().getLabelByRemoteIdDirectly(accountId, remoteId); } - public long createLabel(long accountId, Label label) { + public long createLabel(long accountId, @NonNull Label label) { label.setAccountId(accountId); return db.getLabelDao().insert(label); } @@ -356,7 +362,6 @@ public class DataBaseAdapter { return LiveDataHelper.wrapInLiveData(() -> { long id = db.getAccountDao().insert(account); return readAccountDirectly(id); - }); } @@ -377,6 +382,7 @@ public class DataBaseAdapter { // return distinctUntilChanged(db.getAccountDao().getAccountByName(name)); } + @WorkerThread public Account readAccountDirectly(long id) { return db.getAccountDao().getAccountByIdDirectly(id); } @@ -396,7 +402,7 @@ public class DataBaseAdapter { return distinctUntilChanged(db.getBoardDao().getBoardsWithEditPermissionsForAccount(accountId)); } - public WrappedLiveData<Board> createBoard(long accountId, Board board) { + public WrappedLiveData<Board> createBoard(long accountId, @NonNull Board board) { return LiveDataHelper.wrapInLiveData(() -> { board.setAccountId(accountId); long id = db.getBoardDao().insert(board); @@ -405,7 +411,8 @@ public class DataBaseAdapter { }); } - public long createBoardDirectly(long accountId, Board board) { + @WorkerThread + public long createBoardDirectly(long accountId, @NonNull Board board) { board.setAccountId(accountId); return db.getBoardDao().insert(board); } @@ -428,6 +435,7 @@ public class DataBaseAdapter { return distinctUntilChanged(db.getStackDao().getFullStacksForBoard(accountId, localBoardId)); } + @WorkerThread public List<FullStack> getFullStacksForBoardDirectly(long accountId, long localBoardId) { return db.getStackDao().getFullStacksForBoardDirectly(accountId, localBoardId); } @@ -455,6 +463,7 @@ public class DataBaseAdapter { db.getStackDao().update(stack); } + @WorkerThread public Card getCardByLocalIdDirectly(long accountId, long localCardId) { return db.getCardDao().getCardByLocalIdDirectly(accountId, localCardId); } @@ -463,10 +472,12 @@ public class DataBaseAdapter { return LiveDataHelper.interceptLiveData(db.getCardDao().getFullCardByLocalId(accountId, localCardId), this::filterRelationsForCard); } + @WorkerThread public List<FullCard> getLocallyChangedCardsDirectly(long accountId) { return db.getCardDao().getLocallyChangedCardsDirectly(accountId); } + @WorkerThread public List<FullCard> getLocallyChangedCardsByLocalStackIdDirectly(long accountId, long localStackId) { return db.getCardDao().getLocallyChangedCardsByLocalStackIdDirectly(accountId, localStackId); } @@ -493,7 +504,7 @@ public class DataBaseAdapter { db.getCardDao().delete(card); } - public void updateCard(Card card, boolean setStatus) { + public void updateCard(@NonNull Card card, boolean setStatus) { markAsEditedIfNeeded(card, setStatus); db.getCardDao().update(card); if (db.getSingleCardWidgetModelDao().containsCardLocalId(card.getLocalId())) { @@ -502,19 +513,22 @@ public class DataBaseAdapter { } } - public long createAccessControl(long accountId, AccessControl entity) { + public long createAccessControl(long accountId, @NonNull AccessControl entity) { entity.setAccountId(accountId); return db.getAccessControlDao().insert(entity); } + @WorkerThread public AccessControl getAccessControlByRemoteIdDirectly(long accountId, Long id) { return db.getAccessControlDao().getAccessControlByRemoteIdDirectly(accountId, id); } - public LiveData<List<AccessControl>> getAccessControlByLocalBoardId(long accountId, Long id) { - return LiveDataHelper.interceptLiveData(db.getAccessControlDao().getAccessControlByLocalBoardId(accountId, id), (acl) -> { - readRelationsForACL(acl); - }); + public LiveData<List<AccessControl>> getAccessControlByLocalBoardId(long accountId, Long localBoardId) { + return LiveDataHelper.interceptLiveData(db.getAccessControlDao().getAccessControlByLocalBoardId(accountId, localBoardId), this::readRelationsForACL); + } + + public List<AccessControl> getAccessControlByLocalBoardIdDirectly(long accountId, Long localBoardId) { + return db.getAccessControlDao().getAccessControlByLocalBoardIdDirectly(accountId, localBoardId); } public void updateAccessControl(AccessControl entity, boolean setStatus) { @@ -535,6 +549,7 @@ public class DataBaseAdapter { return distinctUntilChanged(db.getBoardDao().getFullBoardById(accountId, localId)); } + @WorkerThread public Board getBoardByLocalIdDirectly(long localId) { return db.getBoardDao().getBoardByIdDirectly(localId); } @@ -551,9 +566,9 @@ public class DataBaseAdapter { return db.getUserDao().getUsersForAccount(accountId); } - public LiveData<List<User>> searchUserByUidOrDisplayName(final long accountId, final long notYetAssignedToLocalCardId, final String searchTerm) { + public LiveData<List<User>> searchUserByUidOrDisplayName(final long accountId, final long boardId, final long notYetAssignedToLocalCardId, final String searchTerm) { validateSearchTerm(searchTerm); - return db.getUserDao().searchUserByUidOrDisplayName(accountId, notYetAssignedToLocalCardId, "%" + searchTerm.trim() + "%"); + return db.getUserDao().searchUserByUidOrDisplayName(accountId, boardId, notYetAssignedToLocalCardId, "%" + searchTerm.trim() + "%"); } public LiveData<List<User>> searchUserByUidOrDisplayNameForACL(final long accountId, final long notYetAssignedToACL, final String searchTerm) { @@ -578,34 +593,38 @@ public class DataBaseAdapter { return db.getLabelDao().findProposalsForLabelsToAssign(accountId, boardId, notAssignedToLocalCardId); } - + @WorkerThread public Attachment getAttachmentByRemoteIdDirectly(long accountId, Long id) { return db.getAttachmentDao().getAttachmentByRemoteIdDirectly(accountId, id); } + @WorkerThread public Attachment getAttachmentByLocalIdDirectly(long accountId, Long id) { return db.getAttachmentDao().getAttachmentByLocalIdDirectly(accountId, id); } + @WorkerThread public List<Attachment> getAttachmentsForLocalCardIdDirectly(long accountId, Long localCardId) { return db.getAttachmentDao().getAttachmentsForLocalCardIdDirectly(accountId, localCardId); } + @WorkerThread public List<Attachment> getLocallyChangedAttachmentsByLocalCardIdDirectly(long accountId, Long localCardId) { return db.getAttachmentDao().getLocallyChangedAttachmentsByLocalCardIdDirectly(accountId, localCardId); } + @WorkerThread public List<Attachment> getLocallyChangedAttachmentsDirectly(long accountId) { return db.getAttachmentDao().getLocallyChangedAttachmentsDirectly(accountId); } - public long createAttachment(long accountId, Attachment attachment) { + public long createAttachment(long accountId, @NonNull Attachment attachment) { attachment.setAccountId(accountId); attachment.setCreatedAt(new Date()); return db.getAttachmentDao().insert(attachment); } - public void updateAttachment(long accountId, Attachment attachment, boolean setStatus) { + public void updateAttachment(long accountId, @NonNull Attachment attachment, boolean setStatus) { markAsEditedIfNeeded(attachment, setStatus); attachment.setAccountId(accountId); db.getAttachmentDao().update(attachment); @@ -621,20 +640,23 @@ public class DataBaseAdapter { } } - private void validateSearchTerm(String searchTerm) { + private void validateSearchTerm(@Nullable String searchTerm) { if (searchTerm == null || searchTerm.trim().length() < 1) { throw new IllegalArgumentException("please provide a proper search term! \"" + searchTerm + "\" doesn't seem right..."); } } + @WorkerThread public Account getAccountByIdDirectly(long accountId) { return db.getAccountDao().getAccountByIdDirectly(accountId); } + @WorkerThread public List<Account> getAllAccountsDirectly() { return db.getAccountDao().getAllAccountsDirectly(); } + @WorkerThread public User getUserByLocalIdDirectly(long localUserId) { return db.getUserDao().getUserByLocalIdDirectly(localUserId); } @@ -647,6 +669,7 @@ public class DataBaseAdapter { db.getJoinCardWithLabelDao().setDbStatus(localCardId, localLabelId, status); } + @WorkerThread public Label getLabelByLocalIdDirectly(long localLabelId) { return db.getLabelDao().getLabelsByIdDirectly(localLabelId); } @@ -675,10 +698,16 @@ public class DataBaseAdapter { return db.getLabelDao().getLocallyChangedLabelsDirectly(accountId); } + @WorkerThread public Board getBoardByLocalCardIdDirectly(long localCardId) { return db.getBoardDao().getBoardByLocalCardIdDirectly(localCardId); } + @WorkerThread + public FullBoard getFullBoardByLocalCardIdDirectly(long localCardId) { + return db.getBoardDao().getFullBoardByLocalCardIdDirectly(localCardId); + } + public JoinCardWithLabel getJoinCardWithLabel(Long localLabelId, Long localCardId) { return db.getJoinCardWithLabelDao().getJoin(localLabelId, localCardId); } @@ -720,6 +749,7 @@ public class DataBaseAdapter { return db.getActivityDao().insert(activity); } + @WorkerThread public Activity getActivityByRemoteIdDirectly(long accountId, long remoteActivityId) { return db.getActivityDao().getActivityByRemoteIdDirectly(accountId, remoteActivityId); } @@ -760,10 +790,12 @@ public class DataBaseAdapter { }); } + @WorkerThread public DeckComment getCommentByRemoteIdDirectly(long accountId, Long remoteCommentId) { return db.getCommentDao().getCommentByRemoteIdDirectly(accountId, remoteCommentId); } + @WorkerThread public DeckComment getCommentByLocalIdDirectly(long accountId, Long localCommentId) { return db.getCommentDao().getCommentByLocalIdDirectly(accountId, localCommentId); } @@ -787,6 +819,7 @@ public class DataBaseAdapter { } } + @WorkerThread public List<DeckComment> getLocallyChangedCommentsByLocalCardIdDirectly(long accountId, long localCardId) { return db.getCommentDao().getLocallyChangedCommentsByLocalCardIdDirectly(accountId, localCardId); } @@ -799,6 +832,7 @@ public class DataBaseAdapter { return db.getMentionDao().insert(mention); } + @WorkerThread public List<DeckComment> getCommentByLocalCardIdDirectly(Long localCardId) { return db.getCommentDao().getCommentByLocalCardIdDirectly(localCardId); } @@ -807,6 +841,7 @@ public class DataBaseAdapter { return db.getCardDao().getCardsWithLocallyChangedCommentsDirectly(accountId); } + @WorkerThread public Long getLocalStackIdByRemoteStackIdDirectly(long accountId, Long stackId) { return db.getStackDao().getLocalStackIdByRemoteStackIdDirectly(accountId, stackId); } @@ -823,6 +858,7 @@ public class DataBaseAdapter { return db.getJoinCardWithLabelDao().countCardsWithLabel(localLabelId); } + @WorkerThread public Label getLabelByBoardIdAndTitleDirectly(long boardId, String title) { return db.getLabelDao().getLabelByBoardIdAndTitleDirectly(boardId, title); } @@ -835,9 +871,12 @@ public class DataBaseAdapter { return LiveDataHelper.postCustomValue(distinctUntilChanged(db.getBoardDao().countArchivedBoards(accountId)), data -> data != null && data > 0); } + @WorkerThread public Long getRemoteCommentIdForLocalIdDirectly(Long localCommentId) { return db.getCommentDao().getRemoteCommentIdForLocalIdDirectly(localCommentId); } + + @WorkerThread public Long getLocalCommentIdForRemoteIdDirectly(long accountId, Long remoteCommentId) { return db.getCommentDao().getLocalCommentIdForRemoteIdDirectly(accountId, remoteCommentId); } @@ -847,6 +886,7 @@ public class DataBaseAdapter { // Widgets // ------------------- + @WorkerThread public long createSingleCardWidget(int widgetId, long accountId, long boardLocalId, long cardLocalId) { SingleCardWidgetModel model = new SingleCardWidgetModel(); model.setWidgetId(widgetId); @@ -869,4 +909,12 @@ public class DataBaseAdapter { model.setWidgetId(widgetId); db.getSingleCardWidgetModelDao().delete(model); } + + public LiveData<List<Account>> readAccountsForHostWithReadAccessToBoard(String host, long boardRemoteId) { + return db.getAccountDao().readAccountsForHostWithReadAccessToBoard("%"+host+"%", boardRemoteId); + } + + public List<Account> readAccountsForHostWithReadAccessToBoardDirectly(String host, long boardRemoteId) { + return db.getAccountDao().readAccountsForHostWithReadAccessToBoardDirectly("%"+host+"%", boardRemoteId); + } } diff --git a/app/src/main/java/it/niedermann/nextcloud/deck/persistence/sync/adapters/db/DeckDatabase.java b/app/src/main/java/it/niedermann/nextcloud/deck/persistence/sync/adapters/db/DeckDatabase.java index 82fb86c6e..0e86557fa 100644 --- a/app/src/main/java/it/niedermann/nextcloud/deck/persistence/sync/adapters/db/DeckDatabase.java +++ b/app/src/main/java/it/niedermann/nextcloud/deck/persistence/sync/adapters/db/DeckDatabase.java @@ -4,6 +4,7 @@ import android.content.Context; import android.database.Cursor; import androidx.annotation.NonNull; +import androidx.preference.PreferenceManager; import androidx.room.Database; import androidx.room.Room; import androidx.room.RoomDatabase; @@ -32,6 +33,7 @@ import it.niedermann.nextcloud.deck.model.ocs.Activity; import it.niedermann.nextcloud.deck.model.ocs.comment.DeckComment; import it.niedermann.nextcloud.deck.model.ocs.comment.Mention; import it.niedermann.nextcloud.deck.model.widget.singlecard.SingleCardWidgetModel; +import it.niedermann.nextcloud.deck.persistence.sync.SyncWorker; import it.niedermann.nextcloud.deck.persistence.sync.adapters.db.dao.AccessControlDao; import it.niedermann.nextcloud.deck.persistence.sync.adapters.db.dao.AccountDao; import it.niedermann.nextcloud.deck.persistence.sync.adapters.db.dao.ActivityDao; @@ -73,7 +75,7 @@ import it.niedermann.nextcloud.deck.persistence.sync.adapters.db.dao.UserDao; SingleCardWidgetModel.class, }, exportSchema = false, - version = 14 + version = 15 ) @TypeConverters({DateTypeConverter.class}) public abstract class DeckDatabase extends RoomDatabase { @@ -201,6 +203,19 @@ public abstract class DeckDatabase extends RoomDatabase { .addMigrations(MIGRATION_11_12) .addMigrations(MIGRATION_12_13) .addMigrations(MIGRATION_13_14) + .addMigrations(new Migration(14, 15) { + @Override + public void migrate(@NonNull SupportSQLiteDatabase database) { + // https://github.com/stefan-niedermann/nextcloud-deck/issues/570 + SyncWorker.update(context); + // https://github.com/stefan-niedermann/nextcloud-deck/issues/525 + PreferenceManager + .getDefaultSharedPreferences(context) + .edit() + .remove("it.niedermann.nextcloud.deck.theme_text") + .apply(); + } + }) .fallbackToDestructiveMigration() .addCallback(ON_CREATE_CALLBACK) .build(); diff --git a/app/src/main/java/it/niedermann/nextcloud/deck/persistence/sync/adapters/db/dao/AccessControlDao.java b/app/src/main/java/it/niedermann/nextcloud/deck/persistence/sync/adapters/db/dao/AccessControlDao.java index 9dc9b7b3d..2ed1e7d59 100644 --- a/app/src/main/java/it/niedermann/nextcloud/deck/persistence/sync/adapters/db/dao/AccessControlDao.java +++ b/app/src/main/java/it/niedermann/nextcloud/deck/persistence/sync/adapters/db/dao/AccessControlDao.java @@ -17,6 +17,9 @@ public interface AccessControlDao extends GenericDao<AccessControl> { @Query("SELECT * FROM AccessControl WHERE accountId = :accountId and boardId = :localBoardId and status <> 3") LiveData<List<AccessControl>> getAccessControlByLocalBoardId(final long accountId, final long localBoardId); + @Query("SELECT * FROM AccessControl WHERE accountId = :accountId and boardId = :localBoardId and status <> 3") + List<AccessControl> getAccessControlByLocalBoardIdDirectly(final long accountId, final long localBoardId); + @Query("SELECT * FROM AccessControl WHERE accountId = :accountId and id = :remoteId") AccessControl getAccessControlByRemoteIdDirectly(final long accountId, final long remoteId); diff --git a/app/src/main/java/it/niedermann/nextcloud/deck/persistence/sync/adapters/db/dao/AccountDao.java b/app/src/main/java/it/niedermann/nextcloud/deck/persistence/sync/adapters/db/dao/AccountDao.java index 515ecc0a4..6ebe4a8f3 100644 --- a/app/src/main/java/it/niedermann/nextcloud/deck/persistence/sync/adapters/db/dao/AccountDao.java +++ b/app/src/main/java/it/niedermann/nextcloud/deck/persistence/sync/adapters/db/dao/AccountDao.java @@ -36,4 +36,10 @@ public interface AccountDao extends GenericDao<Account> { @Query("SELECT * from account") List<Account> getAllAccountsDirectly(); + + @Query("SELECT * from account a where a.url like :hostLike and exists (select 1 from board b where b.id = :boardRemoteId and a.id = b.accountId)") + LiveData<List<Account>> readAccountsForHostWithReadAccessToBoard(String hostLike, long boardRemoteId); + + @Query("SELECT * from account a where a.url like :hostLike and exists (select 1 from board b where b.id = :boardRemoteId and a.id = b.accountId)") + List<Account> readAccountsForHostWithReadAccessToBoardDirectly(String hostLike, long boardRemoteId); }
\ No newline at end of file diff --git a/app/src/main/java/it/niedermann/nextcloud/deck/persistence/sync/adapters/db/dao/BoardDao.java b/app/src/main/java/it/niedermann/nextcloud/deck/persistence/sync/adapters/db/dao/BoardDao.java index 0f4117481..bd189e846 100644 --- a/app/src/main/java/it/niedermann/nextcloud/deck/persistence/sync/adapters/db/dao/BoardDao.java +++ b/app/src/main/java/it/niedermann/nextcloud/deck/persistence/sync/adapters/db/dao/BoardDao.java @@ -52,6 +52,9 @@ public interface BoardDao extends GenericDao<Board> { @Query("SELECT b.* FROM board b JOIN stack s ON s.boardId = b.localId JOIN card c ON c.localId = :localCardId") Board getBoardByLocalCardIdDirectly(long localCardId); + @Query("SELECT b.* FROM board b JOIN stack s ON s.boardId = b.localId JOIN card c ON c.localId = :localCardId") + FullBoard getFullBoardByLocalCardIdDirectly(long localCardId); + @Transaction @Query("SELECT * FROM board WHERE accountId = :accountId") List<FullBoard> getAllFullBoards(long accountId); diff --git a/app/src/main/java/it/niedermann/nextcloud/deck/persistence/sync/adapters/db/dao/UserDao.java b/app/src/main/java/it/niedermann/nextcloud/deck/persistence/sync/adapters/db/dao/UserDao.java index e9b9ee65b..6f712d07f 100644 --- a/app/src/main/java/it/niedermann/nextcloud/deck/persistence/sync/adapters/db/dao/UserDao.java +++ b/app/src/main/java/it/niedermann/nextcloud/deck/persistence/sync/adapters/db/dao/UserDao.java @@ -26,8 +26,19 @@ public interface UserDao extends GenericDao<User> { " where ju.userId = u.localId" + " and ju.cardId = :notYetAssignedToLocalCardId AND status <> 3" + // not LOCAL_DELETED " )" + + " AND" + + " (" + + " EXISTS (" + + " select 1 from accesscontrol" + + " where userId = u.localId and boardId = :boardId" + + " )" + + " OR" + + " EXISTS (" + + " select 1 from board where localId = :boardId AND ownerId = u.localId" + + " )" + + ")" + "and ( uid LIKE :searchTerm or displayname LIKE :searchTerm or primaryKey LIKE :searchTerm )") - LiveData<List<User>> searchUserByUidOrDisplayName(final long accountId, final long notYetAssignedToLocalCardId, final String searchTerm); + LiveData<List<User>> searchUserByUidOrDisplayName(final long accountId, final long boardId, final long notYetAssignedToLocalCardId, final String searchTerm); @Query("SELECT u.* FROM user u WHERE accountId = :accountId " + " AND NOT EXISTS (" + diff --git a/app/src/main/java/it/niedermann/nextcloud/deck/persistence/sync/adapters/db/util/LiveDataHelper.java b/app/src/main/java/it/niedermann/nextcloud/deck/persistence/sync/adapters/db/util/LiveDataHelper.java index 650c903b3..10a18bb09 100644 --- a/app/src/main/java/it/niedermann/nextcloud/deck/persistence/sync/adapters/db/util/LiveDataHelper.java +++ b/app/src/main/java/it/niedermann/nextcloud/deck/persistence/sync/adapters/db/util/LiveDataHelper.java @@ -85,7 +85,13 @@ public class LiveDataHelper { public static <T> WrappedLiveData<T> wrapInLiveData(final LiveDataWrapper<T> liveDataWrapper) { final WrappedLiveData<T> liveData = new WrappedLiveData<>(); - doAsync(() -> liveDataWrapper.postResult(liveData)); + doAsync(() -> { + try { + liveDataWrapper.postResult(liveData); + } catch (Throwable t) { + liveData.postError(t); + } + }); return liveData; } diff --git a/app/src/main/java/it/niedermann/nextcloud/deck/persistence/sync/helpers/providers/partial/BoardWitAclDownSyncDataProvider.java b/app/src/main/java/it/niedermann/nextcloud/deck/persistence/sync/helpers/providers/partial/BoardWitAclDownSyncDataProvider.java new file mode 100644 index 000000000..326d257ab --- /dev/null +++ b/app/src/main/java/it/niedermann/nextcloud/deck/persistence/sync/helpers/providers/partial/BoardWitAclDownSyncDataProvider.java @@ -0,0 +1,32 @@ +package it.niedermann.nextcloud.deck.persistence.sync.helpers.providers.partial; + +import java.util.List; + +import it.niedermann.nextcloud.deck.api.IResponseCallback; +import it.niedermann.nextcloud.deck.model.AccessControl; +import it.niedermann.nextcloud.deck.model.full.FullBoard; +import it.niedermann.nextcloud.deck.persistence.sync.adapters.ServerAdapter; +import it.niedermann.nextcloud.deck.persistence.sync.adapters.db.DataBaseAdapter; +import it.niedermann.nextcloud.deck.persistence.sync.helpers.SyncHelper; +import it.niedermann.nextcloud.deck.persistence.sync.helpers.providers.AccessControlDataProvider; +import it.niedermann.nextcloud.deck.persistence.sync.helpers.providers.BoardDataProvider; + +public class BoardWitAclDownSyncDataProvider extends BoardDataProvider { + + @Override + public void goDeeper(SyncHelper syncHelper, FullBoard existingEntity, FullBoard entityFromServer, IResponseCallback<Boolean> callback) { + + List<AccessControl> acl = entityFromServer.getParticipants(); + if (acl != null && !acl.isEmpty()){ + for (AccessControl ac : acl){ + ac.setBoardId(existingEntity.getLocalId()); + } + syncHelper.doSyncFor(new AccessControlDataProvider(this, existingEntity, acl)); + } + } + + @Override + public void goDeeperForUpSync(SyncHelper syncHelper, ServerAdapter serverAdapter, DataBaseAdapter dataBaseAdapter, IResponseCallback<Boolean> callback) { + // do nothing! + } +} diff --git a/app/src/main/java/it/niedermann/nextcloud/deck/ui/ImportAccountActivity.java b/app/src/main/java/it/niedermann/nextcloud/deck/ui/ImportAccountActivity.java index ae7ab3683..854b99a6a 100644 --- a/app/src/main/java/it/niedermann/nextcloud/deck/ui/ImportAccountActivity.java +++ b/app/src/main/java/it/niedermann/nextcloud/deck/ui/ImportAccountActivity.java @@ -4,13 +4,17 @@ import android.annotation.SuppressLint; import android.content.Intent; import android.content.SharedPreferences; import android.database.sqlite.SQLiteConstraintException; +import android.graphics.drawable.Drawable; import android.net.Uri; +import android.os.Build; import android.os.Bundle; import android.view.View; import androidx.annotation.NonNull; import androidx.annotation.StringRes; import androidx.appcompat.app.AppCompatActivity; +import androidx.core.content.ContextCompat; +import androidx.core.graphics.drawable.DrawableCompat; import androidx.preference.PreferenceManager; import com.nextcloud.android.sso.AccountImporter; @@ -81,6 +85,12 @@ public class ImportAccountActivity extends AppCompatActivity { AccountImporter.requestAndroidAccountPermissionsAndPickAccount(this); } }); + + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) { + Drawable wrapDrawable = DrawableCompat.wrap(binding.progressCircular.getIndeterminateDrawable()); + DrawableCompat.setTint(wrapDrawable, ContextCompat.getColor(this, R.color.defaultBrand)); + binding.progressCircular.setIndeterminateDrawable(DrawableCompat.unwrap(wrapDrawable)); + } } @Override diff --git a/app/src/main/java/it/niedermann/nextcloud/deck/ui/MainActivity.java b/app/src/main/java/it/niedermann/nextcloud/deck/ui/MainActivity.java index c29994e64..b5713909b 100644 --- a/app/src/main/java/it/niedermann/nextcloud/deck/ui/MainActivity.java +++ b/app/src/main/java/it/niedermann/nextcloud/deck/ui/MainActivity.java @@ -5,7 +5,6 @@ import android.content.Intent; import android.content.SharedPreferences; import android.database.sqlite.SQLiteConstraintException; import android.graphics.Color; -import android.graphics.drawable.Drawable; import android.net.ConnectivityManager; import android.net.Network; import android.net.NetworkInfo; @@ -26,6 +25,7 @@ import androidx.annotation.RequiresApi; import androidx.annotation.UiThread; import androidx.appcompat.app.ActionBarDrawerToggle; import androidx.appcompat.app.AppCompatDelegate; +import androidx.core.content.ContextCompat; import androidx.core.graphics.drawable.DrawableCompat; import androidx.core.util.Pair; import androidx.core.view.GravityCompat; @@ -53,7 +53,6 @@ import java.util.Objects; import it.niedermann.android.crosstabdnd.CrossTabDragAndDrop; import it.niedermann.android.tablayouthelper.TabLayoutHelper; import it.niedermann.android.tablayouthelper.TabTitleGenerator; -import it.niedermann.nextcloud.deck.Application; import it.niedermann.nextcloud.deck.DeckLog; import it.niedermann.nextcloud.deck.R; import it.niedermann.nextcloud.deck.api.IResponseCallback; @@ -97,12 +96,24 @@ import it.niedermann.nextcloud.deck.ui.stack.StackAdapter; import it.niedermann.nextcloud.deck.ui.stack.StackFragment; import it.niedermann.nextcloud.deck.util.DrawerMenuUtil; -import static android.graphics.Color.parseColor; import static androidx.lifecycle.Transformations.switchMap; -import static it.niedermann.nextcloud.deck.Application.NO_ACCOUNT_ID; -import static it.niedermann.nextcloud.deck.Application.NO_BOARD_ID; -import static it.niedermann.nextcloud.deck.Application.NO_STACK_ID; +import static it.niedermann.nextcloud.deck.DeckApplication.NO_ACCOUNT_ID; +import static it.niedermann.nextcloud.deck.DeckApplication.NO_BOARD_ID; +import static it.niedermann.nextcloud.deck.DeckApplication.NO_STACK_ID; +import static it.niedermann.nextcloud.deck.DeckApplication.readCurrentAccountId; +import static it.niedermann.nextcloud.deck.DeckApplication.readCurrentBoardId; +import static it.niedermann.nextcloud.deck.DeckApplication.readCurrentStackId; +import static it.niedermann.nextcloud.deck.DeckApplication.saveCurrentAccountId; +import static it.niedermann.nextcloud.deck.DeckApplication.saveCurrentBoardId; +import static it.niedermann.nextcloud.deck.DeckApplication.saveCurrentStackId; import static it.niedermann.nextcloud.deck.persistence.sync.adapters.db.util.LiveDataHelper.observeOnce; +import static it.niedermann.nextcloud.deck.ui.branding.BrandingUtil.applyBrandToFAB; +import static it.niedermann.nextcloud.deck.ui.branding.BrandingUtil.applyBrandToPrimaryTabLayout; +import static it.niedermann.nextcloud.deck.ui.branding.BrandingUtil.clearBrandColors; +import static it.niedermann.nextcloud.deck.ui.branding.BrandingUtil.getSecondaryForegroundColorDependingOnTheme; +import static it.niedermann.nextcloud.deck.ui.branding.BrandingUtil.saveBrandColors; +import static it.niedermann.nextcloud.deck.util.ColorUtil.contrastRatioIsSufficient; +import static it.niedermann.nextcloud.deck.util.ColorUtil.contrastRatioIsSufficientBigAreas; import static it.niedermann.nextcloud.deck.util.DrawerMenuUtil.MENU_ID_ABOUT; import static it.niedermann.nextcloud.deck.util.DrawerMenuUtil.MENU_ID_ADD_BOARD; import static it.niedermann.nextcloud.deck.util.DrawerMenuUtil.MENU_ID_ARCHIVED_BOARDS; @@ -159,6 +170,8 @@ public class MainActivity extends BrandedActivity implements DeleteStackListener Thread.setDefaultUncaughtExceptionHandler(new ExceptionHandler(this)); + setTheme(R.style.AppTheme); + binding = ActivityMainBinding.inflate(getLayoutInflater()); headerBinding = NavHeaderMainBinding.bind(binding.navigationView.getHeaderView(0)); setContentView(binding.getRoot()); @@ -190,18 +203,18 @@ public class MainActivity extends BrandedActivity implements DeleteStackListener } }).observe(this, (List<Account> accounts) -> { if (accounts == null || accounts.size() == 0) { - // Last account has been deleted. hasAccounts LiveData will handle this, but we make sure, that branding is reset. - Application.saveBrandColors(this, getResources().getColor(R.color.primary), Color.WHITE); + // Last account has been deleted. hasAccounts LiveData will handle this, but we make sure, that branding is reset. + saveBrandColors(this, ContextCompat.getColor(this, R.color.defaultBrand)); return; } accountsList = accounts; - long lastAccountId = Application.readCurrentAccountId(this); + long lastAccountId = readCurrentAccountId(this); for (Account account : accountsList) { if (lastAccountId == account.getId() || lastAccountId == NO_ACCOUNT_ID) { - mainViewModel.setCurrentAccount(account, account.getServerDeckVersionAsObject().isSupported(this)); + mainViewModel.setCurrentAccount(account); if (!firstAccountAdded) { DeckLog.info("Syncing the current account on app start"); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { @@ -229,13 +242,12 @@ public class MainActivity extends BrandedActivity implements DeleteStackListener SingleAccountHelper.setCurrentAccount(getApplicationContext(), mainViewModel.getCurrentAccount().getName()); syncManager = new SyncManager(this); - Application.saveBrandColors(this, parseColor(mainViewModel.getCurrentAccount().getColor()), parseColor(mainViewModel.getCurrentAccount().getTextColor())); - Application.saveCurrentAccountId(this, mainViewModel.getCurrentAccount().getId()); + saveCurrentAccountId(this, mainViewModel.getCurrentAccount().getId()); if (mainViewModel.getCurrentAccount().isMaintenanceEnabled()) { refreshCapabilities(mainViewModel.getCurrentAccount()); } - lastBoardId = Application.readCurrentBoardId(this, mainViewModel.getCurrentAccount().getId()); + lastBoardId = readCurrentBoardId(this, mainViewModel.getCurrentAccount().getId()); if (boardsLiveData != null && boardsLiveDataObserver != null) { boardsLiveData.removeObserver(boardsLiveDataObserver); @@ -262,6 +274,7 @@ public class MainActivity extends BrandedActivity implements DeleteStackListener setCurrentBoard(boardsList.get(0)); } } else { + clearBrandColors(this); clearCurrentBoard(); } @@ -277,13 +290,11 @@ public class MainActivity extends BrandedActivity implements DeleteStackListener }; boardsLiveData.observe(this, boardsLiveDataObserver); - final Drawable placeholderAvatar = getResources().getDrawable(R.drawable.ic_baseline_account_circle_24); - DrawableCompat.setTint(placeholderAvatar, parseColor(currentAccount.getTextColor())); Glide .with(binding.accountSwitcher.getContext()) .load(currentAccount.getAvatarUrl(64)) - .placeholder(placeholderAvatar) - .error(placeholderAvatar) + .placeholder(R.drawable.ic_baseline_account_circle_24) + .error(R.drawable.ic_baseline_account_circle_24) .apply(RequestOptions.circleCropTransform()) .into(binding.accountSwitcher); @@ -345,7 +356,7 @@ public class MainActivity extends BrandedActivity implements DeleteStackListener binding.viewPager.post(() -> { // stackAdapter size might differ from position when an account has been deleted if (stackAdapter.getItemCount() > position) { - Application.saveCurrentStackId(getApplicationContext(), mainViewModel.getCurrentAccount().getId(), mainViewModel.getCurrentBoardLocalId(), stackAdapter.getItem(position).getLocalId()); + saveCurrentStackId(getApplicationContext(), mainViewModel.getCurrentAccount().getId(), mainViewModel.getCurrentBoardLocalId(), stackAdapter.getItem(position).getLocalId()); } else { DeckLog.logError(new IllegalStateException("Tried to save current Stack which cannot be available (stackAdapter doesn't have this position)")); } @@ -362,11 +373,7 @@ public class MainActivity extends BrandedActivity implements DeleteStackListener } }); filterViewModel.getFilterInformation().observe(this, (info) -> - binding.filter.setImageDrawable(getResources().getDrawable( - filterViewModel.getFilterInformation().getValue() == null - ? R.drawable.ic_filter_list_white_24dp - : R.drawable.ic_filter_list_active_white_24dp) - )); + binding.filterIndicator.setVisibility(filterViewModel.getFilterInformation().getValue() == null ? View.GONE : View.VISIBLE)); binding.filter.setOnClickListener((v) -> FilterDialogFragment.newInstance().show(getSupportFragmentManager(), EditStackDialogFragment.class.getCanonicalName())); binding.archivedCards.setOnClickListener((v) -> startActivity(ArchivedCardsActvitiy.createIntent(this, mainViewModel.getCurrentAccount(), mainViewModel.getCurrentBoardLocalId(), mainViewModel.currentBoardHasEditPermission()))); @@ -411,23 +418,16 @@ public class MainActivity extends BrandedActivity implements DeleteStackListener } @Override - public void applyBrand(@ColorInt int mainColor, @ColorInt int textColor) { - applyBrandToPrimaryToolbar(mainColor, textColor, binding.toolbar); - applyBrandToPrimaryTabLayout(mainColor, textColor, binding.stackTitles); - applyBrandToFAB(mainColor, textColor, binding.fab); - - // Is null as soon as the avatar has been set -// @Nullable -// Drawable accountSwitcherDrawable = binding.accountSwitcher.getDrawable(); -// if (accountSwitcherDrawable != null) { -// DrawableCompat.setTint(accountSwitcherDrawable, textColor); -// } - - binding.listMenuButton.setBackgroundColor(mainColor); - binding.listMenuButton.setColorFilter(textColor); - + public void applyBrand(@ColorInt int mainColor) { + applyBrandToPrimaryTabLayout(mainColor, binding.stackTitles); + applyBrandToFAB(mainColor, binding.fab); + // TODO We assume, that the background of the spinner is always white + binding.swipeRefreshLayout.setColorSchemeColors(contrastRatioIsSufficient(Color.WHITE, mainColor) ? mainColor : colorAccent); headerBinding.headerView.setBackgroundColor(mainColor); - headerBinding.appName.setTextColor(textColor); + @ColorInt final int headerTextColor = contrastRatioIsSufficientBigAreas(mainColor, Color.WHITE) ? Color.WHITE : Color.BLACK; + DrawableCompat.setTint(headerBinding.logo.getDrawable(), headerTextColor); + headerBinding.appName.setTextColor(headerTextColor); + DrawableCompat.setTint(binding.filterIndicator.getDrawable(), getSecondaryForegroundColorDependingOnTheme(this, mainColor)); } @Override @@ -522,9 +522,6 @@ public class MainActivity extends BrandedActivity implements DeleteStackListener if (mainViewModel.getCurrentAccount().getServerDeckVersionAsObject().isSupported(MainActivity.this) && !response.getDeckVersion().isSupported(MainActivity.this)) { recreate(); } - @ColorInt final int mainColor = parseColor(response.getColor()); - @ColorInt final int textColor = parseColor(response.getTextColor()); - runOnUiThread(() -> Application.saveBrandColors(MainActivity.this, mainColor, textColor)); } } @@ -551,11 +548,12 @@ public class MainActivity extends BrandedActivity implements DeleteStackListener if (stacksLiveData != null) { stacksLiveData.removeObservers(this); } + saveBrandColors(this, Color.parseColor('#' + board.getColor())); mainViewModel.setCurrentBoard(board); filterViewModel.clearFilterInformation(); lastBoardId = board.getLocalId(); - Application.saveCurrentBoardId(this, mainViewModel.getCurrentAccount().getId(), mainViewModel.getCurrentBoardLocalId()); + saveCurrentBoardId(this, mainViewModel.getCurrentAccount().getId(), mainViewModel.getCurrentBoardLocalId()); binding.toolbar.setTitle(board.getTitle()); @@ -585,11 +583,12 @@ public class MainActivity extends BrandedActivity implements DeleteStackListener binding.emptyContentViewStacks.setVisibility(View.GONE); currentBoardHasStacks = true; } + listMenu.findItem(R.id.archive_cards).setVisible(currentBoardHasStacks); int stackPositionInAdapter = 0; stackAdapter.setStacks(fullStacks); - long currentStackId = Application.readCurrentStackId(this, mainViewModel.getCurrentAccount().getId(), mainViewModel.getCurrentBoardLocalId()); + long currentStackId = readCurrentStackId(this, mainViewModel.getCurrentAccount().getId(), mainViewModel.getCurrentBoardLocalId()); for (int i = 0; i < currentBoardStacksCount; i++) { if (fullStacks.get(i).getLocalId() == currentStackId || currentStackId == NO_STACK_ID) { stackPositionInAdapter = i; @@ -796,7 +795,7 @@ public class MainActivity extends BrandedActivity implements DeleteStackListener if (response.getDeckVersion().isSupported(getApplicationContext())) { runOnUiThread(() -> { syncManager = importSyncManager; - mainViewModel.setCurrentAccount(account, account.getServerDeckVersionAsObject().isSupported(MainActivity.this)); + mainViewModel.setCurrentAccount(account); final Snackbar importSnackbar = BrandedSnackbar.make(binding.coordinatorLayout, R.string.account_is_getting_imported, Snackbar.LENGTH_INDEFINITE); importSnackbar.show(); @@ -942,6 +941,7 @@ public class MainActivity extends BrandedActivity implements DeleteStackListener } else if (this.boardsList.size() > 1) { // Select second board after deletion setCurrentBoard(this.boardsList.get(1)); } else { // No other board is available, open create dialog + clearBrandColors(this); clearCurrentBoard(); EditBoardDialogFragment.newInstance().show(getSupportFragmentManager(), addBoard); } diff --git a/app/src/main/java/it/niedermann/nextcloud/deck/ui/MainViewModel.java b/app/src/main/java/it/niedermann/nextcloud/deck/ui/MainViewModel.java index b9ba588df..e5bd4f482 100644 --- a/app/src/main/java/it/niedermann/nextcloud/deck/ui/MainViewModel.java +++ b/app/src/main/java/it/niedermann/nextcloud/deck/ui/MainViewModel.java @@ -1,15 +1,18 @@ package it.niedermann.nextcloud.deck.ui; +import android.app.Application; + +import androidx.annotation.NonNull; import androidx.annotation.Nullable; +import androidx.lifecycle.AndroidViewModel; import androidx.lifecycle.LiveData; import androidx.lifecycle.MutableLiveData; -import androidx.lifecycle.ViewModel; import it.niedermann.nextcloud.deck.model.Account; import it.niedermann.nextcloud.deck.model.Board; @SuppressWarnings("WeakerAccess") -public class MainViewModel extends ViewModel { +public class MainViewModel extends AndroidViewModel { private MutableLiveData<Account> currentAccount = new MutableLiveData<>(); private Board currentBoard; @@ -17,6 +20,10 @@ public class MainViewModel extends ViewModel { private boolean currentAccountIsSupportedVersion = false; + public MainViewModel(@NonNull Application application) { + super(application); + } + public Account getCurrentAccount() { return currentAccount.getValue(); } @@ -25,9 +32,9 @@ public class MainViewModel extends ViewModel { return this.currentAccount; } - public void setCurrentAccount(Account currentAccount, boolean versionIsSupported) { + public void setCurrentAccount(Account currentAccount) { this.currentAccount.setValue(currentAccount); - this.currentAccountIsSupportedVersion = versionIsSupported; + this.currentAccountIsSupportedVersion = currentAccount.getServerDeckVersionAsObject().isSupported(getApplication().getApplicationContext()); } public void setCurrentBoard(Board currentBoard) { diff --git a/app/src/main/java/it/niedermann/nextcloud/deck/ui/PushNotificationActivity.java b/app/src/main/java/it/niedermann/nextcloud/deck/ui/PushNotificationActivity.java index f3d462c90..b0b0d68ae 100644 --- a/app/src/main/java/it/niedermann/nextcloud/deck/ui/PushNotificationActivity.java +++ b/app/src/main/java/it/niedermann/nextcloud/deck/ui/PushNotificationActivity.java @@ -1,40 +1,31 @@ package it.niedermann.nextcloud.deck.ui; import android.content.Intent; -import android.graphics.Color; import android.net.Uri; import android.text.TextUtils; import android.view.View; -import androidx.annotation.ColorInt; import androidx.annotation.NonNull; import androidx.annotation.UiThread; import androidx.appcompat.app.AppCompatActivity; import com.nextcloud.android.sso.helper.SingleAccountHelper; -import it.niedermann.nextcloud.deck.Application; import it.niedermann.nextcloud.deck.DeckLog; import it.niedermann.nextcloud.deck.R; import it.niedermann.nextcloud.deck.databinding.ActivityPushNotificationBinding; import it.niedermann.nextcloud.deck.model.Account; import it.niedermann.nextcloud.deck.persistence.sync.SyncManager; -import it.niedermann.nextcloud.deck.ui.branding.Branded; import it.niedermann.nextcloud.deck.ui.card.EditActivity; import it.niedermann.nextcloud.deck.ui.exception.ExceptionHandler; import static android.graphics.Color.parseColor; import static it.niedermann.nextcloud.deck.persistence.sync.adapters.db.util.LiveDataHelper.observeOnce; -import static it.niedermann.nextcloud.deck.ui.branding.BrandedActivity.applyBrandToPrimaryToolbar; -import static it.niedermann.nextcloud.deck.ui.branding.BrandedActivity.applyBrandToStatusbar; -import static it.niedermann.nextcloud.deck.ui.branding.BrandedActivity.getSecondaryForegroundColorDependingOnTheme; -public class PushNotificationActivity extends AppCompatActivity implements Branded { +public class PushNotificationActivity extends AppCompatActivity { private ActivityPushNotificationBinding binding; - private boolean brandingEnabled; - // Provided by Files app NotificationJob private static final String KEY_SUBJECT = "subject"; private static final String KEY_MESSAGE = "message"; @@ -56,8 +47,6 @@ public class PushNotificationActivity extends AppCompatActivity implements Brand setContentView(binding.getRoot()); setSupportActionBar(binding.toolbar); - brandingEnabled = Application.isBrandingEnabled(this); - binding.subject.setText(getIntent().getStringExtra(KEY_SUBJECT)); final String message = getIntent().getStringExtra(KEY_MESSAGE); @@ -82,13 +71,6 @@ public class PushNotificationActivity extends AppCompatActivity implements Brand if (account != null) { SingleAccountHelper.setCurrentAccount(this, account.getName()); final SyncManager syncManager = new SyncManager(this); - try { - if (brandingEnabled) { - applyBrand(parseColor(account.getColor()), parseColor(account.getTextColor())); - } - } catch (Throwable t) { - DeckLog.logError(t); - } DeckLog.verbose("account: " + account); observeOnce(syncManager.getLocalBoardIdByCardRemoteIdAndAccount(cardRemoteId, account), PushNotificationActivity.this, (boardLocalId -> { DeckLog.verbose("BoardLocalId " + boardLocalId); @@ -153,11 +135,6 @@ public class PushNotificationActivity extends AppCompatActivity implements Brand @UiThread private void launchEditActivity(@NonNull Account account, Long boardId, Long cardId) { - try { - Application.saveBrandColors(this, Color.parseColor(account.getColor()), Color.parseColor(account.getTextColor())); - } catch (Throwable t) { - DeckLog.logError(t); - } DeckLog.info("starting " + EditActivity.class.getSimpleName() + " with [" + account + ", " + boardId + ", " + cardId + "]"); startActivity(EditActivity.createEditCardIntent(this, account, boardId, cardId)); finish(); @@ -169,15 +146,6 @@ public class PushNotificationActivity extends AppCompatActivity implements Brand return true; } - @Override - public void applyBrand(@ColorInt int mainColor, @ColorInt int textColor) { - if (brandingEnabled) { - applyBrandToStatusbar(getWindow(), mainColor, textColor); - applyBrandToPrimaryToolbar(mainColor, textColor, binding.toolbar); - binding.cancel.setTextColor(getSecondaryForegroundColorDependingOnTheme(this, mainColor)); - } - } - public void applyBrandToSubmitButton(@NonNull Account account) { try { binding.submit.setBackgroundColor(parseColor(account.getColor())); diff --git a/app/src/main/java/it/niedermann/nextcloud/deck/ui/about/AboutActivity.java b/app/src/main/java/it/niedermann/nextcloud/deck/ui/about/AboutActivity.java index 943e48bd6..bf3734b72 100644 --- a/app/src/main/java/it/niedermann/nextcloud/deck/ui/about/AboutActivity.java +++ b/app/src/main/java/it/niedermann/nextcloud/deck/ui/about/AboutActivity.java @@ -19,6 +19,8 @@ import it.niedermann.nextcloud.deck.model.Account; import it.niedermann.nextcloud.deck.ui.branding.BrandedActivity; import it.niedermann.nextcloud.deck.ui.exception.ExceptionHandler; +import static it.niedermann.nextcloud.deck.ui.branding.BrandingUtil.applyBrandToPrimaryTabLayout; + public class AboutActivity extends BrandedActivity { private static final String BUNDLE_KEY_ACCOUNT = "account"; @@ -82,9 +84,8 @@ public class AboutActivity extends BrandedActivity { } @Override - public void applyBrand(int mainColor, int textColor) { - applyBrandToPrimaryToolbar(mainColor, textColor, binding.toolbar); - applyBrandToPrimaryTabLayout(mainColor, textColor, binding.tabLayout); + public void applyBrand(int mainColor) { + applyBrandToPrimaryTabLayout(mainColor, binding.tabLayout); } @NonNull diff --git a/app/src/main/java/it/niedermann/nextcloud/deck/ui/about/AboutFragmentLicenseTab.java b/app/src/main/java/it/niedermann/nextcloud/deck/ui/about/AboutFragmentLicenseTab.java index f33b30ea3..c00ff212a 100644 --- a/app/src/main/java/it/niedermann/nextcloud/deck/ui/about/AboutFragmentLicenseTab.java +++ b/app/src/main/java/it/niedermann/nextcloud/deck/ui/about/AboutFragmentLicenseTab.java @@ -1,18 +1,26 @@ package it.niedermann.nextcloud.deck.ui.about; import android.content.Intent; +import android.content.res.ColorStateList; +import android.graphics.Color; import android.net.Uri; import android.os.Bundle; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; +import androidx.annotation.ColorInt; import androidx.annotation.NonNull; +import androidx.core.content.ContextCompat; +import androidx.core.graphics.drawable.DrawableCompat; import it.niedermann.nextcloud.deck.R; import it.niedermann.nextcloud.deck.databinding.FragmentAboutLicenseTabBinding; import it.niedermann.nextcloud.deck.ui.branding.BrandedFragment; +import it.niedermann.nextcloud.deck.util.ColorUtil; +import static it.niedermann.nextcloud.deck.DeckApplication.isDarkTheme; +import static it.niedermann.nextcloud.deck.util.ColorUtil.contrastRatioIsSufficientBigAreas; import static it.niedermann.nextcloud.deck.util.SpannableUtil.setTextWithURL; public class AboutFragmentLicenseTab extends BrandedFragment { @@ -29,8 +37,11 @@ public class AboutFragmentLicenseTab extends BrandedFragment { } @Override - public void applyBrand(int mainColor, int textColor) { - binding.aboutAppLicenseButton.setBackgroundColor(mainColor); - binding.aboutAppLicenseButton.setTextColor(textColor); + public void applyBrand(int mainColor) { + @ColorInt final int finalMainColor = contrastRatioIsSufficientBigAreas(mainColor, ContextCompat.getColor(requireContext(), R.color.primary)) + ? mainColor + : isDarkTheme(requireContext()) ? Color.WHITE : Color.BLACK; + DrawableCompat.setTintList(binding.aboutAppLicenseButton.getBackground(), ColorStateList.valueOf(finalMainColor)); + binding.aboutAppLicenseButton.setTextColor(ColorUtil.getForegroundColorForBackgroundColor(finalMainColor)); } }
\ No newline at end of file diff --git a/app/src/main/java/it/niedermann/nextcloud/deck/ui/accountswitcher/AccountSwitcherDialog.java b/app/src/main/java/it/niedermann/nextcloud/deck/ui/accountswitcher/AccountSwitcherDialog.java index bae4720b9..592f2e8cc 100644 --- a/app/src/main/java/it/niedermann/nextcloud/deck/ui/accountswitcher/AccountSwitcherDialog.java +++ b/app/src/main/java/it/niedermann/nextcloud/deck/ui/accountswitcher/AccountSwitcherDialog.java @@ -53,14 +53,15 @@ public class AccountSwitcherDialog extends BrandedDialogFragment { Glide.with(requireContext()) .load(viewModel.getCurrentAccount().getAvatarUrl(dpToPx(binding.currentAccountItemAvatar.getContext(), R.dimen.avatar_size))) - .error(R.drawable.ic_person_grey600_24dp) + .placeholder(R.drawable.ic_baseline_account_circle_24) + .error(R.drawable.ic_baseline_account_circle_24) .apply(RequestOptions.circleCropTransform()) .into(binding.currentAccountItemAvatar); binding.accountLayout.setOnClickListener((v) -> dismiss()); adapter = new AccountSwitcherAdapter((localAccount -> { - viewModel.setCurrentAccount(localAccount, localAccount.getServerDeckVersionAsObject().isSupported(requireContext())); + viewModel.setCurrentAccount(localAccount); dismiss(); })); @@ -97,7 +98,7 @@ public class AccountSwitcherDialog extends BrandedDialogFragment { } @Override - public void applyBrand(int mainColor, int textColor) { + public void applyBrand(int mainColor) { // applyBrandToLayerDrawable((LayerDrawable) binding.check.getDrawable(), R.id.area, mainColor); } } diff --git a/app/src/main/java/it/niedermann/nextcloud/deck/ui/accountswitcher/AccountSwitcherViewHolder.java b/app/src/main/java/it/niedermann/nextcloud/deck/ui/accountswitcher/AccountSwitcherViewHolder.java index 9de4185b1..9c93c422e 100644 --- a/app/src/main/java/it/niedermann/nextcloud/deck/ui/accountswitcher/AccountSwitcherViewHolder.java +++ b/app/src/main/java/it/niedermann/nextcloud/deck/ui/accountswitcher/AccountSwitcherViewHolder.java @@ -31,7 +31,8 @@ public class AccountSwitcherViewHolder extends RecyclerView.ViewHolder { binding.accountHost.setText(Uri.parse(account.getUrl()).getHost()); Glide.with(itemView.getContext()) .load(new SingleSignOnUrl(account.getName(), account.getAvatarUrl(dpToPx(binding.accountItemAvatar.getContext(), R.dimen.avatar_size)))) - .error(R.drawable.ic_person_grey600_24dp) + .placeholder(R.drawable.ic_baseline_account_circle_24) + .error(R.drawable.ic_baseline_account_circle_24) .apply(RequestOptions.circleCropTransform()) .into(binding.accountItemAvatar); itemView.setOnClickListener((v) -> onAccountClick.accept(account)); diff --git a/app/src/main/java/it/niedermann/nextcloud/deck/ui/archivedboards/ArchivedBoardsActvitiy.java b/app/src/main/java/it/niedermann/nextcloud/deck/ui/archivedboards/ArchivedBoardsActvitiy.java index 07ca40661..7c3a2d23c 100644 --- a/app/src/main/java/it/niedermann/nextcloud/deck/ui/archivedboards/ArchivedBoardsActvitiy.java +++ b/app/src/main/java/it/niedermann/nextcloud/deck/ui/archivedboards/ArchivedBoardsActvitiy.java @@ -53,7 +53,7 @@ public class ArchivedBoardsActvitiy extends BrandedActivity implements DeleteBoa setSupportActionBar(binding.toolbar); viewModel = new ViewModelProvider(this).get(MainViewModel.class); - viewModel.setCurrentAccount(account, account.getServerDeckVersionAsObject().isSupported(this)); + viewModel.setCurrentAccount(account); syncManager = new SyncManager(this); adapter = new ArchivedBoardsAdapter(viewModel.isCurrentAccountIsSupportedVersion(), getSupportFragmentManager(), (board) -> syncManager.dearchiveBoard(board)); @@ -67,8 +67,8 @@ public class ArchivedBoardsActvitiy extends BrandedActivity implements DeleteBoa } @Override - public void applyBrand(int mainColor, int textColor) { - applyBrandToPrimaryToolbar(mainColor, textColor, binding.toolbar); + public void applyBrand(int mainColor) { + // Nothing to do... } @NonNull diff --git a/app/src/main/java/it/niedermann/nextcloud/deck/ui/archivedcards/ArchivedCardsActvitiy.java b/app/src/main/java/it/niedermann/nextcloud/deck/ui/archivedcards/ArchivedCardsActvitiy.java index 00500ae97..ed2ee7097 100644 --- a/app/src/main/java/it/niedermann/nextcloud/deck/ui/archivedcards/ArchivedCardsActvitiy.java +++ b/app/src/main/java/it/niedermann/nextcloud/deck/ui/archivedcards/ArchivedCardsActvitiy.java @@ -65,8 +65,8 @@ public class ArchivedCardsActvitiy extends BrandedActivity { } @Override - public void applyBrand(int mainColor, int textColor) { - applyBrandToPrimaryToolbar(mainColor, textColor, binding.toolbar); + public void applyBrand(int mainColor) { + // Nothing to do... } @NonNull diff --git a/app/src/main/java/it/niedermann/nextcloud/deck/ui/board/EditBoardDialogFragment.java b/app/src/main/java/it/niedermann/nextcloud/deck/ui/board/EditBoardDialogFragment.java index 1283695e0..e5d0a482b 100644 --- a/app/src/main/java/it/niedermann/nextcloud/deck/ui/board/EditBoardDialogFragment.java +++ b/app/src/main/java/it/niedermann/nextcloud/deck/ui/board/EditBoardDialogFragment.java @@ -15,10 +15,11 @@ import it.niedermann.nextcloud.deck.databinding.DialogTextColorInputBinding; import it.niedermann.nextcloud.deck.model.full.FullBoard; import it.niedermann.nextcloud.deck.persistence.sync.SyncManager; import it.niedermann.nextcloud.deck.ui.MainViewModel; -import it.niedermann.nextcloud.deck.ui.branding.BrandedActivity; import it.niedermann.nextcloud.deck.ui.branding.BrandedAlertDialogBuilder; import it.niedermann.nextcloud.deck.ui.branding.BrandedDialogFragment; +import static it.niedermann.nextcloud.deck.ui.branding.BrandingUtil.applyBrandToEditText; + public class EditBoardDialogFragment extends BrandedDialogFragment { private DialogTextColorInputBinding binding; @@ -95,7 +96,7 @@ public class EditBoardDialogFragment extends BrandedDialogFragment { } @Override - public void applyBrand(int mainColor, int textColor) { - BrandedActivity.applyBrandToEditText(mainColor, textColor, binding.input); + public void applyBrand(int mainColor) { + applyBrandToEditText(mainColor, binding.input); } }
\ No newline at end of file diff --git a/app/src/main/java/it/niedermann/nextcloud/deck/ui/board/accesscontrol/AccessControlAdapter.java b/app/src/main/java/it/niedermann/nextcloud/deck/ui/board/accesscontrol/AccessControlAdapter.java index 4ab3ac80b..e5a50d9f4 100644 --- a/app/src/main/java/it/niedermann/nextcloud/deck/ui/board/accesscontrol/AccessControlAdapter.java +++ b/app/src/main/java/it/niedermann/nextcloud/deck/ui/board/accesscontrol/AccessControlAdapter.java @@ -17,7 +17,6 @@ import java.util.LinkedList; import java.util.List; import java.util.NoSuchElementException; -import it.niedermann.nextcloud.deck.Application; import it.niedermann.nextcloud.deck.R; import it.niedermann.nextcloud.deck.databinding.ItemAccessControlBinding; import it.niedermann.nextcloud.deck.databinding.ItemAccessControlOwnerBinding; @@ -25,10 +24,10 @@ import it.niedermann.nextcloud.deck.model.AccessControl; import it.niedermann.nextcloud.deck.model.Account; import it.niedermann.nextcloud.deck.model.enums.DBStatus; import it.niedermann.nextcloud.deck.ui.branding.Branded; -import it.niedermann.nextcloud.deck.ui.branding.BrandedActivity; import it.niedermann.nextcloud.deck.util.ViewUtil; -import static it.niedermann.nextcloud.deck.ui.branding.BrandedActivity.getSecondaryForegroundColorDependingOnTheme; +import static it.niedermann.nextcloud.deck.ui.branding.BrandingUtil.getSecondaryForegroundColorDependingOnTheme; +import static it.niedermann.nextcloud.deck.ui.branding.BrandingUtil.isBrandingEnabled; public class AccessControlAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> implements Branded { @@ -122,7 +121,7 @@ public class AccessControlAdapter extends RecyclerView.Adapter<RecyclerView.View accessControlChangedListener.updateAccessControl(ac); }); - if (Application.isBrandingEnabled(context)) { + if (isBrandingEnabled(context)) { if (hasManagePermission) { brandSwitch(context, acHolder.binding.permissionEdit, mainColor); brandSwitch(context, acHolder.binding.permissionManage, mainColor); @@ -159,9 +158,9 @@ public class AccessControlAdapter extends RecyclerView.Adapter<RecyclerView.View } @Override - public void applyBrand(int mainColor, int textColor) { - if (Application.isBrandingEnabled(context)) { - this.mainColor = BrandedActivity.getSecondaryForegroundColorDependingOnTheme(context, mainColor); + public void applyBrand(int mainColor) { + if (isBrandingEnabled(context)) { + this.mainColor = getSecondaryForegroundColorDependingOnTheme(context, mainColor); notifyDataSetChanged(); } } diff --git a/app/src/main/java/it/niedermann/nextcloud/deck/ui/board/accesscontrol/AccessControlDialogFragment.java b/app/src/main/java/it/niedermann/nextcloud/deck/ui/board/accesscontrol/AccessControlDialogFragment.java index 9b170aacd..33c0fe5b1 100644 --- a/app/src/main/java/it/niedermann/nextcloud/deck/ui/board/accesscontrol/AccessControlDialogFragment.java +++ b/app/src/main/java/it/niedermann/nextcloud/deck/ui/board/accesscontrol/AccessControlDialogFragment.java @@ -25,7 +25,6 @@ import it.niedermann.nextcloud.deck.model.full.FullBoard; import it.niedermann.nextcloud.deck.persistence.sync.SyncManager; import it.niedermann.nextcloud.deck.persistence.sync.adapters.db.util.WrappedLiveData; import it.niedermann.nextcloud.deck.ui.MainViewModel; -import it.niedermann.nextcloud.deck.ui.branding.BrandedActivity; import it.niedermann.nextcloud.deck.ui.branding.BrandedAlertDialogBuilder; import it.niedermann.nextcloud.deck.ui.branding.BrandedDialogFragment; import it.niedermann.nextcloud.deck.ui.branding.BrandedSnackbar; @@ -34,6 +33,7 @@ import it.niedermann.nextcloud.deck.ui.exception.ExceptionDialogFragment; import static it.niedermann.nextcloud.deck.persistence.sync.adapters.db.util.LiveDataHelper.observeOnce; import static it.niedermann.nextcloud.deck.ui.board.accesscontrol.AccessControlAdapter.HEADER_ITEM_LOCAL_ID; +import static it.niedermann.nextcloud.deck.ui.branding.BrandingUtil.applyBrandToEditText; public class AccessControlDialogFragment extends BrandedDialogFragment implements AccessControlChangedListener, OnItemClickListener { @@ -135,9 +135,9 @@ public class AccessControlDialogFragment extends BrandedDialogFragment implement } @Override - public void applyBrand(int mainColor, int textColor) { - BrandedActivity.applyBrandToEditText(mainColor, textColor, binding.people); - this.adapter.applyBrand(mainColor, textColor); + public void applyBrand(int mainColor) { + applyBrandToEditText(mainColor, binding.people); + this.adapter.applyBrand(mainColor); } public static DialogFragment newInstance(long boardLocalId) { diff --git a/app/src/main/java/it/niedermann/nextcloud/deck/ui/board/managelabels/EditLabelDialogFragment.java b/app/src/main/java/it/niedermann/nextcloud/deck/ui/board/managelabels/EditLabelDialogFragment.java index 7570510fd..d460d1590 100644 --- a/app/src/main/java/it/niedermann/nextcloud/deck/ui/board/managelabels/EditLabelDialogFragment.java +++ b/app/src/main/java/it/niedermann/nextcloud/deck/ui/board/managelabels/EditLabelDialogFragment.java @@ -11,10 +11,11 @@ import androidx.fragment.app.DialogFragment; import it.niedermann.nextcloud.deck.R; import it.niedermann.nextcloud.deck.databinding.DialogTextColorInputBinding; import it.niedermann.nextcloud.deck.model.Label; -import it.niedermann.nextcloud.deck.ui.branding.BrandedActivity; import it.niedermann.nextcloud.deck.ui.branding.BrandedAlertDialogBuilder; import it.niedermann.nextcloud.deck.ui.branding.BrandedDialogFragment; +import static it.niedermann.nextcloud.deck.ui.branding.BrandingUtil.applyBrandToEditText; + public class EditLabelDialogFragment extends BrandedDialogFragment { private DialogTextColorInputBinding binding; @@ -83,7 +84,7 @@ public class EditLabelDialogFragment extends BrandedDialogFragment { } @Override - public void applyBrand(int mainColor, int textColor) { - BrandedActivity.applyBrandToEditText(mainColor, textColor, binding.input); + public void applyBrand(int mainColor) { + applyBrandToEditText(mainColor, binding.input); } }
\ No newline at end of file diff --git a/app/src/main/java/it/niedermann/nextcloud/deck/ui/board/managelabels/ManageLabelsAdapter.java b/app/src/main/java/it/niedermann/nextcloud/deck/ui/board/managelabels/ManageLabelsAdapter.java index 34c06530c..d8bb57a11 100644 --- a/app/src/main/java/it/niedermann/nextcloud/deck/ui/board/managelabels/ManageLabelsAdapter.java +++ b/app/src/main/java/it/niedermann/nextcloud/deck/ui/board/managelabels/ManageLabelsAdapter.java @@ -11,12 +11,13 @@ import java.util.LinkedList; import java.util.List; import java.util.NoSuchElementException; -import it.niedermann.nextcloud.deck.Application; import it.niedermann.nextcloud.deck.R; import it.niedermann.nextcloud.deck.databinding.ItemManageLabelBinding; import it.niedermann.nextcloud.deck.model.Label; import it.niedermann.nextcloud.deck.ui.branding.Branded; -import it.niedermann.nextcloud.deck.ui.branding.BrandedActivity; + +import static it.niedermann.nextcloud.deck.ui.branding.BrandingUtil.getSecondaryForegroundColorDependingOnTheme; +import static it.niedermann.nextcloud.deck.ui.branding.BrandingUtil.isBrandingEnabled; public class ManageLabelsAdapter extends RecyclerView.Adapter<ManageLabelsViewHolder> implements Branded { @@ -74,9 +75,9 @@ public class ManageLabelsAdapter extends RecyclerView.Adapter<ManageLabelsViewHo } @Override - public void applyBrand(int mainColor, int textColor) { - if (Application.isBrandingEnabled(context)) { - this.mainColor = BrandedActivity.getSecondaryForegroundColorDependingOnTheme(context, mainColor); + public void applyBrand(int mainColor) { + if (isBrandingEnabled(context)) { + this.mainColor = getSecondaryForegroundColorDependingOnTheme(context, mainColor); notifyDataSetChanged(); } } diff --git a/app/src/main/java/it/niedermann/nextcloud/deck/ui/board/managelabels/ManageLabelsDialogFragment.java b/app/src/main/java/it/niedermann/nextcloud/deck/ui/board/managelabels/ManageLabelsDialogFragment.java index d6727f4b0..3391c7a99 100644 --- a/app/src/main/java/it/niedermann/nextcloud/deck/ui/board/managelabels/ManageLabelsDialogFragment.java +++ b/app/src/main/java/it/niedermann/nextcloud/deck/ui/board/managelabels/ManageLabelsDialogFragment.java @@ -20,12 +20,13 @@ import it.niedermann.nextcloud.deck.model.Label; import it.niedermann.nextcloud.deck.persistence.sync.SyncManager; import it.niedermann.nextcloud.deck.persistence.sync.adapters.db.util.WrappedLiveData; import it.niedermann.nextcloud.deck.ui.MainViewModel; -import it.niedermann.nextcloud.deck.ui.branding.BrandedActivity; import it.niedermann.nextcloud.deck.ui.branding.BrandedAlertDialogBuilder; import it.niedermann.nextcloud.deck.ui.branding.BrandedDeleteAlertDialogBuilder; import it.niedermann.nextcloud.deck.ui.branding.BrandedDialogFragment; import static it.niedermann.nextcloud.deck.persistence.sync.adapters.db.util.LiveDataHelper.observeOnce; +import static it.niedermann.nextcloud.deck.ui.branding.BrandingUtil.applyBrandToEditText; +import static it.niedermann.nextcloud.deck.ui.branding.BrandingUtil.applyBrandToFAB; public class ManageLabelsDialogFragment extends BrandedDialogFragment implements ManageLabelListener, EditLabelListener { @@ -108,9 +109,9 @@ public class ManageLabelsDialogFragment extends BrandedDialogFragment implements } @Override - public void applyBrand(int mainColor, int textColor) { - BrandedActivity.applyBrandToFAB(mainColor, textColor, binding.fab); - BrandedActivity.applyBrandToEditText(mainColor, textColor, binding.addLabelTitle); + public void applyBrand(int mainColor) { + applyBrandToFAB(mainColor, binding.fab); + applyBrandToEditText(mainColor, binding.addLabelTitle); } public static DialogFragment newInstance(long boardLocalId) { diff --git a/app/src/main/java/it/niedermann/nextcloud/deck/ui/branding/Branded.java b/app/src/main/java/it/niedermann/nextcloud/deck/ui/branding/Branded.java index 15ffb8528..99ad9c074 100644 --- a/app/src/main/java/it/niedermann/nextcloud/deck/ui/branding/Branded.java +++ b/app/src/main/java/it/niedermann/nextcloud/deck/ui/branding/Branded.java @@ -5,5 +5,5 @@ import androidx.annotation.UiThread; public interface Branded { @UiThread - void applyBrand(@ColorInt int mainColor, @ColorInt int textColor); + void applyBrand(@ColorInt int mainColor); }
\ No newline at end of file diff --git a/app/src/main/java/it/niedermann/nextcloud/deck/ui/branding/BrandedActivity.java b/app/src/main/java/it/niedermann/nextcloud/deck/ui/branding/BrandedActivity.java index 6278ac1f5..3420e888b 100644 --- a/app/src/main/java/it/niedermann/nextcloud/deck/ui/branding/BrandedActivity.java +++ b/app/src/main/java/it/niedermann/nextcloud/deck/ui/branding/BrandedActivity.java @@ -1,158 +1,41 @@ package it.niedermann.nextcloud.deck.ui.branding; -import android.content.Context; -import android.content.res.ColorStateList; -import android.graphics.Color; -import android.graphics.PorterDuff; -import android.graphics.drawable.Drawable; -import android.os.Bundle; +import android.util.TypedValue; import android.view.Menu; -import android.view.View; -import android.view.Window; -import android.widget.EditText; import androidx.annotation.ColorInt; -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; import androidx.appcompat.app.AppCompatActivity; -import androidx.appcompat.widget.Toolbar; -import androidx.core.graphics.drawable.DrawableCompat; -import com.google.android.material.floatingactionbutton.FloatingActionButton; -import com.google.android.material.tabs.TabLayout; - -import it.niedermann.nextcloud.deck.Application; -import it.niedermann.nextcloud.deck.DeckLog; import it.niedermann.nextcloud.deck.R; -import it.niedermann.nextcloud.deck.util.ColorUtil; -import static android.os.Build.VERSION.SDK_INT; -import static android.os.Build.VERSION_CODES.LOLLIPOP; -import static android.os.Build.VERSION_CODES.M; -import static it.niedermann.nextcloud.deck.util.ColorUtil.contrastRatioIsSufficient; -import static it.niedermann.nextcloud.deck.util.ColorUtil.isColorDark; +import static it.niedermann.nextcloud.deck.ui.branding.BrandingUtil.isBrandingEnabled; +import static it.niedermann.nextcloud.deck.ui.branding.BrandingUtil.readBrandMainColor; +import static it.niedermann.nextcloud.deck.ui.branding.BrandingUtil.tintMenuIcon; public abstract class BrandedActivity extends AppCompatActivity implements Branded { - @Override - protected void onCreate(@Nullable Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - - if (Application.isBrandingEnabled(this)) { - @ColorInt final int mainColor = Application.readBrandMainColor(this); - @ColorInt final int textColor = Application.readBrandTextColor(this); - setTheme(ColorUtil.isColorDark(textColor) ? R.style.AppThemeLightBrand : R.style.AppTheme); - applyBrandToStatusbar(getWindow(), mainColor, textColor); - } else { - setTheme(R.style.AppTheme); - } - } + @ColorInt + protected int colorAccent; @Override protected void onStart() { super.onStart(); - if (Application.isBrandingEnabled(this)) { - @ColorInt final int mainColor = Application.readBrandMainColor(this); - @ColorInt final int textColor = Application.readBrandTextColor(this); - applyBrand(mainColor, textColor); + final TypedValue typedValue = new TypedValue(); + getTheme().resolveAttribute(R.attr.colorAccent, typedValue, true); + colorAccent = typedValue.data; + + if (isBrandingEnabled(this)) { + @ColorInt final int mainColor = readBrandMainColor(this); + applyBrand(mainColor); } } - // TODO maybe this can be handled in R.style.AppThemLightBrand @Override public boolean onCreateOptionsMenu(Menu menu) { - @ColorInt final int textColor = Application.readBrandTextColor(this); for (int i = 0; i < menu.size(); i++) { - Drawable drawable = menu.getItem(i).getIcon(); - if (drawable != null) { - drawable = DrawableCompat.wrap(drawable); - DrawableCompat.setTint(drawable, textColor); - menu.getItem(i).setIcon(drawable); - } + tintMenuIcon(menu.getItem(i), colorAccent); } return super.onCreateOptionsMenu(menu); } - - public static void applyBrandToStatusbar(@NonNull Window window, @ColorInt int mainColor, @ColorInt int textColor) { - if (SDK_INT >= LOLLIPOP) { // Set status bar color - window.setStatusBarColor(mainColor); - if (SDK_INT >= M) { // Set icon and text color of status bar - final View decorView = window.getDecorView(); - if (isColorDark(mainColor)) { - int flags = decorView.getSystemUiVisibility(); - flags &= ~View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR; - decorView.setSystemUiVisibility(flags); - } else { - decorView.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR); - } - } - } - } - - public static void applyBrandToPrimaryToolbar(@ColorInt int mainColor, @ColorInt int textColor, @NonNull Toolbar toolbar) { - toolbar.setBackgroundColor(mainColor); - toolbar.setTitleTextColor(textColor); - final Drawable overflowDrawable = toolbar.getOverflowIcon(); - if (overflowDrawable != null) { - overflowDrawable.setColorFilter(textColor, PorterDuff.Mode.SRC_ATOP); - toolbar.setOverflowIcon(overflowDrawable); - } - - final Drawable navigationDrawable = toolbar.getNavigationIcon(); - if (navigationDrawable != null) { - navigationDrawable.setColorFilter(textColor, PorterDuff.Mode.SRC_ATOP); - toolbar.setNavigationIcon(navigationDrawable); - } - } - - protected void applyBrandToPrimaryTabLayout(@ColorInt int mainColor, @ColorInt int textColor, @NonNull TabLayout tabLayout) { - tabLayout.setBackgroundColor(mainColor); - tabLayout.setTabTextColors(textColor, textColor); - tabLayout.setTabIconTint(ColorStateList.valueOf(textColor)); - tabLayout.setSelectedTabIndicatorColor(textColor); - } - - public static void applyBrandToFAB(@ColorInt int mainColor, @ColorInt int textColor, @NonNull FloatingActionButton fab) { - fab.setSupportBackgroundTintList(ColorStateList.valueOf(mainColor)); - fab.setColorFilter(textColor); - } - - public static void applyBrandToEditText(@ColorInt int mainColor, @ColorInt int textColor, @NonNull EditText editText) { - @ColorInt final int finalMainColor = getSecondaryForegroundColorDependingOnTheme(editText.getContext(), mainColor); - DrawableCompat.setTintList(editText.getBackground(), new ColorStateList( - new int[][]{ - new int[]{android.R.attr.state_active}, - new int[]{android.R.attr.state_activated}, - new int[]{android.R.attr.state_focused}, - new int[]{android.R.attr.state_pressed}, - new int[]{} - }, - new int[]{ - finalMainColor, - finalMainColor, - finalMainColor, - finalMainColor, - editText.getContext().getResources().getColor(R.color.fg_secondary) - } - )); - } - - /** - * Since we may collide with dark theme in this area, we have to make sure that the color is visible depending on the background - */ - @ColorInt - public static int - getSecondaryForegroundColorDependingOnTheme(@NonNull Context context, @ColorInt int mainColor) { - final boolean isDarkTheme = Application.getAppTheme(context); - if (isDarkTheme && !contrastRatioIsSufficient(mainColor, Color.BLACK)) { - DeckLog.verbose("Contrast ratio between brand color " + String.format("#%06X", (0xFFFFFF & mainColor)) + " and dark theme is too low. Falling back to WHITE as brand color."); - return Color.WHITE; - } else if (!isDarkTheme && !contrastRatioIsSufficient(mainColor, Color.WHITE)) { - DeckLog.verbose("Contrast ratio between brand color " + String.format("#%06X", (0xFFFFFF & mainColor)) + " and light theme is too low. Falling back to BLACK as brand color."); - return Color.BLACK; - } else { - return mainColor; - } - } } diff --git a/app/src/main/java/it/niedermann/nextcloud/deck/ui/branding/BrandedAlertDialogBuilder.java b/app/src/main/java/it/niedermann/nextcloud/deck/ui/branding/BrandedAlertDialogBuilder.java index d502772b3..cfeffe7dc 100644 --- a/app/src/main/java/it/niedermann/nextcloud/deck/ui/branding/BrandedAlertDialogBuilder.java +++ b/app/src/main/java/it/niedermann/nextcloud/deck/ui/branding/BrandedAlertDialogBuilder.java @@ -11,7 +11,8 @@ import androidx.appcompat.app.AlertDialog; import org.jetbrains.annotations.NotNull; -import it.niedermann.nextcloud.deck.Application; +import static it.niedermann.nextcloud.deck.ui.branding.BrandingUtil.getSecondaryForegroundColorDependingOnTheme; +import static it.niedermann.nextcloud.deck.ui.branding.BrandingUtil.readBrandMainColor; public class BrandedAlertDialogBuilder extends AlertDialog.Builder implements Branded { @@ -27,23 +28,22 @@ public class BrandedAlertDialogBuilder extends AlertDialog.Builder implements Br this.dialog = super.create(); @NonNull Context context = getContext(); - @ColorInt final int mainColor = Application.readBrandMainColor(context); - @ColorInt final int textColor = Application.readBrandTextColor(context); - applyBrand(mainColor, textColor); - dialog.setOnShowListener(dialog -> applyBrand(mainColor, textColor)); + @ColorInt final int mainColor = readBrandMainColor(context); + applyBrand(mainColor); + dialog.setOnShowListener(dialog -> applyBrand(mainColor)); return dialog; } @CallSuper @Override - public void applyBrand(int mainColor, int textColor) { + public void applyBrand(int mainColor) { final Button[] buttons = new Button[3]; buttons[0] = dialog.getButton(DialogInterface.BUTTON_POSITIVE); buttons[1] = dialog.getButton(DialogInterface.BUTTON_NEGATIVE); buttons[2] = dialog.getButton(DialogInterface.BUTTON_NEUTRAL); for (Button button : buttons) { if (button != null) { - button.setTextColor(BrandedActivity.getSecondaryForegroundColorDependingOnTheme(button.getContext(), mainColor)); + button.setTextColor(getSecondaryForegroundColorDependingOnTheme(button.getContext(), mainColor)); } } } diff --git a/app/src/main/java/it/niedermann/nextcloud/deck/ui/branding/BrandedDatePickerDialog.java b/app/src/main/java/it/niedermann/nextcloud/deck/ui/branding/BrandedDatePickerDialog.java index b56ad3669..5bef66f2c 100644 --- a/app/src/main/java/it/niedermann/nextcloud/deck/ui/branding/BrandedDatePickerDialog.java +++ b/app/src/main/java/it/niedermann/nextcloud/deck/ui/branding/BrandedDatePickerDialog.java @@ -10,37 +10,41 @@ import android.view.ViewGroup; import androidx.annotation.ColorInt; import androidx.annotation.NonNull; import androidx.annotation.Nullable; +import androidx.core.content.ContextCompat; import com.wdullaer.materialdatetimepicker.date.DatePickerDialog; import java.util.Calendar; -import it.niedermann.nextcloud.deck.Application; +import it.niedermann.nextcloud.deck.R; import it.niedermann.nextcloud.deck.util.ColorUtil; +import static it.niedermann.nextcloud.deck.DeckApplication.isDarkTheme; +import static it.niedermann.nextcloud.deck.ui.branding.BrandingUtil.getSecondaryForegroundColorDependingOnTheme; +import static it.niedermann.nextcloud.deck.ui.branding.BrandingUtil.isBrandingEnabled; +import static it.niedermann.nextcloud.deck.ui.branding.BrandingUtil.readBrandMainColor; + public class BrandedDatePickerDialog extends DatePickerDialog implements Branded { @Override public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { @Nullable Context context = getContext(); if (context != null) { - setThemeDark(Application.getAppTheme(context)); - if (Application.isBrandingEnabled(context)) { - @ColorInt final int mainColor = Application.readBrandMainColor(context); - @ColorInt final int textColor = Application.readBrandTextColor(context); - applyBrand(mainColor, textColor); + setThemeDark(isDarkTheme(context)); + if (isBrandingEnabled(context)) { + applyBrand(readBrandMainColor(context)); } } return super.onCreateView(inflater, container, savedInstanceState); } @Override - public void applyBrand(int mainColor, int textColor) { - @ColorInt final int buttonTextColor = BrandedActivity.getSecondaryForegroundColorDependingOnTheme(requireContext(), mainColor); + public void applyBrand(int mainColor) { + @ColorInt final int buttonTextColor = getSecondaryForegroundColorDependingOnTheme(requireContext(), mainColor); setOkColor(buttonTextColor); setCancelColor(buttonTextColor); // Text in picker title is always white - setAccentColor(ColorUtil.contrastRatioIsSufficient(Color.WHITE, mainColor) ? mainColor : textColor); + setAccentColor(ColorUtil.contrastRatioIsSufficientBigAreas(Color.WHITE, mainColor) ? mainColor : ContextCompat.getColor(requireContext(), R.color.accent)); } /** diff --git a/app/src/main/java/it/niedermann/nextcloud/deck/ui/branding/BrandedDeleteAlertDialogBuilder.java b/app/src/main/java/it/niedermann/nextcloud/deck/ui/branding/BrandedDeleteAlertDialogBuilder.java index 29cac691f..ec3cef553 100644 --- a/app/src/main/java/it/niedermann/nextcloud/deck/ui/branding/BrandedDeleteAlertDialogBuilder.java +++ b/app/src/main/java/it/niedermann/nextcloud/deck/ui/branding/BrandedDeleteAlertDialogBuilder.java @@ -16,8 +16,8 @@ public class BrandedDeleteAlertDialogBuilder extends BrandedAlertDialogBuilder { @CallSuper @Override - public void applyBrand(int mainColor, int textColor) { - super.applyBrand(mainColor, textColor); + public void applyBrand(int mainColor) { + super.applyBrand(mainColor); final Button positiveButton = dialog.getButton(DialogInterface.BUTTON_POSITIVE); if (positiveButton != null) { positiveButton.setTextColor(getContext().getResources().getColor(R.color.danger)); diff --git a/app/src/main/java/it/niedermann/nextcloud/deck/ui/branding/BrandedDialogFragment.java b/app/src/main/java/it/niedermann/nextcloud/deck/ui/branding/BrandedDialogFragment.java index 75eb90b12..adf3844e9 100644 --- a/app/src/main/java/it/niedermann/nextcloud/deck/ui/branding/BrandedDialogFragment.java +++ b/app/src/main/java/it/niedermann/nextcloud/deck/ui/branding/BrandedDialogFragment.java @@ -2,11 +2,11 @@ package it.niedermann.nextcloud.deck.ui.branding; import android.content.Context; -import androidx.annotation.ColorInt; import androidx.annotation.Nullable; import androidx.fragment.app.DialogFragment; -import it.niedermann.nextcloud.deck.Application; +import static it.niedermann.nextcloud.deck.ui.branding.BrandingUtil.isBrandingEnabled; +import static it.niedermann.nextcloud.deck.ui.branding.BrandingUtil.readBrandMainColor; public abstract class BrandedDialogFragment extends DialogFragment implements Branded { @@ -16,10 +16,8 @@ public abstract class BrandedDialogFragment extends DialogFragment implements Br @Nullable Context context = getContext(); if (context != null) { - if (Application.isBrandingEnabled(context)) { - @ColorInt final int mainColor = Application.readBrandMainColor(context); - @ColorInt final int textColor = Application.readBrandTextColor(context); - applyBrand(mainColor, textColor); + if (isBrandingEnabled(context)) { + applyBrand(readBrandMainColor(context)); } } } diff --git a/app/src/main/java/it/niedermann/nextcloud/deck/ui/branding/BrandedFragment.java b/app/src/main/java/it/niedermann/nextcloud/deck/ui/branding/BrandedFragment.java index 0854b547b..fe202f48c 100644 --- a/app/src/main/java/it/niedermann/nextcloud/deck/ui/branding/BrandedFragment.java +++ b/app/src/main/java/it/niedermann/nextcloud/deck/ui/branding/BrandedFragment.java @@ -2,11 +2,11 @@ package it.niedermann.nextcloud.deck.ui.branding; import android.content.Context; -import androidx.annotation.ColorInt; import androidx.annotation.Nullable; import androidx.fragment.app.Fragment; -import it.niedermann.nextcloud.deck.Application; +import static it.niedermann.nextcloud.deck.ui.branding.BrandingUtil.isBrandingEnabled; +import static it.niedermann.nextcloud.deck.ui.branding.BrandingUtil.readBrandMainColor; public abstract class BrandedFragment extends Fragment implements Branded { @@ -15,10 +15,8 @@ public abstract class BrandedFragment extends Fragment implements Branded { super.onStart(); @Nullable Context context = getContext(); - if (context != null && Application.isBrandingEnabled(context)) { - @ColorInt final int mainColor = Application.readBrandMainColor(context); - @ColorInt final int textColor = Application.readBrandTextColor(context); - applyBrand(mainColor, textColor); + if (context != null && isBrandingEnabled(context)) { + applyBrand(readBrandMainColor(context)); } } } diff --git a/app/src/main/java/it/niedermann/nextcloud/deck/ui/branding/BrandedPreferenceCategory.java b/app/src/main/java/it/niedermann/nextcloud/deck/ui/branding/BrandedPreferenceCategory.java index dd7f0a037..e01c523c6 100644 --- a/app/src/main/java/it/niedermann/nextcloud/deck/ui/branding/BrandedPreferenceCategory.java +++ b/app/src/main/java/it/niedermann/nextcloud/deck/ui/branding/BrandedPreferenceCategory.java @@ -10,10 +10,9 @@ import androidx.annotation.Nullable; import androidx.preference.PreferenceCategory; import androidx.preference.PreferenceViewHolder; -import it.niedermann.nextcloud.deck.Application; - -import static it.niedermann.nextcloud.deck.Application.readBrandMainColor; -import static it.niedermann.nextcloud.deck.ui.branding.BrandedActivity.getSecondaryForegroundColorDependingOnTheme; +import static it.niedermann.nextcloud.deck.ui.branding.BrandingUtil.getSecondaryForegroundColorDependingOnTheme; +import static it.niedermann.nextcloud.deck.ui.branding.BrandingUtil.isBrandingEnabled; +import static it.niedermann.nextcloud.deck.ui.branding.BrandingUtil.readBrandMainColor; public class BrandedPreferenceCategory extends PreferenceCategory { @@ -21,7 +20,7 @@ public class BrandedPreferenceCategory extends PreferenceCategory { public void onBindViewHolder(PreferenceViewHolder holder) { super.onBindViewHolder(holder); - if (Application.isBrandingEnabled(getContext())) { + if (isBrandingEnabled(getContext())) { final View v = holder.itemView.findViewById(android.R.id.title); @Nullable final Context context = getContext(); if (context != null && v instanceof TextView) { diff --git a/app/src/main/java/it/niedermann/nextcloud/deck/ui/branding/BrandedSnackbar.java b/app/src/main/java/it/niedermann/nextcloud/deck/ui/branding/BrandedSnackbar.java index b28b00ac0..20e6f8dc8 100644 --- a/app/src/main/java/it/niedermann/nextcloud/deck/ui/branding/BrandedSnackbar.java +++ b/app/src/main/java/it/niedermann/nextcloud/deck/ui/branding/BrandedSnackbar.java @@ -3,24 +3,31 @@ package it.niedermann.nextcloud.deck.ui.branding; import android.graphics.Color; import android.view.View; +import androidx.annotation.ColorInt; import androidx.annotation.NonNull; import androidx.annotation.StringRes; +import androidx.core.content.ContextCompat; import com.google.android.material.snackbar.BaseTransientBottomBar; import com.google.android.material.snackbar.Snackbar; -import it.niedermann.nextcloud.deck.Application; +import it.niedermann.nextcloud.deck.R; import it.niedermann.nextcloud.deck.util.ColorUtil; +import static it.niedermann.nextcloud.deck.ui.branding.BrandingUtil.isBrandingEnabled; +import static it.niedermann.nextcloud.deck.ui.branding.BrandingUtil.readBrandMainColor; + public class BrandedSnackbar { @NonNull public static Snackbar make( @NonNull View view, @NonNull CharSequence text, @BaseTransientBottomBar.Duration int duration) { final Snackbar snackbar = Snackbar.make(view, text, duration); - if (Application.isBrandingEnabled(view.getContext())) { - int color = Application.readBrandMainColor(view.getContext()); + if (isBrandingEnabled(view.getContext())) { + @ColorInt final int color = readBrandMainColor(view.getContext()); snackbar.setActionTextColor(ColorUtil.isColorDark(color) ? Color.WHITE : color); + } else { + snackbar.setActionTextColor(ContextCompat.getColor(view.getContext(), R.color.primary)); } return snackbar; } diff --git a/app/src/main/java/it/niedermann/nextcloud/deck/ui/branding/BrandedSwitchPreference.java b/app/src/main/java/it/niedermann/nextcloud/deck/ui/branding/BrandedSwitchPreference.java index a0b2c222e..463800a0a 100644 --- a/app/src/main/java/it/niedermann/nextcloud/deck/ui/branding/BrandedSwitchPreference.java +++ b/app/src/main/java/it/niedermann/nextcloud/deck/ui/branding/BrandedSwitchPreference.java @@ -13,21 +13,16 @@ import androidx.core.graphics.drawable.DrawableCompat; import androidx.preference.PreferenceViewHolder; import androidx.preference.SwitchPreference; -import it.niedermann.nextcloud.deck.Application; import it.niedermann.nextcloud.deck.R; -import static android.os.Build.VERSION.SDK_INT; -import static android.os.Build.VERSION_CODES.JELLY_BEAN; -import static it.niedermann.nextcloud.deck.ui.branding.BrandedActivity.getSecondaryForegroundColorDependingOnTheme; +import static it.niedermann.nextcloud.deck.ui.branding.BrandingUtil.getSecondaryForegroundColorDependingOnTheme; +import static it.niedermann.nextcloud.deck.ui.branding.BrandingUtil.isBrandingEnabled; public class BrandedSwitchPreference extends SwitchPreference implements Branded { @ColorInt private Integer mainColor = null; - @ColorInt - private Integer textColor = null; - @Nullable private Switch switchView; @@ -51,26 +46,25 @@ public class BrandedSwitchPreference extends SwitchPreference implements Branded public void onBindViewHolder(PreferenceViewHolder holder) { super.onBindViewHolder(holder); - if (Application.isBrandingEnabled(getContext()) && holder.itemView instanceof ViewGroup) { + if (isBrandingEnabled(getContext()) && holder.itemView instanceof ViewGroup) { switchView = findSwitchWidget(holder.itemView); - if (mainColor != null && textColor != null) { + if (mainColor != null) { applyBrand(); } } } @Override - public void applyBrand(@ColorInt int mainColor, @ColorInt int textColor) { + public void applyBrand(@ColorInt int mainColor) { this.mainColor = mainColor; - this.textColor = textColor; // onBindViewHolder is called after applyBrand, therefore we have to store the given values and apply them later. - if (Application.isBrandingEnabled(getContext())) { + if (isBrandingEnabled(getContext())) { applyBrand(); } } private void applyBrand() { - if (switchView != null && SDK_INT >= JELLY_BEAN) { + if (switchView != null) { final int finalMainColor = getSecondaryForegroundColorDependingOnTheme(getContext(), mainColor); // int trackColor = Color.argb(77, Color.red(finalMainColor), Color.green(finalMainColor), Color.blue(finalMainColor)); DrawableCompat.setTintList(switchView.getThumbDrawable(), new ColorStateList( diff --git a/app/src/main/java/it/niedermann/nextcloud/deck/ui/branding/BrandedTimePickerDialog.java b/app/src/main/java/it/niedermann/nextcloud/deck/ui/branding/BrandedTimePickerDialog.java index cd67d7820..a1963aa18 100644 --- a/app/src/main/java/it/niedermann/nextcloud/deck/ui/branding/BrandedTimePickerDialog.java +++ b/app/src/main/java/it/niedermann/nextcloud/deck/ui/branding/BrandedTimePickerDialog.java @@ -10,37 +10,41 @@ import android.view.ViewGroup; import androidx.annotation.ColorInt; import androidx.annotation.NonNull; import androidx.annotation.Nullable; +import androidx.core.content.ContextCompat; import com.wdullaer.materialdatetimepicker.time.TimePickerDialog; import java.util.Calendar; -import it.niedermann.nextcloud.deck.Application; +import it.niedermann.nextcloud.deck.R; import it.niedermann.nextcloud.deck.util.ColorUtil; +import static it.niedermann.nextcloud.deck.DeckApplication.isDarkTheme; +import static it.niedermann.nextcloud.deck.ui.branding.BrandingUtil.getSecondaryForegroundColorDependingOnTheme; +import static it.niedermann.nextcloud.deck.ui.branding.BrandingUtil.isBrandingEnabled; +import static it.niedermann.nextcloud.deck.ui.branding.BrandingUtil.readBrandMainColor; + public class BrandedTimePickerDialog extends TimePickerDialog implements Branded { @Override public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { @Nullable Context context = getContext(); if (context != null) { - setThemeDark(Application.getAppTheme(context)); - if (Application.isBrandingEnabled(context)) { - @ColorInt final int mainColor = Application.readBrandMainColor(context); - @ColorInt final int textColor = Application.readBrandTextColor(context); - applyBrand(mainColor, textColor); + setThemeDark(isDarkTheme(context)); + if (isBrandingEnabled(context)) { + applyBrand(readBrandMainColor(context)); } } return super.onCreateView(inflater, container, savedInstanceState); } @Override - public void applyBrand(int mainColor, int textColor) { - @ColorInt final int buttonTextColor = BrandedActivity.getSecondaryForegroundColorDependingOnTheme(requireContext(), mainColor); + public void applyBrand(int mainColor) { + @ColorInt final int buttonTextColor = getSecondaryForegroundColorDependingOnTheme(requireContext(), mainColor); setOkColor(buttonTextColor); setCancelColor(buttonTextColor); // Text in picker title is always white - setAccentColor(ColorUtil.contrastRatioIsSufficient(Color.WHITE, mainColor) ? mainColor : textColor); + setAccentColor(ColorUtil.contrastRatioIsSufficientBigAreas(Color.WHITE, mainColor) ? mainColor : ContextCompat.getColor(requireContext(), R.color.accent)); } /** diff --git a/app/src/main/java/it/niedermann/nextcloud/deck/ui/branding/BrandingUtil.java b/app/src/main/java/it/niedermann/nextcloud/deck/ui/branding/BrandingUtil.java new file mode 100644 index 000000000..02ad6b309 --- /dev/null +++ b/app/src/main/java/it/niedermann/nextcloud/deck/ui/branding/BrandingUtil.java @@ -0,0 +1,128 @@ +package it.niedermann.nextcloud.deck.ui.branding; + +import android.content.Context; +import android.content.SharedPreferences; +import android.content.res.ColorStateList; +import android.graphics.Color; +import android.graphics.drawable.Drawable; +import android.view.MenuItem; +import android.widget.EditText; + +import androidx.annotation.ColorInt; +import androidx.annotation.NonNull; +import androidx.core.content.ContextCompat; +import androidx.core.graphics.drawable.DrawableCompat; +import androidx.preference.PreferenceManager; + +import com.google.android.material.floatingactionbutton.FloatingActionButton; +import com.google.android.material.tabs.TabLayout; + +import it.niedermann.nextcloud.deck.DeckLog; +import it.niedermann.nextcloud.deck.R; + +import static it.niedermann.nextcloud.deck.DeckApplication.isDarkTheme; +import static it.niedermann.nextcloud.deck.util.ColorUtil.contrastRatioIsSufficient; +import static it.niedermann.nextcloud.deck.util.ColorUtil.contrastRatioIsSufficientBigAreas; +import static it.niedermann.nextcloud.deck.util.ColorUtil.getContrastRatio; +import static it.niedermann.nextcloud.deck.util.ColorUtil.getForegroundColorForBackgroundColor; + +public abstract class BrandingUtil { + + private BrandingUtil() { + // Util class + } + + public static boolean isBrandingEnabled(@NonNull Context context) { + SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context); + return prefs.getBoolean(context.getString(R.string.pref_key_branding), true); + } + + @ColorInt + public static int readBrandMainColor(@NonNull Context context) { + if (isBrandingEnabled(context)) { + SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(context.getApplicationContext()); + DeckLog.log("--- Read: shared_preference_theme_main"); + return sharedPreferences.getInt(context.getString(R.string.shared_preference_theme_main), context.getApplicationContext().getResources().getColor(R.color.defaultBrand)); + } else { + return context.getResources().getColor(R.color.defaultBrand); + } + } + + public static void saveBrandColors(@NonNull Context context, @ColorInt int mainColor) { + if (isBrandingEnabled(context) && context instanceof BrandedActivity) { + final BrandedActivity activity = (BrandedActivity) context; + activity.applyBrand(mainColor); + } + SharedPreferences.Editor editor = PreferenceManager.getDefaultSharedPreferences(context).edit(); + DeckLog.log("--- Write: shared_preference_theme_main" + " | " + mainColor); + editor.putInt(context.getString(R.string.shared_preference_theme_main), mainColor); + editor.apply(); + } + + public static void clearBrandColors(@NonNull Context context) { + if (isBrandingEnabled(context) && context instanceof BrandedActivity) { + final BrandedActivity activity = (BrandedActivity) context; + activity.applyBrand(ContextCompat.getColor(context, R.color.defaultBrand)); + } + SharedPreferences.Editor editor = PreferenceManager.getDefaultSharedPreferences(context).edit(); + DeckLog.log("--- Write: Remove: shared_preference_theme_main" + " | "); + editor.remove(context.getString(R.string.shared_preference_theme_main)); + editor.apply(); + } + + /** + * Since we may collide with dark theme in this area, we have to make sure that the color is visible depending on the background + */ + @ColorInt + public static int getSecondaryForegroundColorDependingOnTheme(@NonNull Context context, @ColorInt int mainColor) { + if (contrastRatioIsSufficient(mainColor, ContextCompat.getColor(context, R.color.primary))) { + return mainColor; + } + DeckLog.verbose("Contrast ratio between brand color " + String.format("#%06X", (0xFFFFFF & mainColor)) + " and primary theme background is too low. Falling back to WHITE/BLACK as brand color."); + return isDarkTheme(context) ? Color.WHITE : Color.BLACK; + } + + public static void applyBrandToFAB(@ColorInt int mainColor, @NonNull FloatingActionButton fab) { + final boolean contrastRatioIsSufficient = contrastRatioIsSufficientBigAreas(mainColor, ContextCompat.getColor(fab.getContext(), R.color.primary)); + fab.setSupportBackgroundTintList(ColorStateList.valueOf(contrastRatioIsSufficient + ? mainColor + : ContextCompat.getColor(fab.getContext(), R.color.accent))); + fab.setColorFilter(contrastRatioIsSufficient ? getForegroundColorForBackgroundColor(mainColor) : mainColor); + } + + public static void applyBrandToPrimaryTabLayout(@ColorInt int mainColor, @NonNull TabLayout tabLayout) { + @ColorInt int finalMainColor = getSecondaryForegroundColorDependingOnTheme(tabLayout.getContext(), mainColor); + tabLayout.setBackgroundColor(ContextCompat.getColor(tabLayout.getContext(), R.color.primary)); + final boolean contrastRatioIsSufficient = getContrastRatio(mainColor, ContextCompat.getColor(tabLayout.getContext(), R.color.primary)) > 1.7d; + tabLayout.setSelectedTabIndicatorColor(contrastRatioIsSufficient ? mainColor : finalMainColor); + } + + public static void applyBrandToEditText(@ColorInt int mainColor, @NonNull EditText editText) { + @ColorInt final int finalMainColor = getSecondaryForegroundColorDependingOnTheme(editText.getContext(), mainColor); + DrawableCompat.setTintList(editText.getBackground(), new ColorStateList( + new int[][]{ + new int[]{android.R.attr.state_active}, + new int[]{android.R.attr.state_activated}, + new int[]{android.R.attr.state_focused}, + new int[]{android.R.attr.state_pressed}, + new int[]{} + }, + new int[]{ + finalMainColor, + finalMainColor, + finalMainColor, + finalMainColor, + editText.getContext().getResources().getColor(R.color.fg_secondary) + } + )); + } + + public static void tintMenuIcon(@NonNull MenuItem menuItem, @ColorInt int color) { + Drawable drawable = menuItem.getIcon(); + if (drawable != null) { + drawable = DrawableCompat.wrap(drawable); + DrawableCompat.setTint(drawable, color); + menuItem.setIcon(drawable); + } + } +} diff --git a/app/src/main/java/it/niedermann/nextcloud/deck/ui/card/CardAdapter.java b/app/src/main/java/it/niedermann/nextcloud/deck/ui/card/CardAdapter.java index b618c16a9..986a66cad 100644 --- a/app/src/main/java/it/niedermann/nextcloud/deck/ui/card/CardAdapter.java +++ b/app/src/main/java/it/niedermann/nextcloud/deck/ui/card/CardAdapter.java @@ -44,13 +44,13 @@ import it.niedermann.nextcloud.deck.persistence.sync.SyncManager; import it.niedermann.nextcloud.deck.persistence.sync.adapters.db.util.LiveDataHelper; import it.niedermann.nextcloud.deck.persistence.sync.adapters.db.util.WrappedLiveData; import it.niedermann.nextcloud.deck.ui.branding.Branded; -import it.niedermann.nextcloud.deck.ui.branding.BrandedActivity; import it.niedermann.nextcloud.deck.ui.branding.BrandedAlertDialogBuilder; import it.niedermann.nextcloud.deck.ui.exception.ExceptionDialogFragment; import it.niedermann.nextcloud.deck.util.DateUtil; import it.niedermann.nextcloud.deck.util.ViewUtil; import static it.niedermann.nextcloud.deck.persistence.sync.adapters.db.util.LiveDataHelper.observeOnce; +import static it.niedermann.nextcloud.deck.ui.branding.BrandingUtil.getSecondaryForegroundColorDependingOnTheme; import static it.niedermann.nextcloud.deck.util.MimeTypeUtil.TEXT_PLAIN; public class CardAdapter extends RecyclerView.Adapter<ItemCardViewHolder> implements DragAndDropAdapter<FullCard>, Branded { @@ -346,8 +346,8 @@ public class CardAdapter extends RecyclerView.Adapter<ItemCardViewHolder> implem } @Override - public void applyBrand(int mainColor, int textColor) { - this.mainColor = BrandedActivity.getSecondaryForegroundColorDependingOnTheme(context, mainColor); + public void applyBrand(int mainColor) { + this.mainColor = getSecondaryForegroundColorDependingOnTheme(context, mainColor); notifyDataSetChanged(); } } diff --git a/app/src/main/java/it/niedermann/nextcloud/deck/ui/card/EditActivity.java b/app/src/main/java/it/niedermann/nextcloud/deck/ui/card/EditActivity.java index c039b2952..0710b7d7c 100644 --- a/app/src/main/java/it/niedermann/nextcloud/deck/ui/card/EditActivity.java +++ b/app/src/main/java/it/niedermann/nextcloud/deck/ui/card/EditActivity.java @@ -2,7 +2,7 @@ package it.niedermann.nextcloud.deck.ui.card; import android.content.Context; import android.content.Intent; -import android.graphics.Color; +import android.graphics.drawable.Drawable; import android.os.Bundle; import android.text.Editable; import android.text.InputFilter; @@ -15,11 +15,13 @@ import android.view.WindowManager; import androidx.annotation.NonNull; import androidx.appcompat.app.AppCompatActivity; +import androidx.core.graphics.drawable.DrawableCompat; import androidx.lifecycle.ViewModelProvider; import com.google.android.material.tabs.TabLayout; import com.google.android.material.tabs.TabLayoutMediator; +import it.niedermann.nextcloud.deck.DeckLog; import it.niedermann.nextcloud.deck.R; import it.niedermann.nextcloud.deck.databinding.ActivityEditBinding; import it.niedermann.nextcloud.deck.model.Account; @@ -31,6 +33,8 @@ import it.niedermann.nextcloud.deck.util.CardUtil; import static android.graphics.Color.parseColor; import static it.niedermann.nextcloud.deck.persistence.sync.adapters.db.util.LiveDataHelper.observeOnce; +import static it.niedermann.nextcloud.deck.ui.branding.BrandingUtil.applyBrandToPrimaryTabLayout; +import static it.niedermann.nextcloud.deck.ui.branding.BrandingUtil.isBrandingEnabled; public class EditActivity extends BrandedActivity { private static final String BUNDLE_KEY_ACCOUNT = "account"; @@ -87,9 +91,9 @@ public class EditActivity extends BrandedActivity { @Override protected void onNewIntent(Intent intent) { super.onNewIntent(intent); + applyBrand(colorAccent); setIntent(intent); loadDataFromIntent(); - applyBrand(parseColor(viewModel.getAccount().getColor()), parseColor(viewModel.getAccount().getTextColor())); } private void loadDataFromIntent() { @@ -117,6 +121,7 @@ public class EditActivity extends BrandedActivity { final long boardId = args.getLong(BUNDLE_KEY_BOARD_ID); observeOnce(syncManager.getFullBoardById(account.getId(), boardId), EditActivity.this, (fullBoard -> { + applyBrand(parseColor('#' + fullBoard.getBoard().getColor())); viewModel.setCanEdit(fullBoard.getBoard().isPermissionEdit()); invalidateOptionsMenu(); if (viewModel.isCreateMode()) { @@ -290,12 +295,16 @@ public class EditActivity extends BrandedActivity { } @Override - public void applyBrand(int mainColor, int textColor) { - applyBrandToPrimaryToolbar(mainColor, textColor, binding.toolbar); - applyBrandToPrimaryTabLayout(mainColor, textColor, binding.tabLayout); - final int highlightColor = Color.argb(77, Color.red(textColor), Color.green(textColor), Color.blue(textColor)); - binding.title.setHighlightColor(highlightColor); - binding.title.setTextColor(textColor); + public void applyBrand(int mainColor) { + if(isBrandingEnabled(this)) { + final Drawable navigationIcon = binding.toolbar.getNavigationIcon(); + if (navigationIcon == null) { + DeckLog.error("Excpected navigationIcon to be present."); + } else { + DrawableCompat.setTint(binding.toolbar.getNavigationIcon(), colorAccent); + } + applyBrandToPrimaryTabLayout(mainColor, binding.tabLayout); + } } @NonNull diff --git a/app/src/main/java/it/niedermann/nextcloud/deck/ui/card/UserAutoCompleteAdapter.java b/app/src/main/java/it/niedermann/nextcloud/deck/ui/card/UserAutoCompleteAdapter.java index 0880b343b..d2fd4d40d 100644 --- a/app/src/main/java/it/niedermann/nextcloud/deck/ui/card/UserAutoCompleteAdapter.java +++ b/app/src/main/java/it/niedermann/nextcloud/deck/ui/card/UserAutoCompleteAdapter.java @@ -64,7 +64,7 @@ public class UserAutoCompleteAdapter extends AutoCompleteAdapter<User> { : syncManager.findProposalsForUsersToAssignForACL(accountId, boardId, activity.getResources().getInteger(R.integer.max_users_suggested)); } else { liveData = constraintLength > 0 - ? syncManager.searchUserByUidOrDisplayName(accountId, cardId, constraint.toString()) + ? syncManager.searchUserByUidOrDisplayName(accountId, boardId, cardId, constraint.toString()) : syncManager.findProposalsForUsersToAssign(accountId, boardId, cardId, activity.getResources().getInteger(R.integer.max_users_suggested)); } observeOnce(liveData, activity, users -> { diff --git a/app/src/main/java/it/niedermann/nextcloud/deck/ui/card/attachments/CardAttachmentAdapter.java b/app/src/main/java/it/niedermann/nextcloud/deck/ui/card/attachments/CardAttachmentAdapter.java index 3ecee8583..a72ed8ef2 100644 --- a/app/src/main/java/it/niedermann/nextcloud/deck/ui/card/attachments/CardAttachmentAdapter.java +++ b/app/src/main/java/it/niedermann/nextcloud/deck/ui/card/attachments/CardAttachmentAdapter.java @@ -35,8 +35,6 @@ import it.niedermann.nextcloud.deck.util.DateUtil; import it.niedermann.nextcloud.deck.util.MimeTypeUtil; import static androidx.recyclerview.widget.RecyclerView.NO_ID; -import static it.niedermann.nextcloud.deck.Application.readBrandMainColor; -import static it.niedermann.nextcloud.deck.ui.branding.BrandedActivity.getSecondaryForegroundColorDependingOnTheme; import static it.niedermann.nextcloud.deck.util.ClipboardUtil.copyToClipboard; @SuppressWarnings("WeakerAccess") @@ -73,7 +71,6 @@ public class CardAttachmentAdapter extends RecyclerView.Adapter<AttachmentViewHo this.attachmentClickedListener = attachmentClickedListener; this.account = account; this.cardLocalId = cardLocalId == null ? NO_ID : cardLocalId; - this.mainColor = getSecondaryForegroundColorDependingOnTheme(context, readBrandMainColor(context)); setHasStableIds(true); } diff --git a/app/src/main/java/it/niedermann/nextcloud/deck/ui/card/attachments/CardAttachmentsFragment.java b/app/src/main/java/it/niedermann/nextcloud/deck/ui/card/attachments/CardAttachmentsFragment.java index cab648c6b..95cbb3d5e 100644 --- a/app/src/main/java/it/niedermann/nextcloud/deck/ui/card/attachments/CardAttachmentsFragment.java +++ b/app/src/main/java/it/niedermann/nextcloud/deck/ui/card/attachments/CardAttachmentsFragment.java @@ -56,7 +56,7 @@ import it.niedermann.nextcloud.deck.ui.exception.ExceptionDialogFragment; import static android.app.Activity.RESULT_OK; import static androidx.core.content.PermissionChecker.checkSelfPermission; import static it.niedermann.nextcloud.deck.persistence.sync.adapters.db.util.LiveDataHelper.observeOnce; -import static it.niedermann.nextcloud.deck.ui.branding.BrandedActivity.applyBrandToFAB; +import static it.niedermann.nextcloud.deck.ui.branding.BrandingUtil.applyBrandToFAB; import static it.niedermann.nextcloud.deck.ui.card.attachments.CardAttachmentAdapter.VIEW_TYPE_DEFAULT; import static it.niedermann.nextcloud.deck.ui.card.attachments.CardAttachmentAdapter.VIEW_TYPE_IMAGE; import static it.niedermann.nextcloud.deck.util.AttachmentUtil.copyContentUriToTempFile; @@ -364,8 +364,8 @@ public class CardAttachmentsFragment extends BrandedFragment implements Attachme } @Override - public void applyBrand(int mainColor, int textColor) { - applyBrandToFAB(mainColor, textColor, binding.fab); + public void applyBrand(int mainColor) { + applyBrandToFAB(mainColor, binding.fab); } public static Fragment newInstance() { diff --git a/app/src/main/java/it/niedermann/nextcloud/deck/ui/card/comments/CardCommentsAdapter.java b/app/src/main/java/it/niedermann/nextcloud/deck/ui/card/comments/CardCommentsAdapter.java index 5e9324a3c..169a37c79 100644 --- a/app/src/main/java/it/niedermann/nextcloud/deck/ui/card/comments/CardCommentsAdapter.java +++ b/app/src/main/java/it/niedermann/nextcloud/deck/ui/card/comments/CardCommentsAdapter.java @@ -16,8 +16,8 @@ import it.niedermann.nextcloud.deck.databinding.ItemCommentBinding; import it.niedermann.nextcloud.deck.model.Account; import it.niedermann.nextcloud.deck.model.ocs.comment.full.FullDeckComment; -import static it.niedermann.nextcloud.deck.Application.readBrandMainColor; -import static it.niedermann.nextcloud.deck.ui.branding.BrandedActivity.getSecondaryForegroundColorDependingOnTheme; +import static it.niedermann.nextcloud.deck.ui.branding.BrandingUtil.getSecondaryForegroundColorDependingOnTheme; +import static it.niedermann.nextcloud.deck.ui.branding.BrandingUtil.readBrandMainColor; public class CardCommentsAdapter extends RecyclerView.Adapter<ItemCommentViewHolder> { diff --git a/app/src/main/java/it/niedermann/nextcloud/deck/ui/card/comments/CardCommentsEditDialogFragment.java b/app/src/main/java/it/niedermann/nextcloud/deck/ui/card/comments/CardCommentsEditDialogFragment.java index f43ccb5a0..692f8eb8f 100644 --- a/app/src/main/java/it/niedermann/nextcloud/deck/ui/card/comments/CardCommentsEditDialogFragment.java +++ b/app/src/main/java/it/niedermann/nextcloud/deck/ui/card/comments/CardCommentsEditDialogFragment.java @@ -16,10 +16,11 @@ import java.util.Objects; import it.niedermann.nextcloud.deck.R; import it.niedermann.nextcloud.deck.databinding.DialogAddCommentBinding; -import it.niedermann.nextcloud.deck.ui.branding.BrandedActivity; import it.niedermann.nextcloud.deck.ui.branding.BrandedAlertDialogBuilder; import it.niedermann.nextcloud.deck.ui.branding.BrandedDialogFragment; +import static it.niedermann.nextcloud.deck.ui.branding.BrandingUtil.applyBrandToEditText; + public class CardCommentsEditDialogFragment extends BrandedDialogFragment { private static final String BUNDLE_KEY_COMMENT_ID = "commentId"; private static final String BUNDLE_KEY_COMMENT_MESSAGE = "commentMessage"; @@ -78,8 +79,8 @@ public class CardCommentsEditDialogFragment extends BrandedDialogFragment { } @Override - public void applyBrand(int mainColor, int textColor) { - BrandedActivity.applyBrandToEditText(mainColor, textColor, binding.input); + public void applyBrand(int mainColor) { + applyBrandToEditText(mainColor, binding.input); } } diff --git a/app/src/main/java/it/niedermann/nextcloud/deck/ui/card/comments/CardCommentsFragment.java b/app/src/main/java/it/niedermann/nextcloud/deck/ui/card/comments/CardCommentsFragment.java index 302173544..e261c37a2 100644 --- a/app/src/main/java/it/niedermann/nextcloud/deck/ui/card/comments/CardCommentsFragment.java +++ b/app/src/main/java/it/niedermann/nextcloud/deck/ui/card/comments/CardCommentsFragment.java @@ -29,8 +29,8 @@ import it.niedermann.nextcloud.deck.ui.card.EditCardViewModel; import static android.view.View.GONE; import static android.view.View.VISIBLE; -import static it.niedermann.nextcloud.deck.ui.branding.BrandedActivity.applyBrandToEditText; -import static it.niedermann.nextcloud.deck.ui.branding.BrandedActivity.applyBrandToFAB; +import static it.niedermann.nextcloud.deck.ui.branding.BrandingUtil.applyBrandToEditText; +import static it.niedermann.nextcloud.deck.ui.branding.BrandingUtil.applyBrandToFAB; import static it.niedermann.nextcloud.deck.util.ViewUtil.setupMentions; public class CardCommentsFragment extends BrandedFragment implements CommentEditedListener, CommentDeletedListener, CommentSelectAsReplyListener { @@ -142,9 +142,9 @@ public class CardCommentsFragment extends BrandedFragment implements CommentEdit } @Override - public void applyBrand(int mainColor, int textColor) { - applyBrandToEditText(mainColor, textColor, binding.message); - applyBrandToFAB(mainColor, textColor, binding.fab); + public void applyBrand(int mainColor) { + applyBrandToEditText(mainColor, binding.message); + applyBrandToFAB(mainColor, binding.fab); } @Override diff --git a/app/src/main/java/it/niedermann/nextcloud/deck/ui/card/details/CardDetailsFragment.java b/app/src/main/java/it/niedermann/nextcloud/deck/ui/card/details/CardDetailsFragment.java index a2a01b618..3182fffa2 100644 --- a/app/src/main/java/it/niedermann/nextcloud/deck/ui/card/details/CardDetailsFragment.java +++ b/app/src/main/java/it/niedermann/nextcloud/deck/ui/card/details/CardDetailsFragment.java @@ -58,7 +58,7 @@ import it.niedermann.nextcloud.deck.util.ViewUtil; import static android.text.format.DateFormat.getDateFormat; import static it.niedermann.nextcloud.deck.persistence.sync.adapters.db.util.LiveDataHelper.observeOnce; -import static it.niedermann.nextcloud.deck.ui.branding.BrandedActivity.applyBrandToEditText; +import static it.niedermann.nextcloud.deck.ui.branding.BrandingUtil.applyBrandToEditText; import static it.niedermann.nextcloud.deck.util.DimensionUtil.dpToPx; public class CardDetailsFragment extends BrandedFragment implements OnDateSetListener, OnTimeSetListener { @@ -136,12 +136,12 @@ public class CardDetailsFragment extends BrandedFragment implements OnDateSetLis } @Override - public void applyBrand(int mainColor, int textColor) { - applyBrandToEditText(mainColor, textColor, binding.labels); - applyBrandToEditText(mainColor, textColor, binding.dueDateDate); - applyBrandToEditText(mainColor, textColor, binding.dueDateTime); - applyBrandToEditText(mainColor, textColor, binding.people); - applyBrandToEditText(mainColor, textColor, binding.description); + public void applyBrand(int mainColor) { + applyBrandToEditText(mainColor, binding.labels); + applyBrandToEditText(mainColor, binding.dueDateDate); + applyBrandToEditText(mainColor, binding.dueDateTime); + applyBrandToEditText(mainColor, binding.people); + applyBrandToEditText(mainColor, binding.description); } private void setupDescription() { diff --git a/app/src/main/java/it/niedermann/nextcloud/deck/ui/exception/ExceptionActivity.java b/app/src/main/java/it/niedermann/nextcloud/deck/ui/exception/ExceptionActivity.java index 3ba3eb59f..9eef878c3 100644 --- a/app/src/main/java/it/niedermann/nextcloud/deck/ui/exception/ExceptionActivity.java +++ b/app/src/main/java/it/niedermann/nextcloud/deck/ui/exception/ExceptionActivity.java @@ -11,6 +11,7 @@ import androidx.appcompat.app.AppCompatActivity; import it.niedermann.nextcloud.deck.DeckLog; import it.niedermann.nextcloud.deck.R; import it.niedermann.nextcloud.deck.databinding.ActivityExceptionBinding; +import it.niedermann.nextcloud.deck.ui.exception.tips.TipsAdapter; import it.niedermann.nextcloud.deck.util.ExceptionUtil; import static it.niedermann.nextcloud.deck.util.ClipboardUtil.copyToClipboard; @@ -41,6 +42,11 @@ public class ExceptionActivity extends AppCompatActivity { binding.stacktrace.setText(debugInfo); + final TipsAdapter adapter = new TipsAdapter(this::startActivity); + binding.tips.setAdapter(adapter); + binding.tips.setNestedScrollingEnabled(false); + adapter.setThrowable(this, null, throwable); + binding.copy.setOnClickListener((v) -> copyToClipboard(this, getString(R.string.simple_exception), "```\n" + debugInfo + "\n```")); binding.close.setOnClickListener((v) -> finish()); } diff --git a/app/src/main/java/it/niedermann/nextcloud/deck/ui/exception/ExceptionDialogFragment.java b/app/src/main/java/it/niedermann/nextcloud/deck/ui/exception/ExceptionDialogFragment.java index 9ab9b70c8..6c0d0ba79 100644 --- a/app/src/main/java/it/niedermann/nextcloud/deck/ui/exception/ExceptionDialogFragment.java +++ b/app/src/main/java/it/niedermann/nextcloud/deck/ui/exception/ExceptionDialogFragment.java @@ -2,8 +2,6 @@ package it.niedermann.nextcloud.deck.ui.exception; import android.app.Dialog; import android.content.Context; -import android.content.Intent; -import android.net.Uri; import android.os.Bundle; import android.view.View; @@ -13,27 +11,13 @@ import androidx.appcompat.app.AlertDialog; import androidx.appcompat.app.AppCompatDialogFragment; import androidx.fragment.app.DialogFragment; -import com.nextcloud.android.sso.exceptions.NextcloudApiNotRespondingException; -import com.nextcloud.android.sso.exceptions.NextcloudFilesAppNotSupportedException; -import com.nextcloud.android.sso.exceptions.NextcloudHttpRequestFailedException; -import com.nextcloud.android.sso.exceptions.TokenMismatchException; - -import org.json.JSONException; - -import java.net.ConnectException; -import java.net.SocketTimeoutException; - -import it.niedermann.nextcloud.deck.BuildConfig; import it.niedermann.nextcloud.deck.DeckLog; import it.niedermann.nextcloud.deck.R; import it.niedermann.nextcloud.deck.databinding.DialogExceptionBinding; -import it.niedermann.nextcloud.deck.exceptions.DeckException; -import it.niedermann.nextcloud.deck.exceptions.UploadAttachmentFailedException; import it.niedermann.nextcloud.deck.model.Account; import it.niedermann.nextcloud.deck.ui.exception.tips.TipsAdapter; import it.niedermann.nextcloud.deck.util.ExceptionUtil; -import static android.provider.Settings.ACTION_APPLICATION_DETAILS_SETTINGS; import static it.niedermann.nextcloud.deck.util.ClipboardUtil.copyToClipboard; public class ExceptionDialogFragment extends AppCompatDialogFragment { @@ -75,69 +59,7 @@ public class ExceptionDialogFragment extends AppCompatDialogFragment { DeckLog.logError(throwable); - if (throwable instanceof TokenMismatchException) { - adapter.add(R.string.error_dialog_tip_token_mismatch_retry); - adapter.add(R.string.error_dialog_tip_token_mismatch_clear_storage); - Intent intent = new Intent(ACTION_APPLICATION_DETAILS_SETTINGS) - .setData(Uri.parse("package:" + BuildConfig.APPLICATION_ID)) - .putExtra(INTENT_EXTRA_BUTTON_TEXT, R.string.error_action_open_deck_info); - adapter.add(R.string.error_dialog_tip_clear_storage, intent); - } else if (throwable instanceof NextcloudFilesAppNotSupportedException) { - adapter.add(R.string.error_dialog_tip_files_outdated); - } else if (throwable instanceof NextcloudApiNotRespondingException) { - adapter.add(R.string.error_dialog_tip_files_force_stop); - adapter.add(R.string.error_dialog_tip_files_delete_storage); - } else if (throwable instanceof SocketTimeoutException || throwable instanceof ConnectException) { - adapter.add(R.string.error_dialog_timeout_instance); - adapter.add(R.string.error_dialog_timeout_toggle, new Intent(android.provider.Settings.ACTION_WIFI_SETTINGS).putExtra(INTENT_EXTRA_BUTTON_TEXT, R.string.error_action_open_network)); - } else if (throwable instanceof JSONException || throwable instanceof NullPointerException) { - adapter.add(R.string.error_dialog_check_server); - } else if (throwable instanceof NextcloudHttpRequestFailedException) { - int statusCode = ((NextcloudHttpRequestFailedException) throwable).getStatusCode(); - switch (statusCode) { - case 302: - adapter.add(R.string.error_dialog_redirect); - break; - case 500: - if (account != null) { - adapter.add(R.string.error_dialog_check_server_logs, new Intent(Intent.ACTION_VIEW) - .putExtra(INTENT_EXTRA_BUTTON_TEXT, R.string.error_action_server_logs) - .setData(Uri.parse(account.getUrl() + getString(R.string.url_fragment_server_logs)))); - } else { - adapter.add(R.string.error_dialog_check_server_logs); - } - break; - case 503: - adapter.add(R.string.error_dialog_check_maintenance); - break; - case 507: - adapter.add(R.string.error_dialog_insufficient_storage); - break; - } - } else if (throwable instanceof UploadAttachmentFailedException) { - adapter.add(R.string.error_dialog_attachment_upload_failed); - } else if (throwable instanceof DeckException) { - switch (((DeckException) throwable).getHint()) { - case CAPABILITIES_VERSION_NOT_PARSABLE: - if (account != null) { - adapter.add(R.string.error_dialog_version_not_parsable, new Intent(Intent.ACTION_VIEW) - .putExtra(INTENT_EXTRA_BUTTON_TEXT, R.string.error_action_install) - .setData(Uri.parse(account.getUrl() + getString(R.string.url_fragment_install_deck)))); - } else { - adapter.add(R.string.error_dialog_version_not_parsable); - } - break; - case CAPABILITIES_NOT_PARSABLE: - default: - if (account != null) { - adapter.add(R.string.error_dialog_capabilities_not_parsable, new Intent(Intent.ACTION_VIEW) - .putExtra(INTENT_EXTRA_BUTTON_TEXT, R.string.error_action_server_logs) - .setData(Uri.parse(account.getUrl() + getString(R.string.url_fragment_server_logs)))); - } else { - adapter.add(R.string.error_dialog_capabilities_not_parsable); - } - } - } + adapter.setThrowable(requireContext(), account, throwable); return new AlertDialog.Builder(requireActivity()) .setView(binding.getRoot()) diff --git a/app/src/main/java/it/niedermann/nextcloud/deck/ui/exception/tips/TipsAdapter.java b/app/src/main/java/it/niedermann/nextcloud/deck/ui/exception/tips/TipsAdapter.java index 8ca1a4e09..a059b2956 100644 --- a/app/src/main/java/it/niedermann/nextcloud/deck/ui/exception/tips/TipsAdapter.java +++ b/app/src/main/java/it/niedermann/nextcloud/deck/ui/exception/tips/TipsAdapter.java @@ -1,6 +1,11 @@ package it.niedermann.nextcloud.deck.ui.exception.tips; +import android.content.Context; import android.content.Intent; +import android.net.Uri; +import android.os.Build.VERSION; +import android.os.Build.VERSION_CODES; +import android.provider.Settings; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; @@ -11,10 +16,26 @@ import androidx.annotation.StringRes; import androidx.core.util.Consumer; import androidx.recyclerview.widget.RecyclerView; +import com.nextcloud.android.sso.exceptions.NextcloudApiNotRespondingException; +import com.nextcloud.android.sso.exceptions.NextcloudFilesAppNotSupportedException; +import com.nextcloud.android.sso.exceptions.NextcloudHttpRequestFailedException; +import com.nextcloud.android.sso.exceptions.TokenMismatchException; + +import org.json.JSONException; + +import java.net.ConnectException; +import java.net.SocketTimeoutException; import java.util.LinkedList; import java.util.List; +import it.niedermann.nextcloud.deck.BuildConfig; import it.niedermann.nextcloud.deck.R; +import it.niedermann.nextcloud.deck.exceptions.DeckException; +import it.niedermann.nextcloud.deck.exceptions.UploadAttachmentFailedException; +import it.niedermann.nextcloud.deck.model.Account; + +import static android.provider.Settings.ACTION_APPLICATION_DETAILS_SETTINGS; +import static it.niedermann.nextcloud.deck.ui.exception.ExceptionDialogFragment.INTENT_EXTRA_BUTTON_TEXT; public class TipsAdapter extends RecyclerView.Adapter<TipsViewHolder> { @@ -44,6 +65,87 @@ public class TipsAdapter extends RecyclerView.Adapter<TipsViewHolder> { return tips.size(); } + public void setThrowable(@NonNull Context context, @Nullable Account account, @NonNull Throwable throwable) { + if (throwable instanceof TokenMismatchException) { + add(R.string.error_dialog_tip_token_mismatch_retry); + add(R.string.error_dialog_tip_token_mismatch_clear_storage); + Intent intent = new Intent(ACTION_APPLICATION_DETAILS_SETTINGS) + .setData(Uri.parse("package:" + BuildConfig.APPLICATION_ID)) + .putExtra(INTENT_EXTRA_BUTTON_TEXT, R.string.error_action_open_deck_info); + add(R.string.error_dialog_tip_clear_storage, intent); + } else if (throwable instanceof NextcloudFilesAppNotSupportedException) { + add(R.string.error_dialog_tip_files_outdated); + } else if (throwable instanceof NextcloudApiNotRespondingException) { + if (VERSION.SDK_INT >= VERSION_CODES.M) { + add(R.string.error_dialog_tip_disable_battery_optimizations, new Intent().setAction(Settings.ACTION_IGNORE_BATTERY_OPTIMIZATION_SETTINGS).putExtra(INTENT_EXTRA_BUTTON_TEXT, R.string.error_action_open_battery_settings)); + } else { + add(R.string.error_dialog_tip_disable_battery_optimizations); + } + add(R.string.error_dialog_tip_files_force_stop); + add(R.string.error_dialog_tip_files_delete_storage); + } else if (throwable instanceof SocketTimeoutException || throwable instanceof ConnectException) { + add(R.string.error_dialog_timeout_instance); + add(R.string.error_dialog_timeout_toggle, new Intent(Settings.ACTION_WIFI_SETTINGS).putExtra(INTENT_EXTRA_BUTTON_TEXT, R.string.error_action_open_network)); + } else if (throwable instanceof JSONException || throwable instanceof NullPointerException) { + add(R.string.error_dialog_check_server); + } else if (throwable instanceof NextcloudHttpRequestFailedException) { + int statusCode = ((NextcloudHttpRequestFailedException) throwable).getStatusCode(); + switch (statusCode) { + case 302: + add(R.string.error_dialog_redirect); + break; + case 500: + if (account != null) { + add(R.string.error_dialog_check_server_logs, new Intent(Intent.ACTION_VIEW) + .putExtra(INTENT_EXTRA_BUTTON_TEXT, R.string.error_action_server_logs) + .setData(Uri.parse(account.getUrl() + context.getString(R.string.url_fragment_server_logs)))); + } else { + add(R.string.error_dialog_check_server_logs); + } + break; + case 503: + add(R.string.error_dialog_check_maintenance); + break; + case 507: + add(R.string.error_dialog_insufficient_storage); + break; + } + } else if (throwable instanceof UploadAttachmentFailedException) { + add(R.string.error_dialog_attachment_upload_failed); + } else if (throwable instanceof DeckException) { + switch (((DeckException) throwable).getHint()) { + case CAPABILITIES_VERSION_NOT_PARSABLE: + if (account != null) { + add(R.string.error_dialog_version_not_parsable, new Intent(Intent.ACTION_VIEW) + .putExtra(INTENT_EXTRA_BUTTON_TEXT, R.string.error_action_install) + .setData(Uri.parse(account.getUrl() + context.getString(R.string.url_fragment_install_deck)))); + } else { + add(R.string.error_dialog_version_not_parsable); + } + break; + case CAPABILITIES_NOT_PARSABLE: + default: + if (account != null) { + add(R.string.error_dialog_capabilities_not_parsable, new Intent(Intent.ACTION_VIEW) + .putExtra(INTENT_EXTRA_BUTTON_TEXT, R.string.error_action_server_logs) + .setData(Uri.parse(account.getUrl() + context.getString(R.string.url_fragment_server_logs)))); + } else { + add(R.string.error_dialog_capabilities_not_parsable); + } + } + } else if (throwable instanceof RuntimeException) { + if (throwable.getMessage() != null && throwable.getMessage().contains("database")) { + Intent reportIntent = new Intent(Intent.ACTION_VIEW, Uri.parse(context.getString(R.string.url_report_bug))) + .putExtra(INTENT_EXTRA_BUTTON_TEXT, R.string.error_action_report_issue); + add(R.string.error_dialog_tip_database_upgrade_failed, reportIntent); + Intent clearIntent = new Intent(ACTION_APPLICATION_DETAILS_SETTINGS) + .setData(Uri.parse("package:" + BuildConfig.APPLICATION_ID)) + .putExtra(INTENT_EXTRA_BUTTON_TEXT, R.string.error_action_open_deck_info); + add(R.string.error_dialog_tip_clear_storage, clearIntent); + } + } + } + public void add(@StringRes int text) { add(text, null); } diff --git a/app/src/main/java/it/niedermann/nextcloud/deck/ui/filter/FilterDialogFragment.java b/app/src/main/java/it/niedermann/nextcloud/deck/ui/filter/FilterDialogFragment.java index 12dd38eac..aa6f59d04 100644 --- a/app/src/main/java/it/niedermann/nextcloud/deck/ui/filter/FilterDialogFragment.java +++ b/app/src/main/java/it/niedermann/nextcloud/deck/ui/filter/FilterDialogFragment.java @@ -23,10 +23,11 @@ import it.niedermann.nextcloud.deck.R; import it.niedermann.nextcloud.deck.databinding.DialogFilterBinding; import it.niedermann.nextcloud.deck.model.enums.EDueType; import it.niedermann.nextcloud.deck.model.internal.FilterInformation; -import it.niedermann.nextcloud.deck.ui.branding.BrandedActivity; import it.niedermann.nextcloud.deck.ui.branding.BrandedAlertDialogBuilder; import it.niedermann.nextcloud.deck.ui.branding.BrandedDialogFragment; +import static it.niedermann.nextcloud.deck.ui.branding.BrandingUtil.getSecondaryForegroundColorDependingOnTheme; + public class FilterDialogFragment extends BrandedDialogFragment { private DialogFilterBinding binding; @@ -101,8 +102,8 @@ public class FilterDialogFragment extends BrandedDialogFragment { } @Override - public void applyBrand(int mainColor, int textColor) { - @ColorInt int finalMainColor = BrandedActivity.getSecondaryForegroundColorDependingOnTheme(requireContext(), mainColor); + public void applyBrand(int mainColor) { + @ColorInt int finalMainColor = getSecondaryForegroundColorDependingOnTheme(requireContext(), mainColor); binding.tabLayout.setSelectedTabIndicatorColor(finalMainColor); indicator.setColorFilter(finalMainColor, PorterDuff.Mode.SRC_ATOP); } diff --git a/app/src/main/java/it/niedermann/nextcloud/deck/ui/manageaccounts/ManageAccountViewHolder.java b/app/src/main/java/it/niedermann/nextcloud/deck/ui/manageaccounts/ManageAccountViewHolder.java index 231e3f124..4b43cbed6 100644 --- a/app/src/main/java/it/niedermann/nextcloud/deck/ui/manageaccounts/ManageAccountViewHolder.java +++ b/app/src/main/java/it/niedermann/nextcloud/deck/ui/manageaccounts/ManageAccountViewHolder.java @@ -34,7 +34,8 @@ public class ManageAccountViewHolder extends RecyclerView.ViewHolder { binding.accountHost.setText(Uri.parse(account.getUrl()).getHost()); Glide.with(itemView.getContext()) .load(new SingleSignOnUrl(account.getName(), account.getAvatarUrl(dpToPx(binding.accountItemAvatar.getContext(), R.dimen.avatar_size)))) - .error(R.drawable.ic_person_grey600_24dp) + .placeholder(R.drawable.ic_baseline_account_circle_24) + .error(R.drawable.ic_baseline_account_circle_24) .apply(RequestOptions.circleCropTransform()) .into(binding.accountItemAvatar); binding.currentAccountIndicator.setSelected(isCurrentAccount); diff --git a/app/src/main/java/it/niedermann/nextcloud/deck/ui/manageaccounts/ManageAccountsActivity.java b/app/src/main/java/it/niedermann/nextcloud/deck/ui/manageaccounts/ManageAccountsActivity.java index ed62e85cb..9d273cdcb 100644 --- a/app/src/main/java/it/niedermann/nextcloud/deck/ui/manageaccounts/ManageAccountsActivity.java +++ b/app/src/main/java/it/niedermann/nextcloud/deck/ui/manageaccounts/ManageAccountsActivity.java @@ -1,6 +1,5 @@ package it.niedermann.nextcloud.deck.ui.manageaccounts; -import android.graphics.Color; import android.os.Bundle; import android.util.Log; @@ -9,15 +8,15 @@ import androidx.appcompat.app.AppCompatActivity; import com.nextcloud.android.sso.helper.SingleAccountHelper; -import it.niedermann.nextcloud.deck.Application; import it.niedermann.nextcloud.deck.databinding.ActivityManageAccountsBinding; import it.niedermann.nextcloud.deck.model.Account; import it.niedermann.nextcloud.deck.persistence.sync.SyncManager; -import it.niedermann.nextcloud.deck.ui.branding.BrandedActivity; +import static it.niedermann.nextcloud.deck.DeckApplication.readCurrentAccountId; +import static it.niedermann.nextcloud.deck.DeckApplication.saveCurrentAccountId; import static it.niedermann.nextcloud.deck.persistence.sync.adapters.db.util.LiveDataHelper.observeOnce; -public class ManageAccountsActivity extends BrandedActivity { +public class ManageAccountsActivity extends AppCompatActivity { private static final String TAG = ManageAccountsActivity.class.getSimpleName(); @@ -39,8 +38,7 @@ public class ManageAccountsActivity extends BrandedActivity { adapter = new ManageAccountAdapter((account) -> { SingleAccountHelper.setCurrentAccount(getApplicationContext(), account.getName()); syncManager = new SyncManager(this); - Application.saveBrandColors(this, Color.parseColor(account.getColor()), Color.parseColor(account.getTextColor())); - Application.saveCurrentAccountId(this, account.getId()); + saveCurrentAccountId(this, account.getId()); }, (accountPair) -> { if (accountPair.first != null) { syncManager.deleteAccount(accountPair.first.getId()); @@ -50,8 +48,7 @@ public class ManageAccountsActivity extends BrandedActivity { Account newAccount = accountPair.second; if (newAccount != null) { SingleAccountHelper.setCurrentAccount(getApplicationContext(), newAccount.getName()); - Application.saveBrandColors(this, Color.parseColor(newAccount.getColor()), Color.parseColor(newAccount.getTextColor())); - Application.saveCurrentAccountId(this, newAccount.getId()); + saveCurrentAccountId(this, newAccount.getId()); syncManager = new SyncManager(this); } else { Log.i(TAG, "Got delete account request, but new account is null. Maybe last account has been deleted?"); @@ -59,7 +56,7 @@ public class ManageAccountsActivity extends BrandedActivity { }); binding.accounts.setAdapter(adapter); - observeOnce(syncManager.readAccount(Application.readCurrentAccountId(this)), this, (account -> { + observeOnce(syncManager.readAccount(readCurrentAccountId(this)), this, (account -> { adapter.setCurrentAccount(account); syncManager.readAccounts().observe(this, (localAccounts -> { if (localAccounts.size() == 0) { @@ -77,9 +74,4 @@ public class ManageAccountsActivity extends BrandedActivity { public void onBackPressed() { onSupportNavigateUp(); } - - @Override - public void applyBrand(int mainColor, int textColor) { - applyBrandToPrimaryToolbar(mainColor, textColor, binding.toolbar); - } } diff --git a/app/src/main/java/it/niedermann/nextcloud/deck/ui/preparecreate/PrepareCreateActivity.java b/app/src/main/java/it/niedermann/nextcloud/deck/ui/preparecreate/PrepareCreateActivity.java index b2e9cb012..ab0cc4816 100644 --- a/app/src/main/java/it/niedermann/nextcloud/deck/ui/preparecreate/PrepareCreateActivity.java +++ b/app/src/main/java/it/niedermann/nextcloud/deck/ui/preparecreate/PrepareCreateActivity.java @@ -2,34 +2,51 @@ package it.niedermann.nextcloud.deck.ui.preparecreate; import android.content.ClipData; import android.content.Intent; +import android.content.res.ColorStateList; +import android.graphics.Color; import android.os.Bundle; import android.text.TextUtils; import android.widget.ArrayAdapter; +import androidx.annotation.ColorInt; import androidx.annotation.NonNull; import androidx.annotation.Nullable; +import androidx.appcompat.app.AppCompatActivity; +import androidx.core.content.ContextCompat; +import androidx.core.graphics.drawable.DrawableCompat; import androidx.lifecycle.LiveData; import androidx.lifecycle.Observer; import java.util.List; -import it.niedermann.nextcloud.deck.Application; import it.niedermann.nextcloud.deck.DeckLog; +import it.niedermann.nextcloud.deck.R; import it.niedermann.nextcloud.deck.databinding.ActivityPrepareCreateBinding; import it.niedermann.nextcloud.deck.model.Account; import it.niedermann.nextcloud.deck.model.Board; import it.niedermann.nextcloud.deck.model.full.FullStack; import it.niedermann.nextcloud.deck.persistence.sync.SyncManager; import it.niedermann.nextcloud.deck.ui.ImportAccountActivity; -import it.niedermann.nextcloud.deck.ui.branding.BrandedActivity; +import it.niedermann.nextcloud.deck.ui.branding.Branded; import it.niedermann.nextcloud.deck.ui.card.EditActivity; import it.niedermann.nextcloud.deck.ui.exception.ExceptionDialogFragment; import it.niedermann.nextcloud.deck.ui.exception.ExceptionHandler; +import it.niedermann.nextcloud.deck.util.ColorUtil; import static android.graphics.Color.parseColor; import static androidx.lifecycle.Transformations.switchMap; - -public class PrepareCreateActivity extends BrandedActivity { +import static it.niedermann.nextcloud.deck.DeckApplication.isDarkTheme; +import static it.niedermann.nextcloud.deck.DeckApplication.readCurrentAccountId; +import static it.niedermann.nextcloud.deck.DeckApplication.readCurrentBoardId; +import static it.niedermann.nextcloud.deck.DeckApplication.readCurrentStackId; +import static it.niedermann.nextcloud.deck.DeckApplication.saveCurrentAccountId; +import static it.niedermann.nextcloud.deck.DeckApplication.saveCurrentBoardId; +import static it.niedermann.nextcloud.deck.DeckApplication.saveCurrentStackId; +import static it.niedermann.nextcloud.deck.ui.branding.BrandingUtil.getSecondaryForegroundColorDependingOnTheme; +import static it.niedermann.nextcloud.deck.ui.branding.BrandingUtil.isBrandingEnabled; +import static it.niedermann.nextcloud.deck.util.ColorUtil.contrastRatioIsSufficientBigAreas; + +public class PrepareCreateActivity extends AppCompatActivity implements Branded { private ActivityPrepareCreateBinding binding; @@ -59,10 +76,12 @@ public class PrepareCreateActivity extends BrandedActivity { for (Board board : boards) { if (board.getLocalId() == lastBoardId) { binding.boardSelect.setSelection(boardAdapter.getPosition(board)); + applyBrand(Color.parseColor('#' + board.getColor())); break; } } } else { + applyBrand(ContextCompat.getColor(this, R.color.defaultBrand)); binding.boardSelect.setEnabled(false); binding.submit.setEnabled(false); } @@ -97,7 +116,7 @@ public class PrepareCreateActivity extends BrandedActivity { Thread.setDefaultUncaughtExceptionHandler(new ExceptionHandler(this)); - brandingEnabled = Application.isBrandingEnabled(this); + brandingEnabled = isBrandingEnabled(this); binding = ActivityPrepareCreateBinding.inflate(getLayoutInflater()); setContentView(binding.getRoot()); @@ -127,9 +146,9 @@ public class PrepareCreateActivity extends BrandedActivity { throw new IllegalStateException("hasAccounts() returns true, but readAccounts() returns null or has no entry"); } - lastAccountId = Application.readCurrentAccountId(this); - lastBoardId = Application.readCurrentBoardId(this, lastAccountId); - lastStackId = Application.readCurrentStackId(this, lastAccountId, lastBoardId); + lastAccountId = readCurrentAccountId(this); + lastBoardId = readCurrentBoardId(this, lastAccountId); + lastStackId = readCurrentStackId(this, lastAccountId, lastBoardId); accountAdapter.clear(); accountAdapter.addAll(accounts); @@ -144,12 +163,13 @@ public class PrepareCreateActivity extends BrandedActivity { }); binding.accountSelect.setOnItemSelectedListener((SelectedListener) (parent, view, position, id) -> { - applyTemporaryBrand(accountAdapter.getItem(position)); updateLiveDataSource(boardsLiveData, boardsObserver, syncManager.getBoardsWithEditPermission(parent.getSelectedItemId())); }); - binding.boardSelect.setOnItemSelectedListener((SelectedListener) (parent, view, position, id) -> - updateLiveDataSource(stacksLiveData, stacksObserver, syncManager.getStacksForBoard(binding.accountSelect.getSelectedItemId(), parent.getSelectedItemId()))); + binding.boardSelect.setOnItemSelectedListener((SelectedListener) (parent, view, position, id) -> { + applyBrand(Color.parseColor('#' + ((Board) binding.boardSelect.getSelectedItem()).getColor())); + updateLiveDataSource(stacksLiveData, stacksObserver, syncManager.getStacksForBoard(binding.accountSelect.getSelectedItemId(), parent.getSelectedItemId())); + }); binding.cancel.setOnClickListener((v) -> finish()); binding.submit.setOnClickListener((v) -> onSubmit()); @@ -181,10 +201,10 @@ public class PrepareCreateActivity extends BrandedActivity { startActivity(EditActivity.createNewCardIntent(this, account, boardId, stackId, receivedClipData)); } - Application.saveCurrentAccountId(this, account.getId()); - Application.saveCurrentBoardId(this, account.getId(), boardId); - Application.saveCurrentStackId(this, account.getId(), boardId, stackId); - applyBrand(parseColor(account.getColor()), parseColor(account.getTextColor())); + saveCurrentAccountId(this, account.getId()); + saveCurrentBoardId(this, account.getId(), boardId); + saveCurrentStackId(this, account.getId(), boardId, stackId); + applyBrand(parseColor(account.getColor())); finish(); } else { @@ -213,21 +233,19 @@ public class PrepareCreateActivity extends BrandedActivity { return TextUtils.isEmpty(text) ? null : text.toString(); } - private void applyTemporaryBrand(@Nullable Account account) { + @Override + public void applyBrand(int mainColor) { try { - if (account != null && brandingEnabled) { - applyBrand(parseColor(account.getColor()), parseColor(account.getTextColor())); + if (brandingEnabled) { + @ColorInt final int finalMainColor = contrastRatioIsSufficientBigAreas(mainColor, ContextCompat.getColor(this, R.color.primary)) + ? mainColor + : isDarkTheme(this) ? Color.WHITE : Color.BLACK; + DrawableCompat.setTintList(binding.submit.getBackground(), ColorStateList.valueOf(finalMainColor)); + binding.submit.setTextColor(ColorUtil.getForegroundColorForBackgroundColor(finalMainColor)); + binding.cancel.setTextColor(getSecondaryForegroundColorDependingOnTheme(this, mainColor)); } } catch (Throwable t) { DeckLog.logError(t); } } - - @Override - public void applyBrand(int mainColor, int textColor) { - applyBrandToPrimaryToolbar(mainColor, textColor, binding.toolbar); - binding.submit.setBackgroundColor(mainColor); - binding.submit.setTextColor(textColor); - binding.cancel.setTextColor(getSecondaryForegroundColorDependingOnTheme(this, mainColor)); - } }
\ No newline at end of file diff --git a/app/src/main/java/it/niedermann/nextcloud/deck/ui/settings/SettingsActivity.java b/app/src/main/java/it/niedermann/nextcloud/deck/ui/settings/SettingsActivity.java index 363e5b46e..6f5a9a7e8 100644 --- a/app/src/main/java/it/niedermann/nextcloud/deck/ui/settings/SettingsActivity.java +++ b/app/src/main/java/it/niedermann/nextcloud/deck/ui/settings/SettingsActivity.java @@ -37,7 +37,7 @@ public class SettingsActivity extends BrandedActivity { } @Override - public void applyBrand(int mainColor, int textColor) { - applyBrandToPrimaryToolbar(mainColor, textColor, binding.toolbar); + public void applyBrand(int mainColor) { + // Nothing to do... } } diff --git a/app/src/main/java/it/niedermann/nextcloud/deck/ui/settings/SettingsFragment.java b/app/src/main/java/it/niedermann/nextcloud/deck/ui/settings/SettingsFragment.java index f531abbe1..ea8d90f22 100644 --- a/app/src/main/java/it/niedermann/nextcloud/deck/ui/settings/SettingsFragment.java +++ b/app/src/main/java/it/niedermann/nextcloud/deck/ui/settings/SettingsFragment.java @@ -4,19 +4,20 @@ import android.app.Activity; import android.content.Context; import android.os.Bundle; -import androidx.annotation.ColorInt; import androidx.annotation.Nullable; import androidx.preference.ListPreference; import androidx.preference.Preference; import androidx.preference.PreferenceFragmentCompat; -import it.niedermann.nextcloud.deck.Application; import it.niedermann.nextcloud.deck.DeckLog; import it.niedermann.nextcloud.deck.R; import it.niedermann.nextcloud.deck.persistence.sync.SyncWorker; import it.niedermann.nextcloud.deck.ui.branding.Branded; import it.niedermann.nextcloud.deck.ui.branding.BrandedSwitchPreference; +import static it.niedermann.nextcloud.deck.DeckApplication.setAppTheme; +import static it.niedermann.nextcloud.deck.ui.branding.BrandingUtil.readBrandMainColor; + public class SettingsFragment extends PreferenceFragmentCompat implements Branded { private BrandedSwitchPreference wifiOnlyPref; @@ -44,7 +45,7 @@ public class SettingsFragment extends PreferenceFragmentCompat implements Brande themePref.setOnPreferenceChangeListener((Preference preference, Object newValue) -> { final Boolean darkTheme = (Boolean) newValue; DeckLog.log("darkTheme: " + darkTheme); - Application.setAppTheme(darkTheme); + setAppTheme(darkTheme); requireActivity().setResult(Activity.RESULT_OK); requireActivity().recreate(); return true; @@ -82,16 +83,14 @@ public class SettingsFragment extends PreferenceFragmentCompat implements Brande super.onStart(); @Nullable Context context = getContext(); if (context != null) { - @ColorInt final int mainColor = Application.readBrandMainColor(context); - @ColorInt final int textColor = Application.readBrandTextColor(context); - applyBrand(mainColor, textColor); + applyBrand(readBrandMainColor(context)); } } @Override - public void applyBrand(int mainColor, int textColor) { - wifiOnlyPref.applyBrand(mainColor, textColor); - themePref.applyBrand(mainColor, textColor); - brandingPref.applyBrand(mainColor, textColor); + public void applyBrand(int mainColor) { + wifiOnlyPref.applyBrand(mainColor); + themePref.applyBrand(mainColor); + brandingPref.applyBrand(mainColor); } } diff --git a/app/src/main/java/it/niedermann/nextcloud/deck/ui/sharetarget/ShareProgressDialogFragment.java b/app/src/main/java/it/niedermann/nextcloud/deck/ui/sharetarget/ShareProgressDialogFragment.java index 12a06bb11..271b60489 100644 --- a/app/src/main/java/it/niedermann/nextcloud/deck/ui/sharetarget/ShareProgressDialogFragment.java +++ b/app/src/main/java/it/niedermann/nextcloud/deck/ui/sharetarget/ShareProgressDialogFragment.java @@ -21,7 +21,7 @@ import it.niedermann.nextcloud.deck.ui.branding.BrandedDialogFragment; import it.niedermann.nextcloud.deck.ui.exception.ExceptionDialogFragment; import static android.graphics.PorterDuff.Mode; -import static it.niedermann.nextcloud.deck.ui.branding.BrandedActivity.getSecondaryForegroundColorDependingOnTheme; +import static it.niedermann.nextcloud.deck.ui.branding.BrandingUtil.getSecondaryForegroundColorDependingOnTheme; import static it.niedermann.nextcloud.deck.util.ExceptionUtil.getDebugInfos; public class ShareProgressDialogFragment extends BrandedDialogFragment { @@ -114,7 +114,7 @@ public class ShareProgressDialogFragment extends BrandedDialogFragment { } @Override - public void applyBrand(int mainColor, int textColor) { + public void applyBrand(int mainColor) { binding.progress.getProgressDrawable().setColorFilter( getSecondaryForegroundColorDependingOnTheme(requireContext(), mainColor), Mode.SRC_IN); binding.errorReportButton.setTextColor(getSecondaryForegroundColorDependingOnTheme(requireContext(), mainColor)); diff --git a/app/src/main/java/it/niedermann/nextcloud/deck/ui/stack/EditStackDialogFragment.java b/app/src/main/java/it/niedermann/nextcloud/deck/ui/stack/EditStackDialogFragment.java index 92272b99e..6bedd0a0f 100644 --- a/app/src/main/java/it/niedermann/nextcloud/deck/ui/stack/EditStackDialogFragment.java +++ b/app/src/main/java/it/niedermann/nextcloud/deck/ui/stack/EditStackDialogFragment.java @@ -17,11 +17,11 @@ import java.util.Objects; import it.niedermann.nextcloud.deck.R; import it.niedermann.nextcloud.deck.databinding.DialogStackCreateBinding; -import it.niedermann.nextcloud.deck.ui.branding.BrandedActivity; import it.niedermann.nextcloud.deck.ui.branding.BrandedAlertDialogBuilder; import it.niedermann.nextcloud.deck.ui.branding.BrandedDialogFragment; -import static it.niedermann.nextcloud.deck.Application.NO_STACK_ID; +import static it.niedermann.nextcloud.deck.DeckApplication.NO_STACK_ID; +import static it.niedermann.nextcloud.deck.ui.branding.BrandingUtil.applyBrandToEditText; public class EditStackDialogFragment extends BrandedDialogFragment { private static final String KEY_STACK_ID = "stack_id"; @@ -90,7 +90,7 @@ public class EditStackDialogFragment extends BrandedDialogFragment { } @Override - public void applyBrand(int mainColor, int textColor) { - BrandedActivity.applyBrandToEditText(mainColor, textColor, binding.input); + public void applyBrand(int mainColor) { + applyBrandToEditText(mainColor, binding.input); } } diff --git a/app/src/main/java/it/niedermann/nextcloud/deck/ui/stack/StackFragment.java b/app/src/main/java/it/niedermann/nextcloud/deck/ui/stack/StackFragment.java index 942310199..726a74184 100644 --- a/app/src/main/java/it/niedermann/nextcloud/deck/ui/stack/StackFragment.java +++ b/app/src/main/java/it/niedermann/nextcloud/deck/ui/stack/StackFragment.java @@ -138,9 +138,9 @@ public class StackFragment extends BrandedFragment implements DragAndDropTab<Car } @Override - public void applyBrand(int mainColor, int textColor) { + public void applyBrand(int mainColor) { if (this.adapter != null) { - this.adapter.applyBrand(mainColor, textColor); + this.adapter.applyBrand(mainColor); } } diff --git a/app/src/main/java/it/niedermann/nextcloud/deck/ui/view/ColorChooser.java b/app/src/main/java/it/niedermann/nextcloud/deck/ui/view/ColorChooser.java index d444e075e..30dc0ada4 100644 --- a/app/src/main/java/it/niedermann/nextcloud/deck/ui/view/ColorChooser.java +++ b/app/src/main/java/it/niedermann/nextcloud/deck/ui/view/ColorChooser.java @@ -5,21 +5,30 @@ import android.content.res.TypedArray; import android.util.AttributeSet; import android.view.LayoutInflater; import android.view.View; +import android.view.ViewGroup; import android.widget.ImageView; import android.widget.LinearLayout; import androidx.annotation.Nullable; +import com.google.android.flexbox.FlexboxLayout; import com.skydoves.colorpickerview.listeners.ColorEnvelopeListener; import it.niedermann.nextcloud.deck.R; import it.niedermann.nextcloud.deck.databinding.WidgetColorChooserBinding; import it.niedermann.nextcloud.deck.util.ViewUtil; +import static it.niedermann.nextcloud.deck.util.DimensionUtil.dpToPx; + public class ColorChooser extends LinearLayout { private WidgetColorChooserBinding binding; + private final FlexboxLayout.LayoutParams params = new FlexboxLayout.LayoutParams( + ViewGroup.LayoutParams.WRAP_CONTENT, + ViewGroup.LayoutParams.WRAP_CONTENT + ); + private Context context; private String[] colors; @@ -32,6 +41,9 @@ public class ColorChooser extends LinearLayout { super(context, attrs); this.context = context; + params.setMargins(0, dpToPx(context, R.dimen.spacer_1x), 0, 0); + params.setFlexBasisPercent(.15f); + TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.ColorChooser, 0, 0); colors = getResources().getStringArray(a.getResourceId(R.styleable.ColorChooser_colors, 0)); @@ -40,6 +52,7 @@ public class ColorChooser extends LinearLayout { binding = WidgetColorChooserBinding.inflate(LayoutInflater.from(context), this, true); for (final String color : colors) { ImageView image = new ImageView(getContext()); + image.setLayoutParams(params); image.setOnClickListener((imageView) -> { if (previouslySelectedImageView != null) { // null when first selection previouslySelectedImageView.setImageDrawable(ViewUtil.getTintedImageView(this.context, R.drawable.circle_grey600_36dp, previouslySelectedColor)); diff --git a/app/src/main/java/it/niedermann/nextcloud/deck/ui/view/OverlappingAvatars.java b/app/src/main/java/it/niedermann/nextcloud/deck/ui/view/OverlappingAvatars.java index 63be42465..501d33106 100644 --- a/app/src/main/java/it/niedermann/nextcloud/deck/ui/view/OverlappingAvatars.java +++ b/app/src/main/java/it/niedermann/nextcloud/deck/ui/view/OverlappingAvatars.java @@ -48,7 +48,7 @@ public class OverlappingAvatars extends RelativeLayout { avatarSize = dpToPx(context, R.dimen.avatar_size_small) + avatarBorderSize * 2; overlapPx = dpToPx(context, R.dimen.avatar_size_small_overlapping); borderDrawable = getResources().getDrawable(R.drawable.avatar_border); - DrawableCompat.setTint(borderDrawable, getResources().getColor(R.color.avatars_overlapping_border_color)); + DrawableCompat.setTint(borderDrawable, getResources().getColor(R.color.bg_card)); } public void setAvatars(@NonNull Account account, @NonNull List<User> assignedUsers) { diff --git a/app/src/main/java/it/niedermann/nextcloud/deck/ui/widget/singlecard/SelectCardForWidgetActivity.java b/app/src/main/java/it/niedermann/nextcloud/deck/ui/widget/singlecard/SelectCardForWidgetActivity.java index cda805450..8c1fbe1fa 100644 --- a/app/src/main/java/it/niedermann/nextcloud/deck/ui/widget/singlecard/SelectCardForWidgetActivity.java +++ b/app/src/main/java/it/niedermann/nextcloud/deck/ui/widget/singlecard/SelectCardForWidgetActivity.java @@ -6,17 +6,22 @@ import android.os.Bundle; import android.view.Menu; import android.view.View; +import androidx.annotation.ColorInt; import androidx.annotation.NonNull; import it.niedermann.nextcloud.deck.R; import it.niedermann.nextcloud.deck.model.Board; import it.niedermann.nextcloud.deck.model.full.FullCard; import it.niedermann.nextcloud.deck.ui.MainActivity; +import it.niedermann.nextcloud.deck.ui.branding.BrandingUtil; import it.niedermann.nextcloud.deck.ui.card.SelectCardListener; +import static it.niedermann.nextcloud.deck.ui.branding.BrandingUtil.saveBrandColors; + public class SelectCardForWidgetActivity extends MainActivity implements SelectCardListener { private int appWidgetId; + @ColorInt private int originalBrandColor; @Override protected void onCreate(Bundle savedInstanceState) { @@ -35,6 +40,7 @@ public class SelectCardForWidgetActivity extends MainActivity implements SelectC if (appWidgetId == AppWidgetManager.INVALID_APPWIDGET_ID) { finish(); } + originalBrandColor = BrandingUtil.readBrandMainColor(this); } @Override @@ -45,6 +51,7 @@ public class SelectCardForWidgetActivity extends MainActivity implements SelectC .putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId); setResult(RESULT_OK, updateIntent); getApplicationContext().sendBroadcast(updateIntent); + saveBrandColors(this, originalBrandColor); finish(); } diff --git a/app/src/main/java/it/niedermann/nextcloud/deck/util/ColorUtil.java b/app/src/main/java/it/niedermann/nextcloud/deck/util/ColorUtil.java index 4adf43b34..08354c5fb 100644 --- a/app/src/main/java/it/niedermann/nextcloud/deck/util/ColorUtil.java +++ b/app/src/main/java/it/niedermann/nextcloud/deck/util/ColorUtil.java @@ -109,7 +109,18 @@ public final class ColorUtil { return ret; } - private static double getContrastRatio(@ColorInt int colorOne, @ColorInt int colorTwo) { + public static boolean contrastRatioIsSufficientBigAreas(@ColorInt int colorOne, @ColorInt int colorTwo) { + ColorPair key = new ColorPair(colorOne, colorTwo); + Boolean ret = CONTRAST_RATIO_SUFFICIENT_CACHE.get(key); + if (ret == null) { + ret = getContrastRatio(colorOne, colorTwo) > 1.47d; + CONTRAST_RATIO_SUFFICIENT_CACHE.put(key, ret); + return ret; + } + return ret; + } + + public static double getContrastRatio(@ColorInt int colorOne, @ColorInt int colorTwo) { final double lum1 = getLuminanace(colorOne); final double lum2 = getLuminanace(colorTwo); final double brightest = Math.max(lum1, lum2); diff --git a/app/src/main/java/it/niedermann/nextcloud/deck/util/ViewUtil.java b/app/src/main/java/it/niedermann/nextcloud/deck/util/ViewUtil.java index 1039b5c95..6abbde557 100644 --- a/app/src/main/java/it/niedermann/nextcloud/deck/util/ViewUtil.java +++ b/app/src/main/java/it/niedermann/nextcloud/deck/util/ViewUtil.java @@ -27,11 +27,12 @@ import com.bumptech.glide.request.transition.Transition; import java.util.Date; import java.util.List; -import it.niedermann.nextcloud.deck.Application; import it.niedermann.nextcloud.deck.R; import it.niedermann.nextcloud.deck.model.Account; import it.niedermann.nextcloud.deck.model.ocs.comment.Mention; +import static it.niedermann.nextcloud.deck.DeckApplication.isDarkTheme; + public final class ViewUtil { private ViewUtil() { } @@ -53,7 +54,7 @@ public final class ViewUtil { long diff = DateUtil.getDayDifference(new Date(), dueDate); int backgroundDrawable = 0; - int textColor = Application.getAppTheme(context) ? R.color.dark_fg_primary : R.color.grey600; + int textColor = isDarkTheme(context) ? R.color.dark_fg_primary : R.color.grey600; if (diff == 1) { // due date: tomorrow |