diff --git a/CHANGELOG.md b/CHANGELOG.md index bd04a98bc..25448aac7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,7 @@ Types of changes can be: Added/Changed/Deprecated/Removed/Fixed/Security ### Changed - improve conversation list design and dark/light theming (@AndyScherzinger) +- introduce new dark/light toolbar/searchbar design (@AndyScherzinger) ### Fixed - @ in username is allowed for phonebook sync diff --git a/app/src/main/java/com/nextcloud/talk/activities/MainActivity.kt b/app/src/main/java/com/nextcloud/talk/activities/MainActivity.kt index 349f82d3c..17c7a5aab 100644 --- a/app/src/main/java/com/nextcloud/talk/activities/MainActivity.kt +++ b/app/src/main/java/com/nextcloud/talk/activities/MainActivity.kt @@ -38,12 +38,20 @@ import com.bluelinelabs.conductor.Router import com.bluelinelabs.conductor.RouterTransaction import com.bluelinelabs.conductor.changehandler.HorizontalChangeHandler import com.bluelinelabs.conductor.changehandler.VerticalChangeHandler +import com.google.android.material.appbar.AppBarLayout import com.google.android.material.appbar.MaterialToolbar +import com.google.android.material.button.MaterialButton +import com.google.android.material.card.MaterialCardView import com.google.android.material.snackbar.Snackbar +import com.google.android.material.textview.MaterialTextView import com.nextcloud.talk.R import com.nextcloud.talk.api.NcApi import com.nextcloud.talk.application.NextcloudTalkApplication -import com.nextcloud.talk.controllers.* +import com.nextcloud.talk.controllers.CallNotificationController +import com.nextcloud.talk.controllers.ConversationsListController +import com.nextcloud.talk.controllers.LockedController +import com.nextcloud.talk.controllers.ServerSelectionController +import com.nextcloud.talk.controllers.WebViewLoginController import com.nextcloud.talk.controllers.base.providers.ActionBarProvider import com.nextcloud.talk.models.json.conversations.RoomOverall import com.nextcloud.talk.utils.ApiUtils @@ -69,8 +77,16 @@ import javax.inject.Inject @AutoInjector(NextcloudTalkApplication::class) class MainActivity : BaseActivity(), ActionBarProvider { + @BindView(R.id.appBar) + lateinit var appBar: AppBarLayout @BindView(R.id.toolbar) lateinit var toolbar: MaterialToolbar + @BindView(R.id.home_toolbar) + lateinit var searchCardView: MaterialCardView + @BindView(R.id.search_text) + lateinit var searchInputText: MaterialTextView + @BindView(R.id.switch_account_button) + lateinit var settingsButton: MaterialButton @BindView(R.id.controller_container) lateinit var container: ViewGroup diff --git a/app/src/main/java/com/nextcloud/talk/controllers/ConversationsListController.java b/app/src/main/java/com/nextcloud/talk/controllers/ConversationsListController.java index 98bb83d78..33d270774 100644 --- a/app/src/main/java/com/nextcloud/talk/controllers/ConversationsListController.java +++ b/app/src/main/java/com/nextcloud/talk/controllers/ConversationsListController.java @@ -2,7 +2,9 @@ * Nextcloud Talk application * * @author Mario Danic - * Copyright (C) 2017 Mario Danic (mario@lovelyhq.com) + * @author Andy Scherzinger + * Copyright (C) 2021 Andy Scherzinger (info@andy-scherzinger.de) + * Copyright (C) 2017-2020 Mario Danic (mario@lovelyhq.com) * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -20,6 +22,7 @@ package com.nextcloud.talk.controllers; +import android.animation.AnimatorInflater; import android.app.SearchManager; import android.content.Context; import android.content.Intent; @@ -27,8 +30,10 @@ import android.graphics.Bitmap; import android.os.Build; import android.os.Bundle; import android.os.Handler; +import android.text.Editable; import android.text.InputType; import android.text.TextUtils; +import android.text.TextWatcher; import android.view.LayoutInflater; import android.view.Menu; import android.view.MenuInflater; @@ -39,6 +44,19 @@ import android.view.inputmethod.EditorInfo; import android.widget.ProgressBar; import android.widget.RelativeLayout; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.appcompat.widget.SearchView; +import androidx.core.content.res.ResourcesCompat; +import androidx.core.graphics.drawable.RoundedBitmapDrawable; +import androidx.core.graphics.drawable.RoundedBitmapDrawableFactory; +import androidx.core.view.MenuItemCompat; +import androidx.recyclerview.widget.RecyclerView; +import androidx.swiperefreshlayout.widget.SwipeRefreshLayout; +import androidx.work.Data; +import androidx.work.OneTimeWorkRequest; +import androidx.work.WorkManager; + import com.bluelinelabs.conductor.RouterTransaction; import com.bluelinelabs.conductor.changehandler.HorizontalChangeHandler; import com.bluelinelabs.conductor.changehandler.TransitionChangeHandlerCompat; @@ -52,10 +70,12 @@ import com.facebook.imagepipeline.core.ImagePipeline; import com.facebook.imagepipeline.datasource.BaseBitmapDataSubscriber; import com.facebook.imagepipeline.image.CloseableImage; import com.facebook.imagepipeline.request.ImageRequest; +import com.google.android.material.button.MaterialButton; import com.google.android.material.floatingactionbutton.FloatingActionButton; import com.kennyc.bottomsheet.BottomSheet; import com.nextcloud.talk.R; import com.nextcloud.talk.activities.MagicCallActivity; +import com.nextcloud.talk.activities.MainActivity; import com.nextcloud.talk.adapters.items.CallItem; import com.nextcloud.talk.adapters.items.ConversationItem; import com.nextcloud.talk.api.NcApi; @@ -95,17 +115,6 @@ import java.util.List; import javax.inject.Inject; -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; -import androidx.appcompat.widget.SearchView; -import androidx.core.graphics.drawable.RoundedBitmapDrawable; -import androidx.core.graphics.drawable.RoundedBitmapDrawableFactory; -import androidx.core.view.MenuItemCompat; -import androidx.recyclerview.widget.RecyclerView; -import androidx.swiperefreshlayout.widget.SwipeRefreshLayout; -import androidx.work.Data; -import androidx.work.OneTimeWorkRequest; -import androidx.work.WorkManager; import autodagger.AutoInjector; import butterknife.BindView; import eu.davidea.fastscroller.FastScroller; @@ -214,9 +223,16 @@ public class ConversationsListController extends BaseController implements Searc prepareViews(); } - private void loadUserAvatar(MenuItem menuItem) { + private void loadUserAvatar(MaterialButton button) { if (getActivity() != null) { - int avatarSize = (int) DisplayUtils.convertDpToPixel(menuItem.getIcon().getIntrinsicHeight(), getActivity()); + int avatarSize; + + if (getResources() != null) { + avatarSize = getResources().getDimensionPixelSize(R.dimen.avatar_size_app_bar); + } else { + avatarSize = (int) DisplayUtils.convertDpToPixel(30.0f, context); + } + ImageRequest imageRequest = DisplayUtils.getImageRequestForUrl(ApiUtils.getUrlForAvatarWithNameAndPixels(currentUser.getBaseUrl(), currentUser.getUserId(), avatarSize), currentUser); @@ -229,13 +245,15 @@ public class ConversationsListController extends BaseController implements Searc RoundedBitmapDrawable roundedBitmapDrawable = RoundedBitmapDrawableFactory.create(getResources(), bitmap); roundedBitmapDrawable.setCircular(true); roundedBitmapDrawable.setAntiAlias(true); - menuItem.setIcon(roundedBitmapDrawable); + button.setIcon(roundedBitmapDrawable); } } @Override protected void onFailureImpl(DataSource> dataSource) { - menuItem.setIcon(R.drawable.ic_settings_white_24dp); + if (getResources() != null) { + button.setIcon(ResourcesCompat.getDrawable(getResources(), R.drawable.ic_settings_white_24dp, null)); + } } }, UiThreadImmediateExecutorService.getInstance()); } @@ -251,6 +269,9 @@ public class ConversationsListController extends BaseController implements Searc if (currentUser != null) { credentials = ApiUtils.getCredentials(currentUser.getUsername(), currentUser.getToken()); shouldUseLastMessageLayout = currentUser.hasSpreedFeatureCapability("last-room-activity"); + if (getActivity() != null && getActivity() instanceof MainActivity) { + loadUserAvatar(((MainActivity) getActivity()).settingsButton); + } fetchData(false); } } @@ -266,6 +287,7 @@ public class ConversationsListController extends BaseController implements Searc SearchManager searchManager = (SearchManager) getActivity().getSystemService(Context.SEARCH_SERVICE); if (searchItem != null) { searchView = (SearchView) MenuItemCompat.getActionView(searchItem); + DisplayUtils.themeSearchView(searchView, context); searchView.setMaxWidth(Integer.MAX_VALUE); searchView.setInputType(InputType.TYPE_TEXT_VARIATION_FILTER); int imeOptions = EditorInfo.IME_ACTION_DONE | EditorInfo.IME_FLAG_NO_FULLSCREEN; @@ -273,7 +295,7 @@ public class ConversationsListController extends BaseController implements Searc imeOptions |= EditorInfo.IME_FLAG_NO_PERSONALIZED_LEARNING; } searchView.setImeOptions(imeOptions); - searchView.setQueryHint(getResources().getString(R.string.nc_search)); + searchView.setQueryHint(getSearchHint()); if (searchManager != null) { searchView.setSearchableInfo(searchManager.getSearchableInfo(getActivity().getComponentName())); } @@ -283,22 +305,7 @@ public class ConversationsListController extends BaseController implements Searc } @Override - public boolean onOptionsItemSelected(@NonNull MenuItem item) { - switch (item.getItemId()) { - case R.id.action_settings: - ArrayList names = new ArrayList<>(); - names.add("userAvatar.transitionTag"); - getRouter().pushController((RouterTransaction.with(new SettingsController()) - .pushChangeHandler(new TransitionChangeHandlerCompat(new SharedElementTransition(names), new VerticalChangeHandler())) - .popChangeHandler(new TransitionChangeHandlerCompat(new SharedElementTransition(names), new VerticalChangeHandler())))); - return true; - default: - return super.onOptionsItemSelected(item); - } - } - - @Override - public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { + public void onCreateOptionsMenu(@NonNull Menu menu, @NonNull MenuInflater inflater) { super.onCreateOptionsMenu(menu, inflater); inflater.inflate(R.menu.menu_conversation_plus_filter, menu); searchItem = menu.findItem(R.id.action_search); @@ -306,16 +313,93 @@ public class ConversationsListController extends BaseController implements Searc } @Override - public void onPrepareOptionsMenu(Menu menu) { + public void onPrepareOptionsMenu(@NonNull Menu menu) { super.onPrepareOptionsMenu(menu); + + searchView = (SearchView) MenuItemCompat.getActionView(searchItem); + + MainActivity activity = (MainActivity) getActivity(); + searchItem.setVisible(callItems.size() > 0); if (adapter.hasFilter()) { - searchItem.expandActionView(); + showSearchView(activity, searchView, searchItem); searchView.setQuery(adapter.getFilter(String.class), false); } - MenuItem menuItem = menu.findItem(R.id.action_settings); - loadUserAvatar(menuItem); + if (activity != null) { + activity.getSearchInputText().setOnClickListener(v -> { + showSearchView(activity, searchView, searchItem); + if (getResources() != null) { + DisplayUtils.applyColorToStatusBar( + activity, + ResourcesCompat.getColor(getResources(), R.color.appbar, null) + ); + } + }); + } + + searchView.setOnCloseListener(() -> { + if (TextUtils.isEmpty(searchView.getQuery().toString())) { + searchView.onActionViewCollapsed(); + if (activity != null && getResources() != null) { + DisplayUtils.applyColorToStatusBar( + activity, + ResourcesCompat.getColor(getResources(), R.color.bg_default, null) + ); + } + } else { + searchView.post(() -> searchView.setQuery("", true)); + } + return true; + }); + + searchItem.setOnActionExpandListener(new MenuItem.OnActionExpandListener() { + @Override + public boolean onMenuItemActionExpand(MenuItem item) { + return true; + } + + @Override + public boolean onMenuItemActionCollapse(MenuItem item) { + searchView.onActionViewCollapsed(); + MainActivity activity = (MainActivity) getActivity(); + if (activity != null) { + activity.appBar.setStateListAnimator(AnimatorInflater.loadStateListAnimator( + activity.appBar.getContext(), + R.animator.appbar_elevation_off) + ); + activity.toolbar.setVisibility(View.GONE); + activity.searchCardView.setVisibility(View.VISIBLE); + if (getResources() != null) { + DisplayUtils.applyColorToStatusBar( + activity, + ResourcesCompat.getColor(getResources(), R.color.bg_default, null) + ); + } + } + SmoothScrollLinearLayoutManager layoutManager = + (SmoothScrollLinearLayoutManager) recyclerView.getLayoutManager(); + if (layoutManager!=null) { + layoutManager.scrollToPositionWithOffset(0, 0); + } + return true; + } + }); + } + + protected void showSearchOrToolbar() { + if (TextUtils.isEmpty(searchQuery)) { + super.showSearchOrToolbar(); + } + } + + public void showSearchView(MainActivity activity, SearchView searchView, MenuItem searchItem) { + activity.appBar.setStateListAnimator(AnimatorInflater.loadStateListAnimator( + activity.appBar.getContext(), + R.animator.appbar_elevation_on)); + activity.toolbar.setVisibility(View.VISIBLE); + activity.searchCardView.setVisibility(View.GONE); + searchItem.expandActionView(); } private void fetchData(boolean fromBottomSheet) { @@ -385,19 +469,11 @@ public class ConversationsListController extends BaseController implements Searc adapter.updateDataSet(callItems, false); - if (searchItem != null) { - searchItem.setVisible(callItems.size() > 0); - } - if (swipeRefreshLayout != null) { swipeRefreshLayout.setRefreshing(false); } }, throwable -> { - if (searchItem != null) { - searchItem.setVisible(false); - } - if (throwable instanceof HttpException) { HttpException exception = (HttpException) throwable; switch (exception.code()) { @@ -436,7 +512,6 @@ public class ConversationsListController extends BaseController implements Searc isRefreshing = false; }); - } private void prepareViews() { @@ -472,6 +547,20 @@ public class ConversationsListController extends BaseController implements Searc } return displayName; }); + + if (getActivity() != null && getActivity() instanceof MainActivity) { + MainActivity activity = (MainActivity) getActivity(); + + if (activity.settingsButton != null) { + activity.settingsButton.setOnClickListener(v -> { + ArrayList names = new ArrayList<>(); + names.add("userAvatar.transitionTag"); + getRouter().pushController((RouterTransaction.with(new SettingsController()) + .pushChangeHandler(new TransitionChangeHandlerCompat(new SharedElementTransition(names), new VerticalChangeHandler())) + .popChangeHandler(new TransitionChangeHandlerCompat(new SharedElementTransition(names), new VerticalChangeHandler())))); + }); + } + } } private void showNewConversationsScreen() { @@ -490,7 +579,6 @@ public class ConversationsListController extends BaseController implements Searc roomsQueryDisposable != null && !roomsQueryDisposable.isDisposed()) { roomsQueryDisposable.dispose(); roomsQueryDisposable = null; - } } @@ -508,7 +596,9 @@ public class ConversationsListController extends BaseController implements Searc @Override public void onRestoreViewState(@NonNull View view, @NonNull Bundle savedViewState) { super.onRestoreViewState(view, savedViewState); - searchQuery = savedViewState.getString(KEY_SEARCH_QUERY, ""); + if (savedViewState.containsKey(KEY_SEARCH_QUERY)) { + searchQuery = savedViewState.getString(KEY_SEARCH_QUERY, ""); + } if (LovelySaveStateHandler.wasDialogOnScreen(savedViewState)) { //Dialog won't be restarted automatically, so we need to call this method. //Each dialog knows how to restore its state @@ -525,7 +615,6 @@ public class ConversationsListController extends BaseController implements Searc @Override public boolean onQueryTextChange(String newText) { if (adapter.hasNewFilter(newText) || !TextUtils.isEmpty(searchQuery)) { - if (!TextUtils.isEmpty(searchQuery)) { adapter.setFilter(searchQuery); searchQuery = ""; @@ -602,7 +691,6 @@ public class ConversationsListController extends BaseController implements Searc bottomSheet.show(); } - @Override protected String getTitle() { return getResources().getString(R.string.nc_app_name); @@ -624,7 +712,6 @@ public class ConversationsListController extends BaseController implements Searc conversation = ((CallItem) clickedItem).getModel(); } - Bundle bundle = new Bundle(); bundle.putParcelable(BundleKeys.INSTANCE.getKEY_USER_ENTITY(), currentUser); bundle.putString(BundleKeys.INSTANCE.getKEY_ROOM_TOKEN(), conversation.getToken()); @@ -753,4 +840,9 @@ public class ConversationsListController extends BaseController implements Searc } } + + @Override + public AppBarLayoutType getAppBarLayoutType() { + return AppBarLayoutType.SEARCH_BAR; + } } diff --git a/app/src/main/java/com/nextcloud/talk/controllers/ServerSelectionController.java b/app/src/main/java/com/nextcloud/talk/controllers/ServerSelectionController.java index 900dc809e..b8a9b3160 100644 --- a/app/src/main/java/com/nextcloud/talk/controllers/ServerSelectionController.java +++ b/app/src/main/java/com/nextcloud/talk/controllers/ServerSelectionController.java @@ -1,6 +1,7 @@ /* * Nextcloud Talk application * + * @author Andy Scherzinger * @author Mario Danic * Copyright (C) 2017 Mario Danic (mario@lovelyhq.com) * @@ -44,6 +45,7 @@ import com.nextcloud.talk.application.NextcloudTalkApplication; import com.nextcloud.talk.controllers.base.BaseController; import com.nextcloud.talk.utils.AccountUtils; import com.nextcloud.talk.utils.ApiUtils; +import com.nextcloud.talk.utils.DisplayUtils; import com.nextcloud.talk.utils.bundle.BundleKeys; import com.nextcloud.talk.utils.database.user.UserUtils; import com.nextcloud.talk.utils.preferences.AppPreferences; @@ -54,6 +56,10 @@ import java.security.cert.CertificateException; import javax.inject.Inject; import androidx.annotation.NonNull; +import androidx.core.content.res.ResourcesCompat; + +import org.jetbrains.annotations.NotNull; + import autodagger.AutoInjector; import butterknife.BindView; import butterknife.OnClick; @@ -90,6 +96,7 @@ public class ServerSelectionController extends BaseController { private Disposable statusQueryDisposable; + @NotNull @Override protected View inflateView(@NonNull LayoutInflater inflater, @NonNull ViewGroup container) { return inflater.inflate(R.layout.controller_server_selection, container, false); @@ -354,6 +361,11 @@ public class ServerSelectionController extends BaseController { ApplicationWideMessageHolder.getInstance().setMessageType(null); } + if (getActivity() != null && getResources() != null) { + DisplayUtils.applyColorToStatusBar(getActivity(), ResourcesCompat.getColor(getResources(), R.color.colorPrimary, null)); + DisplayUtils.applyColorToNavigationBar(getActivity().getWindow(), ResourcesCompat.getColor(getResources(), R.color.colorPrimary, null)); + } + setCertTextView(); } @@ -394,5 +406,8 @@ public class ServerSelectionController extends BaseController { statusQueryDisposable = null; } - + @Override + public AppBarLayoutType getAppBarLayoutType() { + return AppBarLayoutType.EMPTY; + } } diff --git a/app/src/main/java/com/nextcloud/talk/controllers/SettingsController.java b/app/src/main/java/com/nextcloud/talk/controllers/SettingsController.java index 81603d9a4..18fd7503c 100644 --- a/app/src/main/java/com/nextcloud/talk/controllers/SettingsController.java +++ b/app/src/main/java/com/nextcloud/talk/controllers/SettingsController.java @@ -1,6 +1,7 @@ /* * Nextcloud Talk application * + * @author Andy Scherzinger * @author Mario Danic * Copyright (C) 2017 Mario Danic (mario@lovelyhq.com) * diff --git a/app/src/main/java/com/nextcloud/talk/controllers/WebViewLoginController.java b/app/src/main/java/com/nextcloud/talk/controllers/WebViewLoginController.java index 874c8aa13..11ac36f9a 100644 --- a/app/src/main/java/com/nextcloud/talk/controllers/WebViewLoginController.java +++ b/app/src/main/java/com/nextcloud/talk/controllers/WebViewLoginController.java @@ -37,6 +37,7 @@ import android.webkit.*; import android.widget.ProgressBar; import androidx.annotation.NonNull; import androidx.appcompat.app.AppCompatActivity; +import androidx.core.content.res.ResourcesCompat; import androidx.work.OneTimeWorkRequest; import androidx.work.WorkManager; import autodagger.AutoInjector; @@ -50,6 +51,7 @@ import com.nextcloud.talk.events.CertificateEvent; import com.nextcloud.talk.jobs.PushRegistrationWorker; import com.nextcloud.talk.models.LoginData; import com.nextcloud.talk.models.database.UserEntity; +import com.nextcloud.talk.utils.DisplayUtils; import com.nextcloud.talk.utils.bundle.BundleKeys; import com.nextcloud.talk.utils.database.user.UserUtils; import com.nextcloud.talk.utils.preferences.AppPreferences; @@ -452,6 +454,16 @@ public class WebViewLoginController extends BaseController { } } + @Override + protected void onAttach(@NonNull View view) { + super.onAttach(view); + + if (getActivity() != null && getResources() != null) { + DisplayUtils.applyColorToStatusBar(getActivity(), ResourcesCompat.getColor(getResources(), R.color.colorPrimary, null)); + DisplayUtils.applyColorToNavigationBar(getActivity().getWindow(), ResourcesCompat.getColor(getResources(), R.color.colorPrimary, null)); + } + } + @Override public void onDestroy() { super.onDestroy(); @@ -465,4 +477,9 @@ public class WebViewLoginController extends BaseController { getActivity().setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_FULL_SENSOR); } } + + @Override + public AppBarLayoutType getAppBarLayoutType() { + return AppBarLayoutType.EMPTY; + } } diff --git a/app/src/main/java/com/nextcloud/talk/controllers/base/BaseController.java b/app/src/main/java/com/nextcloud/talk/controllers/base/BaseController.java index 4fa6fa602..f445e356d 100644 --- a/app/src/main/java/com/nextcloud/talk/controllers/base/BaseController.java +++ b/app/src/main/java/com/nextcloud/talk/controllers/base/BaseController.java @@ -1,7 +1,11 @@ -/** +/* * Nextcloud Talk application * + * @author Andy Scherzinger * @author BlueLine Labs, Inc. + * @author Mario Danic + * Copyright (C) 2021 Andy Scherzinger (info@andy-scherzinger.de) + * Copyright (C) 2020 Mario Danic (mario@lovelyhq.com) * Copyright (C) 2016 BlueLine Labs, Inc. *

* Licensed under the Apache License, Version 2.0 (the "License"); @@ -18,6 +22,7 @@ */ package com.nextcloud.talk.controllers.base; +import android.animation.AnimatorInflater; import android.content.Context; import android.os.Build; import android.os.Bundle; @@ -28,25 +33,39 @@ import android.view.ViewGroup; import android.view.inputmethod.EditorInfo; import android.view.inputmethod.InputMethodManager; import android.widget.EditText; + import androidx.annotation.NonNull; import androidx.annotation.RequiresApi; import androidx.appcompat.app.ActionBar; -import autodagger.AutoInjector; +import androidx.core.content.res.ResourcesCompat; + import com.bluelinelabs.conductor.Controller; +import com.google.android.material.appbar.AppBarLayout; +import com.nextcloud.talk.R; +import com.nextcloud.talk.activities.MainActivity; import com.nextcloud.talk.application.NextcloudTalkApplication; import com.nextcloud.talk.controllers.AccountVerificationController; import com.nextcloud.talk.controllers.ServerSelectionController; import com.nextcloud.talk.controllers.SwitchAccountController; import com.nextcloud.talk.controllers.WebViewLoginController; import com.nextcloud.talk.controllers.base.providers.ActionBarProvider; +import com.nextcloud.talk.utils.DisplayUtils; import com.nextcloud.talk.utils.preferences.AppPreferences; -import javax.inject.Inject; import java.util.ArrayList; import java.util.List; +import javax.inject.Inject; + +import autodagger.AutoInjector; + @AutoInjector(NextcloudTalkApplication.class) public abstract class BaseController extends ButterKnifeController { + public enum AppBarLayoutType { + TOOLBAR, + SEARCH_BAR, + EMPTY + } private static final String TAG = "BaseController"; @Inject @@ -95,6 +114,11 @@ public abstract class BaseController extends ButterKnifeController { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O && appPreferences.getIsKeyboardIncognito()) { disableKeyboardPersonalisedLearning((ViewGroup) view); + + if (getActivity() != null && getActivity() instanceof MainActivity) { + MainActivity activity = (MainActivity) getActivity(); + disableKeyboardPersonalisedLearning(activity.appBar); + } } } @@ -112,12 +136,71 @@ public abstract class BaseController extends ButterKnifeController { @Override protected void onAttach(@NonNull View view) { - super.onAttach(view); + showSearchOrToolbar(); setTitle(); if (getActionBar() != null) { getActionBar().setDisplayHomeAsUpEnabled(getParentController() != null || getRouter().getBackstackSize() > 1); } + + super.onAttach(view); + } + + protected void showSearchOrToolbar() { + if (getActivity() != null && getActivity() instanceof MainActivity) { + boolean showSearchBar = getAppBarLayoutType() == AppBarLayoutType.SEARCH_BAR; + MainActivity activity = (MainActivity) getActivity(); + + if (getAppBarLayoutType() == AppBarLayoutType.EMPTY) { + activity.toolbar.setVisibility(View.GONE); + activity.getSearchCardView().setVisibility(View.GONE); + } else { + AppBarLayout.LayoutParams layoutParams = (AppBarLayout.LayoutParams) activity.searchCardView.getLayoutParams(); + + if (showSearchBar) { + activity.getSearchCardView().setVisibility(View.VISIBLE); + activity.getSearchInputText().setHint(getSearchHint()); + activity.toolbar.setVisibility(View.GONE); + //layoutParams.setScrollFlags(AppBarLayout.LayoutParams.SCROLL_FLAG_SCROLL | AppBarLayout.LayoutParams.SCROLL_FLAG_SNAP | AppBarLayout.LayoutParams.SCROLL_FLAG_ENTER_ALWAYS); + layoutParams.setScrollFlags(0); + activity.appBar.setStateListAnimator(AnimatorInflater.loadStateListAnimator( + activity.appBar.getContext(), + R.animator.appbar_elevation_off) + ); + } else { + activity.getSearchCardView().setVisibility(View.GONE); + activity.toolbar.setVisibility(View.VISIBLE); + layoutParams.setScrollFlags(0); + activity.appBar.setStateListAnimator(AnimatorInflater.loadStateListAnimator( + activity.appBar.getContext(), + R.animator.appbar_elevation_on) + ); + } + + activity.searchCardView.setLayoutParams(layoutParams); + + if ((getResources() != null)) { + if (showSearchBar) { + DisplayUtils.applyColorToStatusBar( + activity, ResourcesCompat.getColor(getResources(), + R.color.bg_default, null) + ); + } else { + DisplayUtils.applyColorToStatusBar( + activity, ResourcesCompat.getColor(getResources(), + R.color.appbar, null) + ); + } + } + } + + if ((getResources() != null)) { + DisplayUtils.applyColorToNavigationBar( + activity.getWindow(), + ResourcesCompat.getColor(getResources(), R.color.bg_default, null) + ); + } + } } @Override @@ -164,4 +247,12 @@ public abstract class BaseController extends ButterKnifeController { } } } + + public AppBarLayoutType getAppBarLayoutType() { + return AppBarLayoutType.TOOLBAR; + } + + public String getSearchHint() { + return context.getString(R.string.appbar_search_in, context.getString(R.string.nc_app_name)); + } } diff --git a/app/src/main/java/com/nextcloud/talk/jobs/NotificationWorker.java b/app/src/main/java/com/nextcloud/talk/jobs/NotificationWorker.java index 240915f61..12362217c 100644 --- a/app/src/main/java/com/nextcloud/talk/jobs/NotificationWorker.java +++ b/app/src/main/java/com/nextcloud/talk/jobs/NotificationWorker.java @@ -107,6 +107,8 @@ import retrofit2.Retrofit; @AutoInjector(NextcloudTalkApplication.class) public class NotificationWorker extends Worker { public static final String TAG = "NotificationWorker"; + private static final String CHAT = "chat"; + private static final String ROOM = "room"; @Inject AppPreferences appPreferences; @@ -274,7 +276,7 @@ public class NotificationWorker extends Worker { smallIcon = R.drawable.ic_logo; - if (decryptedPushMessage.getType().equals("chat") || decryptedPushMessage.getType().equals("room")) { + if (CHAT.equals(decryptedPushMessage.getType()) || ROOM.equals(decryptedPushMessage.getType())) { category = Notification.CATEGORY_MESSAGE; } else { category = Notification.CATEGORY_CALL; @@ -289,7 +291,7 @@ public class NotificationWorker extends Worker { break; default: // assuming one2one - if (decryptedPushMessage.getType().equals("chat") || decryptedPushMessage.getType().equals("room")) { + if (CHAT.equals(decryptedPushMessage.getType()) || ROOM.equals(decryptedPushMessage.getType())) { largeIcon = BitmapFactory.decodeResource(context.getResources(), R.drawable.ic_comment); } else { largeIcon = BitmapFactory.decodeResource(context.getResources(), R.drawable.ic_call_black_24dp); @@ -339,7 +341,7 @@ public class NotificationWorker extends Worker { Long.toString(crc32.getValue()), groupName);*/ - if (decryptedPushMessage.getType().equals("chat") || decryptedPushMessage.getType().equals("room")) { + if (CHAT.equals(decryptedPushMessage.getType()) || ROOM.equals(decryptedPushMessage.getType())) { AudioAttributes.Builder audioAttributesBuilder = new AudioAttributes.Builder().setContentType (AudioAttributes.CONTENT_TYPE_SONIFICATION); audioAttributesBuilder.setUsage(AudioAttributes.USAGE_NOTIFICATION_COMMUNICATION_INSTANT); @@ -517,7 +519,7 @@ public class NotificationWorker extends Worker { AudioAttributes.Builder audioAttributesBuilder = new AudioAttributes.Builder().setContentType (AudioAttributes.CONTENT_TYPE_SONIFICATION); - if (decryptedPushMessage.getType().equals("chat") || decryptedPushMessage.getType().equals("room")) { + if (CHAT.equals(decryptedPushMessage.getType()) || ROOM.equals(decryptedPushMessage.getType())) { audioAttributesBuilder.setUsage(AudioAttributes.USAGE_NOTIFICATION_COMMUNICATION_INSTANT); } else { audioAttributesBuilder.setUsage(AudioAttributes.USAGE_NOTIFICATION_COMMUNICATION_REQUEST); diff --git a/app/src/main/java/com/nextcloud/talk/utils/DisplayUtils.java b/app/src/main/java/com/nextcloud/talk/utils/DisplayUtils.java index 8e4fabf98..a0b1fc9da 100644 --- a/app/src/main/java/com/nextcloud/talk/utils/DisplayUtils.java +++ b/app/src/main/java/com/nextcloud/talk/utils/DisplayUtils.java @@ -2,7 +2,7 @@ * Nextcloud Talk application * * @author Mario Danic - * Copyright (C) 2017 Mario Danic + * Copyright (C) 2017-2020 Mario Danic * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -21,6 +21,7 @@ package com.nextcloud.talk.utils; import android.annotation.SuppressLint; +import android.app.Activity; import android.content.Context; import android.content.Intent; import android.content.res.ColorStateList; @@ -28,6 +29,7 @@ import android.content.res.Configuration; import android.content.res.Resources; import android.graphics.Bitmap; import android.graphics.Canvas; +import android.graphics.Color; import android.graphics.Typeface; import android.graphics.drawable.Animatable; import android.graphics.drawable.BitmapDrawable; @@ -49,7 +51,10 @@ import android.util.Log; import android.util.TypedValue; import android.view.View; import android.view.ViewGroup; +import android.view.Window; +import android.view.WindowManager; import android.widget.EditText; +import android.widget.ImageView; import android.widget.TextView; import androidx.annotation.ColorInt; @@ -58,10 +63,15 @@ import androidx.annotation.DrawableRes; import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.annotation.XmlRes; +import androidx.appcompat.app.AppCompatDelegate; import androidx.appcompat.widget.AppCompatDrawableManager; +import androidx.appcompat.widget.SearchView; import androidx.core.content.ContextCompat; +import androidx.core.content.res.ResourcesCompat; +import androidx.core.graphics.ColorUtils; import androidx.core.graphics.drawable.DrawableCompat; import androidx.emoji.text.EmojiCompat; +import androidx.viewpager.widget.ViewPager; import com.facebook.common.executors.UiThreadImmediateExecutorService; import com.facebook.common.references.CloseableReference; @@ -101,6 +111,9 @@ public class DisplayUtils { private static final String TAG = "DisplayUtils"; + private static final int INDEX_LUMINATION = 2; + private static final double MAX_LIGHTNESS = 0.92; + public static void setClickableString(String string, String url, TextView textView) { SpannableString spannableString = new SpannableString(string); spannableString.setSpan(new ClickableSpan() { @@ -305,7 +318,6 @@ public class DisplayUtils { return chip; } - public static Spannable searchAndReplaceWithMentionSpan(Context context, Spannable text, String id, String label, String type, UserEntity conversationUser, @@ -342,7 +354,6 @@ public class DisplayUtils { } return spannableString; - } public static Spannable searchAndColor(Spannable text, String searchText, @ColorInt int color) { @@ -395,20 +406,69 @@ public class DisplayUtils { return drawable; } - public static boolean isDarkModeActive(Context context, AppPreferences prefs) { - if (prefs.getTheme().equals("night_yes")) { - return true; - } - - int currentNightMode = - context.getResources().getConfiguration().uiMode & Configuration.UI_MODE_NIGHT_MASK; - switch (currentNightMode) { - case Configuration.UI_MODE_NIGHT_NO: - return false; - case Configuration.UI_MODE_NIGHT_YES: - return true; - default: - return false; + /** + * Sets the color of the status bar to {@code color}. + * + * @param activity activity + * @param color the color + */ + public static void applyColorToStatusBar(Activity activity, @ColorInt int color) { + Window window = activity.getWindow(); + boolean isLightTheme = lightTheme(color); + if (window != null) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { + View decor = window.getDecorView(); + if (isLightTheme) { + int systemUiFlagLightStatusBar; + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + systemUiFlagLightStatusBar = View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR | View.SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR; + } else { + systemUiFlagLightStatusBar = View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR; + } + decor.setSystemUiVisibility(systemUiFlagLightStatusBar); + } else { + decor.setSystemUiVisibility(0); + } + window.setStatusBarColor(color); + } else if (isLightTheme) { + window.setStatusBarColor(Color.BLACK); + } } } + + /** + * Tests if light color is set + * + * @param color the color + * @return true if primaryColor is lighter than MAX_LIGHTNESS + */ + public static boolean lightTheme(int color) { + float[] hsl = colorToHSL(color); + + return hsl[INDEX_LUMINATION] >= MAX_LIGHTNESS; + } + + private static float[] colorToHSL(int color) { + float[] hsl = new float[3]; + ColorUtils.RGBToHSL(Color.red(color), Color.green(color), Color.blue(color), hsl); + + return hsl; + } + + public static void applyColorToNavigationBar(Window window, @ColorInt int color) { + window.setNavigationBarColor(color); + } + + /** + * Theme search view + * + * @param searchView searchView to be changed + * @param context the app's context + */ + public static void themeSearchView(SearchView searchView, Context context) { + // hacky as no default way is provided + SearchView.SearchAutoComplete editText = searchView.findViewById(R.id.search_src_text); + editText.setTextSize(16); + editText.setHintTextColor(context.getResources().getColor(R.color.fontSecondaryAppbar)); + } } diff --git a/app/src/main/res/animator/appbar_elevation_off.xml b/app/src/main/res/animator/appbar_elevation_off.xml new file mode 100644 index 000000000..cb24f4936 --- /dev/null +++ b/app/src/main/res/animator/appbar_elevation_off.xml @@ -0,0 +1,11 @@ + + + + + + diff --git a/app/src/main/res/animator/appbar_elevation_on.xml b/app/src/main/res/animator/appbar_elevation_on.xml new file mode 100644 index 000000000..6bd52cf3b --- /dev/null +++ b/app/src/main/res/animator/appbar_elevation_on.xml @@ -0,0 +1,11 @@ + + + + + + diff --git a/app/src/main/res/drawable-night/ic_close_search.xml b/app/src/main/res/drawable-night/ic_close_search.xml new file mode 100644 index 000000000..4a6ccaf6d --- /dev/null +++ b/app/src/main/res/drawable-night/ic_close_search.xml @@ -0,0 +1,23 @@ + + + + diff --git a/app/src/main/res/drawable/ic_close_search.xml b/app/src/main/res/drawable/ic_close_search.xml new file mode 100644 index 000000000..ec5d46b00 --- /dev/null +++ b/app/src/main/res/drawable/ic_close_search.xml @@ -0,0 +1,23 @@ + + + + diff --git a/app/src/main/res/drawable/ic_menu.xml b/app/src/main/res/drawable/ic_menu.xml new file mode 100644 index 000000000..6d9343b31 --- /dev/null +++ b/app/src/main/res/drawable/ic_menu.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/ic_search_grey.xml b/app/src/main/res/drawable/ic_search_grey.xml new file mode 100644 index 000000000..2302badec --- /dev/null +++ b/app/src/main/res/drawable/ic_search_grey.xml @@ -0,0 +1,23 @@ + + + + diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml index 93822f260..7b110651c 100644 --- a/app/src/main/res/layout/activity_main.xml +++ b/app/src/main/res/layout/activity_main.xml @@ -2,7 +2,9 @@ ~ Nextcloud Talk application ~ ~ @author Mario Danic - ~ Copyright (C) 2017-2019 Mario Danic + ~ @author Andy Scherzinger + ~ Copyright (C) 2021 Andy Scherzinger + ~ Copyright (C) 2017-2020 Mario Danic ~ ~ This program is free software: you can redistribute it and/or modify ~ it under the terms of the GNU General Public License as published by @@ -21,28 +23,59 @@ + + + + + + + + + + + + - - - - - diff --git a/app/src/main/res/layout/controller_account_verification.xml b/app/src/main/res/layout/controller_account_verification.xml index a38bf7863..54e781d65 100644 --- a/app/src/main/res/layout/controller_account_verification.xml +++ b/app/src/main/res/layout/controller_account_verification.xml @@ -22,7 +22,7 @@ xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" - android:background="@color/nc_white_color" + android:background="@color/bg_default" android:keepScreenOn="true"> diff --git a/app/src/main/res/layout/controller_browser.xml b/app/src/main/res/layout/controller_browser.xml index 09a5f89a7..399d64d82 100644 --- a/app/src/main/res/layout/controller_browser.xml +++ b/app/src/main/res/layout/controller_browser.xml @@ -39,6 +39,8 @@ android:layout_width="match_parent" android:layout_height="64dp" android:background="@color/bg_default" + app:itemTextColor="@color/fg_default" + app:itemIconTint="@color/fg_default" app:menu="@menu/file_browser_path" /> + tools:text="Voice Call"/> - + android:background="@color/bg_default"> + android:animateLayoutChanges="true" + android:background="@color/bg_default"> diff --git a/app/src/main/res/layout/controller_generic_rv.xml b/app/src/main/res/layout/controller_generic_rv.xml index 7bd0ad51e..7bedcaae3 100644 --- a/app/src/main/res/layout/controller_generic_rv.xml +++ b/app/src/main/res/layout/controller_generic_rv.xml @@ -19,6 +19,7 @@ --> - - + + diff --git a/app/src/main/res/layout/controller_locked.xml b/app/src/main/res/layout/controller_locked.xml index 933709361..cdd1c5d17 100644 --- a/app/src/main/res/layout/controller_locked.xml +++ b/app/src/main/res/layout/controller_locked.xml @@ -49,5 +49,5 @@ android:padding="16dp" android:text="@string/nc_locked" android:textAlignment="center" - android:textColor="@color/white" /> + android:textColor="@color/fg_inverse" /> diff --git a/app/src/main/res/layout/controller_operations_menu.xml b/app/src/main/res/layout/controller_operations_menu.xml index f825fdacb..ce17af3ca 100644 --- a/app/src/main/res/layout/controller_operations_menu.xml +++ b/app/src/main/res/layout/controller_operations_menu.xml @@ -21,7 +21,7 @@ + android:background="@color/bg_default"> @@ -84,7 +84,7 @@ android:layout_marginEnd="8dp" android:layout_marginBottom="12dp" android:layout_toStartOf="@id/ok_button" - android:background="#0000" + android:background="@color/bg_inverse" android:text="@string/nc_join_via_web" android:textColor="@color/nc_darkGreen" android:visibility="gone" /> diff --git a/app/src/main/res/layout/rv_item_app.xml b/app/src/main/res/layout/rv_item_app.xml index ef72a3e7c..ce5270ef9 100644 --- a/app/src/main/res/layout/rv_item_app.xml +++ b/app/src/main/res/layout/rv_item_app.xml @@ -22,7 +22,7 @@ xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="wrap_content" - android:background="@color/white" + android:background="@color/bg_default" android:paddingTop="16dp" android:paddingBottom="16dp"> @@ -46,7 +46,7 @@ android:layout_toEndOf="@id/icon_image_view" android:focusable="false" android:focusableInTouchMode="false" - android:textColor="@color/black" + android:textColor="@color/fg_default" android:textSize="16sp" tools:text="Start a new conversation" /> diff --git a/app/src/main/res/layout/rv_item_browser_file.xml b/app/src/main/res/layout/rv_item_browser_file.xml index 0734d74ef..32ad6be52 100644 --- a/app/src/main/res/layout/rv_item_browser_file.xml +++ b/app/src/main/res/layout/rv_item_browser_file.xml @@ -43,7 +43,7 @@ android:layout_below="@id/file_icon" android:layout_alignEnd="@id/file_icon" android:src="@drawable/ic_star_black_24dp" - android:tint="@color/grey_600" /> + app:tint="@color/grey_600" /> + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/menu/menu_conversation_plus_filter.xml b/app/src/main/res/menu/menu_conversation_plus_filter.xml index a1924e010..828b7202a 100644 --- a/app/src/main/res/menu/menu_conversation_plus_filter.xml +++ b/app/src/main/res/menu/menu_conversation_plus_filter.xml @@ -28,13 +28,6 @@ android:icon="@drawable/ic_search_white_24dp" app:showAsAction="collapseActionView|always" android:animateLayoutChanges="true" - app:actionViewClass="androidx.appcompat.widget.SearchView" - /> - - - + app:actionViewClass="androidx.appcompat.widget.SearchView" /> diff --git a/app/src/main/res/values-night/colors.xml b/app/src/main/res/values-night/colors.xml index 466a70ac2..85576dd37 100644 --- a/app/src/main/res/values-night/colors.xml +++ b/app/src/main/res/values-night/colors.xml @@ -24,10 +24,22 @@ --> + #0082C9 + #006AA3 + @color/colorPrimary + + + #1E1E1E + #FFFFFF + #deffffff #121212 - #121212 + @color/grey950 + + #FFFFFF + #121212 + #00AA00 #373737 diff --git a/app/src/main/res/values/colors.xml b/app/src/main/res/values/colors.xml index c674ac8af..a1a6bf137 100644 --- a/app/src/main/res/values/colors.xml +++ b/app/src/main/res/values/colors.xml @@ -23,8 +23,15 @@ #0082C9 + #006AA3 + @color/colorPrimary #ffffff + + @android:color/white + #666666 + #A5A5A5 + #FFFFFF @@ -37,7 +44,6 @@ #D32F2F #006400 - @color/per70white #E8E8E8 #757575 #D5D5D5 @@ -48,10 +54,11 @@ #61000000 + #666666 #FFFFFF #FFFFFF - @color/white60 + @color/grey950 #333333 #EFEFEF diff --git a/app/src/main/res/values/dimens.xml b/app/src/main/res/values/dimens.xml index 60c27fda3..f97047a89 100644 --- a/app/src/main/res/values/dimens.xml +++ b/app/src/main/res/values/dimens.xml @@ -30,6 +30,7 @@ 8dp 16dp 40dp + 30dp 96dp @dimen/avatar_fetching_size_very_big 180dp @@ -42,4 +43,5 @@ 80dp 16dp + 24dp diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 5965ab8b1..5c1138899 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -307,7 +307,6 @@ You are not allowed to re-share this file - Webinar Lobby Start time @@ -358,8 +357,12 @@ Chat via %s Account not found + + Search in %s + M3.27,4.27L19.74,20.74 999+ + Open main menu diff --git a/app/src/main/res/values/styles.xml b/app/src/main/res/values/styles.xml index f27cfa3ed..360edf86b 100644 --- a/app/src/main/res/values/styles.xml +++ b/app/src/main/res/values/styles.xml @@ -4,7 +4,7 @@ ~ @author Mario Danic ~ @author Andy Scherzinger ~ Copyright (C) 2021 Andy Scherzinger - ~ Copyright (C) 2017-2019 Mario Danic + ~ Copyright (C) 2017-2020 Mario Danic ~ ~ This program is free software: you can redistribute it and/or modify ~ it under the terms of the GNU General Public License as published by @@ -26,15 +26,16 @@ - - - - - - + + + diff --git a/scripts/analysis/lint-results.txt b/scripts/analysis/lint-results.txt index 5d104dc4f..af1d42380 100644 --- a/scripts/analysis/lint-results.txt +++ b/scripts/analysis/lint-results.txt @@ -1,2 +1,2 @@ DO NOT TOUCH; GENERATED BY DRONE - Lint Report: 3 errors and 131 warnings + Lint Report: 3 errors and 130 warnings