diff --git a/android-app/app/build.gradle b/android-app/app/build.gradle index 0524bddb..d567cc63 100644 --- a/android-app/app/build.gradle +++ b/android-app/app/build.gradle @@ -53,8 +53,8 @@ android { applicationId "arun.com.chromer" minSdkVersion 16 targetSdkVersion 28 - versionCode 53 - versionName "2.1" + versionCode 54 + versionName "2.1.1" multiDexEnabled true diff --git a/android-app/app/src/main/java/arun/com/chromer/browsing/article/BrowsingArticleViewModel.kt b/android-app/app/src/main/java/arun/com/chromer/browsing/article/BrowsingArticleViewModel.kt index 15cffd05..864f3fd9 100644 --- a/android-app/app/src/main/java/arun/com/chromer/browsing/article/BrowsingArticleViewModel.kt +++ b/android-app/app/src/main/java/arun/com/chromer/browsing/article/BrowsingArticleViewModel.kt @@ -26,7 +26,6 @@ import arun.com.chromer.data.webarticle.WebArticleRepository import arun.com.chromer.data.webarticle.model.WebArticle import arun.com.chromer.search.provider.SearchProvider import arun.com.chromer.search.provider.SearchProviders -import arun.com.chromer.settings.RxPreferences import arun.com.chromer.util.SchedulerProvider import io.reactivex.Observable import rx.subjects.PublishSubject @@ -40,9 +39,7 @@ class BrowsingArticleViewModel @Inject constructor( private val webArticleRepository: WebArticleRepository, - private val schedulerProvider: dev.arunkumar.android.rxschedulers.SchedulerProvider, - private val searchProviders: SearchProviders, - private val rxPreferences: RxPreferences + searchProviders: SearchProviders ) : ViewModel() { private val subs = CompositeSubscription() @@ -60,21 +57,7 @@ constructor( }.subscribe { articleLiveData.value = it }) } - val selectedSearchProvider: Observable = rxPreferences - .searchEngine - .observe() - .observeOn(schedulerProvider.pool) - .switchMap { selectedEngine -> - searchProviders - .availableProviders - .toObservable() - .flatMapIterable { it } - .filter { it.name == selectedEngine } - .first(SearchProviders.GOOGLE_SEARCH_PROVIDER) - .toObservable() - }.replay(1) - .refCount() - .observeOn(schedulerProvider.ui) + val selectedSearchProvider: Observable = searchProviders.selectedProvider fun loadArticle(url: String) = loadingQueue.onNext(url) diff --git a/android-app/app/src/main/java/arun/com/chromer/browsing/backgroundloading/ArticleBackgroundLoadingStrategy.kt b/android-app/app/src/main/java/arun/com/chromer/browsing/backgroundloading/ArticleBackgroundLoadingStrategy.kt index 501ea199..8359dd9f 100644 --- a/android-app/app/src/main/java/arun/com/chromer/browsing/backgroundloading/ArticleBackgroundLoadingStrategy.kt +++ b/android-app/app/src/main/java/arun/com/chromer/browsing/backgroundloading/ArticleBackgroundLoadingStrategy.kt @@ -11,7 +11,7 @@ class ArticleBackgroundLoadingStrategy @Inject constructor(private val articlePreloader: ArticlePreloader) : BackgroundLoadingStrategy { - override fun perform(url: String) { + override fun prepare(url: String) { articlePreloader.preloadArticle(Uri.parse(url)) { success -> Timber.d("Article mode preloading for $url: $success") } diff --git a/android-app/app/src/main/java/arun/com/chromer/browsing/backgroundloading/BackgroundLoadingStrategy.kt b/android-app/app/src/main/java/arun/com/chromer/browsing/backgroundloading/BackgroundLoadingStrategy.kt index 3c0b5986..ef5ab0be 100644 --- a/android-app/app/src/main/java/arun/com/chromer/browsing/backgroundloading/BackgroundLoadingStrategy.kt +++ b/android-app/app/src/main/java/arun/com/chromer/browsing/backgroundloading/BackgroundLoadingStrategy.kt @@ -2,5 +2,5 @@ package arun.com.chromer.browsing.backgroundloading interface BackgroundLoadingStrategy { - fun perform(url: String) + fun prepare(url: String) } \ No newline at end of file diff --git a/android-app/app/src/main/java/arun/com/chromer/browsing/backgroundloading/BrowsingBackgroundLoadingStrategy.kt b/android-app/app/src/main/java/arun/com/chromer/browsing/backgroundloading/BrowsingBackgroundLoadingStrategy.kt index 64f2db59..45f40f07 100644 --- a/android-app/app/src/main/java/arun/com/chromer/browsing/backgroundloading/BrowsingBackgroundLoadingStrategy.kt +++ b/android-app/app/src/main/java/arun/com/chromer/browsing/backgroundloading/BrowsingBackgroundLoadingStrategy.kt @@ -2,13 +2,14 @@ package arun.com.chromer.browsing.backgroundloading import android.app.Activity import android.app.Application +import android.os.Handler +import android.os.Looper import arun.com.chromer.browsing.customtabs.CustomTabActivity import arun.com.chromer.browsing.webview.EmbeddableWebViewActivity import arun.com.chromer.browsing.webview.WebViewActivity import arun.com.chromer.util.ActivityLifeCycleCallbackAdapter import dev.arunkumar.android.rxschedulers.SchedulerProvider import timber.log.Timber -import java.util.concurrent.TimeUnit.MILLISECONDS import javax.inject.Inject import javax.inject.Singleton @@ -19,6 +20,7 @@ private fun Application.safeActivityStarted(action: (Activity, UnRegisterAction) override fun onActivityStarted(activity: Activity?) { super.onActivityStarted(activity) val unRegisterAction: UnRegisterAction = { + Timber.i("Unregistering lifecycle callbacks") unregisterActivityLifecycleCallbacks(this) } activity?.let { startedActivity -> @@ -40,17 +42,19 @@ abstract class BrowsingBackgroundLoadingStrategy( abstract val activityClasses: List> - override fun perform(url: String) { + override fun prepare(url: String) { application.safeActivityStarted { startedActivity, unRegisterAction -> if (activityClasses.any { it.isAssignableFrom(startedActivity.javaClass) }) { val activityUrl = startedActivity.intent?.dataString if (url == activityUrl) { - schedulerProvider.ui.scheduleDirect({ + Handler(Looper.getMainLooper()).postDelayed({ startedActivity.moveTaskToBack(true) Timber.d("Moved $activityUrl to back") unRegisterAction() - }, 150, MILLISECONDS) + }, 1000) } + } else { + Timber.d("No match for $startedActivity") } } } diff --git a/android-app/app/src/main/java/arun/com/chromer/browsing/shareintercept/ShareInterceptActivity.kt b/android-app/app/src/main/java/arun/com/chromer/browsing/shareintercept/ShareInterceptActivity.kt index 881caaaa..53ec28a2 100644 --- a/android-app/app/src/main/java/arun/com/chromer/browsing/shareintercept/ShareInterceptActivity.kt +++ b/android-app/app/src/main/java/arun/com/chromer/browsing/shareintercept/ShareInterceptActivity.kt @@ -21,25 +21,31 @@ package arun.com.chromer.browsing.shareintercept import android.annotation.SuppressLint import android.annotation.TargetApi -import android.content.Intent +import android.content.Intent.* import android.os.Build import android.os.Bundle import android.widget.Toast import arun.com.chromer.R import arun.com.chromer.data.website.model.Website import arun.com.chromer.di.activity.ActivityComponent -import arun.com.chromer.shared.Constants +import arun.com.chromer.search.provider.SearchProviders import arun.com.chromer.shared.base.activity.BaseActivity import arun.com.chromer.tabs.TabsManager import arun.com.chromer.util.SafeIntent import arun.com.chromer.util.Utils +import io.reactivex.rxkotlin.subscribeBy +import timber.log.Timber import javax.inject.Inject @SuppressLint("GoogleAppIndexingApiWarning") class ShareInterceptActivity : BaseActivity() { + override fun getLayoutRes() = 0 + @Inject lateinit var tabsManager: TabsManager + @Inject + lateinit var searchProviders: SearchProviders @TargetApi(Build.VERSION_CODES.M) override fun onCreate(savedInstanceState: Bundle?) { @@ -49,12 +55,14 @@ class ShareInterceptActivity : BaseActivity() { val action = safeIntent.action var text: String? = null when (action) { - Intent.ACTION_SEND -> if (safeIntent.hasExtra(Intent.EXTRA_TEXT)) { - text = intent.extras?.getCharSequence(Intent.EXTRA_TEXT)?.toString() + ACTION_SEND -> if (safeIntent.hasExtra(EXTRA_TEXT)) { + text = intent.extras?.getCharSequence(EXTRA_TEXT)?.toString() } - Intent.ACTION_PROCESS_TEXT -> text = safeIntent.getStringExtra(Intent.EXTRA_PROCESS_TEXT) + ACTION_PROCESS_TEXT -> text = safeIntent.getStringExtra(EXTRA_PROCESS_TEXT) + } + if (text?.isNotEmpty() == true) { + findAndOpenLink(text) } - findAndOpenLink(text) } catch (exception: Exception) { invalidLink() } finally { @@ -66,27 +74,29 @@ class ShareInterceptActivity : BaseActivity() { activityComponent.inject(this) } - override fun getLayoutRes(): Int = 0 - - - private fun findAndOpenLink(receivedText: String?) { - var text: String? = receivedText ?: return - val urls = Utils.findURLs(text) - if (!urls.isEmpty()) { + @SuppressLint("CheckResult") + private fun findAndOpenLink(receivedText: String) { + val urls = Utils.findURLs(receivedText) + if (urls.isNotEmpty()) { // use only the first link val url = urls[0] openLink(url) } else { - // No urls were found, so lets do a google search with the text received. - text = Constants.G_SEARCH_URL + text!!.replace(" ", "+") - openLink(text) + searchProviders.selectedProvider + .firstOrError() + .map { it.getSearchUrl(receivedText) } + .subscribeBy( + onSuccess = ::openLink, + onError = Timber::e + ) } } private fun openLink(url: String?) { - if (url != null) { - tabsManager.openUrl(this, website = Website(url)) - } else invalidLink() + when (url) { + null -> invalidLink() + else -> tabsManager.openUrl(this, website = Website(url)) + } finish() } diff --git a/android-app/app/src/main/java/arun/com/chromer/home/HomeActivityViewModel.kt b/android-app/app/src/main/java/arun/com/chromer/home/HomeActivityViewModel.kt index 3cfe2e78..406c1050 100644 --- a/android-app/app/src/main/java/arun/com/chromer/home/HomeActivityViewModel.kt +++ b/android-app/app/src/main/java/arun/com/chromer/home/HomeActivityViewModel.kt @@ -9,6 +9,7 @@ import arun.com.chromer.data.website.model.Website import arun.com.chromer.extenstions.StringResource import arun.com.chromer.extenstions.appName import arun.com.chromer.home.epoxycontroller.model.CustomTabProviderInfo +import arun.com.chromer.settings.Preferences import arun.com.chromer.settings.RxPreferences import arun.com.chromer.shared.Constants import arun.com.chromer.util.glide.appicon.ApplicationIcon @@ -27,7 +28,8 @@ constructor( private val application: Application, private val rxPreferences: RxPreferences, private val schedulerProvider: SchedulerProvider, - private val historyRepository: HistoryRepository + private val historyRepository: HistoryRepository, + private val preferences: Preferences ) : RxViewModel() { val providerInfoLiveData = MutableLiveData() @@ -51,7 +53,12 @@ constructor( private fun bindProviderInfo() { Observable.combineLatest( - rxPreferences.customTabProviderPref.observe(), + rxPreferences.customTabProviderPref.observe().map { packageName -> + when { + packageName.isEmpty() -> preferences.defaultCustomTabApp ?: "" + else -> packageName + } + }, rxPreferences.incognitoPref.observe(), rxPreferences.webviewPref.observe(), Function3 { customTabProvider: String, isIncognito: Boolean, isWebView: Boolean -> diff --git a/android-app/app/src/main/java/arun/com/chromer/home/TabsLifecycleObserver.kt b/android-app/app/src/main/java/arun/com/chromer/home/TabsLifecycleObserver.kt index aaeb560c..e509ecfb 100644 --- a/android-app/app/src/main/java/arun/com/chromer/home/TabsLifecycleObserver.kt +++ b/android-app/app/src/main/java/arun/com/chromer/home/TabsLifecycleObserver.kt @@ -1,13 +1,16 @@ package arun.com.chromer.home import androidx.lifecycle.LifecycleOwner +import arun.com.chromer.data.website.WebsiteRepository import arun.com.chromer.di.scopes.PerActivity import arun.com.chromer.tabs.TabsManager import arun.com.chromer.util.lifecycle.ActivityLifecycle import arun.com.chromer.util.lifecycle.LifecycleEvents import dev.arunkumar.android.rxschedulers.SchedulerProvider -import hu.akarnokd.rxjava.interop.RxJavaInterop +import hu.akarnokd.rxjava.interop.RxJavaInterop.toV2Observable +import hu.akarnokd.rxjava.interop.RxJavaInterop.toV2Single import io.reactivex.Observable +import io.reactivex.rxkotlin.toObservable import java.util.concurrent.TimeUnit import javax.inject.Inject @@ -18,14 +21,28 @@ constructor( @ActivityLifecycle lifecycleOwner: LifecycleOwner, private val tabsManager: TabsManager, - private val schedulerProvider: SchedulerProvider + private val schedulerProvider: SchedulerProvider, + private val websiteRepository: WebsiteRepository ) : LifecycleEvents(lifecycleOwner) { fun activeTabs(): Observable> = starts.flatMap { Observable.interval(750, TimeUnit.MILLISECONDS) - .flatMapSingle { - RxJavaInterop.toV2Single(tabsManager.getActiveTabs()) - }.startWith(RxJavaInterop.toV2Single(tabsManager.getActiveTabs()).toObservable()) + .flatMapSingle { toV2Single(tabsManager.getActiveTabs()) } + .startWith(toV2Single(tabsManager.getActiveTabs()).toObservable()) .distinctUntilChanged() + .switchMap { tabs -> + tabs.toObservable() + .flatMap { tab -> + toV2Observable(websiteRepository.getWebsiteReadOnly(tab.url) + .map { website -> tab.copy(website = website) }) + .sorted { o1, o2 -> + val createdAt = o1.website?.createdAt ?: 0 + val createdAt2 = o2.website?.createdAt ?: 0 + createdAt.compareTo(createdAt2) + } + }.toList() + .toObservable() + .startWith(tabs) + } .takeUntil(stops) .compose(schedulerProvider.poolToUi()) } diff --git a/android-app/app/src/main/java/arun/com/chromer/home/epoxycontroller/model/TabModel.kt b/android-app/app/src/main/java/arun/com/chromer/home/epoxycontroller/model/TabModel.kt new file mode 100644 index 00000000..94be98bf --- /dev/null +++ b/android-app/app/src/main/java/arun/com/chromer/home/epoxycontroller/model/TabModel.kt @@ -0,0 +1,27 @@ +package arun.com.chromer.home.epoxycontroller.model + +import arun.com.chromer.R +import arun.com.chromer.data.website.model.Website +import arun.com.chromer.tabs.TabsManager +import arun.com.chromer.util.glide.GlideApp +import com.airbnb.epoxy.EpoxyAttribute +import com.airbnb.epoxy.EpoxyModelClass +import dev.arunkumar.android.epoxy.model.KotlinEpoxyModelWithHolder +import dev.arunkumar.android.epoxy.model.KotlinHolder +import kotlinx.android.synthetic.main.widget_tab_model_preview.* + +@EpoxyModelClass(layout = R.layout.widget_tab_model_preview) +abstract class TabModel : KotlinEpoxyModelWithHolder() { + @EpoxyAttribute + lateinit var tab: TabsManager.Tab + + override fun bind(holder: ViewHolder) { + super.bind(holder) + GlideApp.with(holder.containerView.context) + .load(tab.website ?: Website(tab.url)) + .circleCrop() + .into(holder.icon) + } + + class ViewHolder : KotlinHolder() +} \ No newline at end of file diff --git a/android-app/app/src/main/java/arun/com/chromer/home/epoxycontroller/model/TabsInfoModel.kt b/android-app/app/src/main/java/arun/com/chromer/home/epoxycontroller/model/TabsInfoModel.kt index c4afebe4..fe6caee2 100644 --- a/android-app/app/src/main/java/arun/com/chromer/home/epoxycontroller/model/TabsInfoModel.kt +++ b/android-app/app/src/main/java/arun/com/chromer/home/epoxycontroller/model/TabsInfoModel.kt @@ -1,6 +1,11 @@ package arun.com.chromer.home.epoxycontroller.model +import androidx.recyclerview.widget.DefaultItemAnimator +import androidx.recyclerview.widget.LinearLayoutManager +import androidx.recyclerview.widget.RecyclerView import arun.com.chromer.R +import arun.com.chromer.extenstions.gone +import arun.com.chromer.extenstions.show import arun.com.chromer.tabs.TabsManager import com.airbnb.epoxy.EpoxyAttribute import com.airbnb.epoxy.EpoxyAttribute.Option.DoNotHash @@ -16,7 +21,7 @@ abstract class TabsInfoModel : KotlinEpoxyModelWithHolder + tab { + id(tab.hashCode()) + tab(tab) } } - revealed = true } - }*/ + } } class ViewHolder : KotlinHolder() diff --git a/android-app/app/src/main/java/arun/com/chromer/search/provider/SearchProviders.kt b/android-app/app/src/main/java/arun/com/chromer/search/provider/SearchProviders.kt index 607ed0de..3bf5c8ef 100644 --- a/android-app/app/src/main/java/arun/com/chromer/search/provider/SearchProviders.kt +++ b/android-app/app/src/main/java/arun/com/chromer/search/provider/SearchProviders.kt @@ -3,6 +3,9 @@ package arun.com.chromer.search.provider import android.net.Uri import android.util.Patterns.WEB_URL import androidx.core.net.toUri +import arun.com.chromer.settings.RxPreferences +import dev.arunkumar.android.rxschedulers.SchedulerProvider +import io.reactivex.Observable import io.reactivex.Single import java.util.* import javax.inject.Inject @@ -25,7 +28,11 @@ data class SearchProvider( @Singleton class SearchProviders -@Inject constructor() { +@Inject +constructor( + rxPreferences: RxPreferences, + schedulerProvider: SchedulerProvider +) { val availableProviders: Single> = Single.fromCallable { listOf( @@ -48,6 +55,21 @@ class SearchProviders ) } + val selectedProvider: Observable = rxPreferences + .searchEngine + .observe() + .observeOn(schedulerProvider.pool) + .switchMap { selectedEngine -> + availableProviders + .toObservable() + .flatMapIterable { it } + .filter { it.name == selectedEngine } + .first(GOOGLE_SEARCH_PROVIDER) + .toObservable() + }.replay(1) + .refCount() + .observeOn(schedulerProvider.ui) + companion object { const val GOOGLE = "Google" const val DUCKDUCKGO = "Duck Duck Go" diff --git a/android-app/app/src/main/java/arun/com/chromer/search/view/MaterialSearchView.kt b/android-app/app/src/main/java/arun/com/chromer/search/view/MaterialSearchView.kt index da8daf87..fd82bf55 100644 --- a/android-app/app/src/main/java/arun/com/chromer/search/view/MaterialSearchView.kt +++ b/android-app/app/src/main/java/arun/com/chromer/search/view/MaterialSearchView.kt @@ -96,13 +96,13 @@ constructor( .color(normalColor) .sizeDp(16) } - private val voiceIcon: IconicsDrawable by lazy { + private val voiceIcon: IconicsDrawable by lazy { IconicsDrawable(context) .icon(CommunityMaterial.Icon.cmd_microphone) .color(normalColor) .sizeDp(18) } - private val menuIcon: IconicsDrawable by lazy { + private val menuIcon: IconicsDrawable by lazy { IconicsDrawable(context) .icon(CommunityMaterial.Icon.cmd_menu) .color(normalColor) diff --git a/android-app/app/src/main/java/arun/com/chromer/search/view/SearchPresenter.kt b/android-app/app/src/main/java/arun/com/chromer/search/view/SearchPresenter.kt index 7c3d2e3d..bc21935b 100644 --- a/android-app/app/src/main/java/arun/com/chromer/search/view/SearchPresenter.kt +++ b/android-app/app/src/main/java/arun/com/chromer/search/view/SearchPresenter.kt @@ -5,7 +5,6 @@ import arun.com.chromer.di.scopes.PerView import arun.com.chromer.di.view.Detaches import arun.com.chromer.search.provider.SearchProvider import arun.com.chromer.search.provider.SearchProviders -import arun.com.chromer.search.provider.SearchProviders.Companion.GOOGLE_SEARCH_PROVIDER import arun.com.chromer.search.suggestion.SuggestionsEngine import arun.com.chromer.search.suggestion.items.SuggestionItem import arun.com.chromer.search.suggestion.items.SuggestionType @@ -58,19 +57,7 @@ constructor( .subscribeOn(schedulerProvider.pool) .share() - val selectedSearchProvider: Observable = rxPreferences - .searchEngine - .observe() - .observeOn(schedulerProvider.pool) - .switchMap { selectedEngine -> - searchEngines.flatMapIterable { it } - .filter { it.name == selectedEngine } - .first(GOOGLE_SEARCH_PROVIDER) - .toObservable() - }.replay(1) - .refCount() - .observeOn(schedulerProvider.ui) - + val selectedSearchProvider: Observable = searchProviders.selectedProvider fun getSearchUrl(searchUrl: String): Observable { return selectedSearchProvider.take(1) diff --git a/android-app/app/src/main/java/arun/com/chromer/settings/Preferences.java b/android-app/app/src/main/java/arun/com/chromer/settings/Preferences.java index f10048a1..94cac5f7 100644 --- a/android-app/app/src/main/java/arun/com/chromer/settings/Preferences.java +++ b/android-app/app/src/main/java/arun/com/chromer/settings/Preferences.java @@ -180,7 +180,7 @@ public String customTabPackage() { } @Nullable - private String getDefaultCustomTabApp() { + public String getDefaultCustomTabApp() { if (CustomTabs.isPackageSupportCustomTabs(context, CHROME_PACKAGE)) return CHROME_PACKAGE; final List supportingPackages = CustomTabs.getCustomTabSupportingPackages(context); @@ -417,14 +417,13 @@ public void perAppSettings(final boolean preference) { @SuppressWarnings("BooleanMethodIsAlwaysInverted") public boolean mergeTabs() { - return Utils.isLollipopAbove() && getDefaultSharedPreferences().getBoolean(MERGE_TABS_AND_APPS, false); + return Utils.isLollipopAbove() && getDefaultSharedPreferences().getBoolean(MERGE_TABS_AND_APPS, true); } public void mergeTabs(final boolean preference) { getDefaultSharedPreferences().edit().putBoolean(MERGE_TABS_AND_APPS, preference).apply(); } - @SuppressWarnings("BooleanMethodIsAlwaysInverted") public boolean bottomBar() { return getDefaultSharedPreferences().getBoolean(BOTTOM_BAR_ENABLED, true); } diff --git a/android-app/app/src/main/java/arun/com/chromer/tabs/DefaultTabsManager.kt b/android-app/app/src/main/java/arun/com/chromer/tabs/DefaultTabsManager.kt index fc1c100f..a99c7f3d 100644 --- a/android-app/app/src/main/java/arun/com/chromer/tabs/DefaultTabsManager.kt +++ b/android-app/app/src/main/java/arun/com/chromer/tabs/DefaultTabsManager.kt @@ -34,7 +34,7 @@ import android.os.Handler import android.os.Looper import android.widget.Toast import androidx.annotation.ColorInt -import androidx.core.content.ContextCompat +import androidx.core.content.ContextCompat.getColor import arun.com.chromer.BuildConfig import arun.com.chromer.R import arun.com.chromer.appdetect.AppDetectionManager @@ -352,14 +352,14 @@ constructor( // If this command was not issued for minimizing, then attempt aggressive loading. if (preferences.aggressiveLoading() && !fromMinimize) { when { - preferences.articleMode() -> backgroundLoadingStrategyFactory[ARTICLE].perform(url) + preferences.articleMode() -> backgroundLoadingStrategyFactory[ARTICLE].prepare(url) else -> { if (shouldUseWebView) { if (!bubbles) { - backgroundLoadingStrategyFactory[WEB_VIEW].perform(url) + backgroundLoadingStrategyFactory[WEB_VIEW].prepare(url) } } else { - backgroundLoadingStrategyFactory[CUSTOM_TAB].perform(url) + backgroundLoadingStrategyFactory[CUSTOM_TAB].prepare(url) } openBrowsingTab( context, @@ -449,9 +449,10 @@ constructor( } } if (preferences.dynamicToolbarOnWeb()) { - websiteColor = websiteRepository.getWebsiteColorSync(website.url) - if (websiteColor == NO_COLOR) { - websiteColor = website.themeColor() + websiteColor = if (website.themeColor() != NO_COLOR) { + website.themeColor() + } else { + websiteRepository.getWebsiteColorSync(website.url) } } return when { @@ -462,7 +463,7 @@ constructor( } else { return preferences.toolbarColor() } - } else return ContextCompat.getColor(application, R.color.primary) + } else return getColor(application, R.color.primary) } diff --git a/android-app/app/src/main/java/arun/com/chromer/util/glide/favicon/WebsiteDecoder.kt b/android-app/app/src/main/java/arun/com/chromer/util/glide/favicon/WebsiteDecoder.kt index b87da946..ce5f8a87 100644 --- a/android-app/app/src/main/java/arun/com/chromer/util/glide/favicon/WebsiteDecoder.kt +++ b/android-app/app/src/main/java/arun/com/chromer/util/glide/favicon/WebsiteDecoder.kt @@ -19,14 +19,16 @@ package arun.com.chromer.util.glide.favicon +import android.annotation.SuppressLint import android.content.Context import android.graphics.* +import android.graphics.Bitmap.Config.ARGB_8888 import android.graphics.Paint.ANTI_ALIAS_FLAG import androidx.annotation.ColorInt import arun.com.chromer.data.website.model.Website import arun.com.chromer.shared.Constants import arun.com.chromer.util.ColorUtil -import arun.com.chromer.util.Utils +import arun.com.chromer.util.Utils.* import arun.com.chromer.util.glide.GlideApp import com.bumptech.glide.Glide import com.bumptech.glide.load.Options @@ -35,19 +37,20 @@ import com.bumptech.glide.load.engine.Resource import com.bumptech.glide.load.engine.bitmap_recycle.BitmapPool import com.bumptech.glide.load.resource.bitmap.BitmapResource import timber.log.Timber -import java.util.* class WebsiteDecoder(private val context: Context, glide: Glide) : ResourceDecoder { private val bitmapPool: BitmapPool = glide.bitmapPool - val size = Utils.dpToPx(56.0) + private val size = dpToPx(56.0) private val placeholderColors = intArrayOf( - Color.parseColor("#D32F2F"), - Color.parseColor("#C2185B"), - Color.parseColor("#303F9F"), - Color.parseColor("#6A1B9A"), - Color.parseColor("#37474F"), - Color.parseColor("#2E7D32") + Color.parseColor("#3891A6"), + Color.parseColor("#FDE74C"), + Color.parseColor("#DB5461"), + Color.parseColor("#EC4E20"), + Color.parseColor("#6EEB83"), + Color.parseColor("#29335C"), + Color.parseColor("#BDE4A8"), + Color.parseColor("#A491D3") ) override fun handles(source: Website, options: Options): Boolean = true @@ -65,14 +68,14 @@ class WebsiteDecoder(private val context: Context, glide: Glide) : ResourceDecod null } return try { - if (Utils.isValidFavicon(websiteFavicon)) { + if (isValidFavicon(websiteFavicon)) { BitmapResource.obtain(websiteFavicon!!.copy(websiteFavicon.config, true), bitmapPool) } else { // Draw a placeholder using theme color if it exists, else use a random color. val color = if (website.themeColor() != Constants.NO_COLOR) { website.themeColor() } else { - placeholderColors[Random().nextInt(placeholderColors.size)] + placeholderColors.random() } val createdIcon = createPlaceholderImage(color, website.safeLabel()) BitmapResource.obtain(createdIcon.copy(createdIcon.config, true), bitmapPool) @@ -83,24 +86,29 @@ class WebsiteDecoder(private val context: Context, glide: Glide) : ResourceDecod } } + @SuppressLint("DefaultLocale") private fun createPlaceholderImage(@ColorInt color: Int, label: String): Bitmap { - val icon = bitmapPool.get(size, size, Bitmap.Config.ARGB_8888) + val icon = bitmapPool.get(size, size, ARGB_8888) val canvas = Canvas(icon) - val shadowRadius = Utils.dpToPx(1.8).toFloat() - val shadowDx = Utils.dpToPx(0.1).toFloat() - val shadowDy = Utils.dpToPx(1.0).toFloat() - val textSize = Utils.dpToPx(24.0).toFloat() + val shadowRadius = dpToPx(1.8).toFloat() + val shadowDx = dpToPx(0.1).toFloat() + val shadowDy = dpToPx(0.8).toFloat() + val textSize = dpToPx(24.0).toFloat() val bgPaint = Paint(ANTI_ALIAS_FLAG).apply { style = Paint.Style.FILL setColor(color) - setShadowLayer(shadowRadius, shadowDx, shadowDy, Color.parseColor("#44000000")) + setShadowLayer(shadowRadius, shadowDx, shadowDy, Color.parseColor("#33000000")) } - val padding = Utils.dpToPx(1.0) - val corner = Utils.dpToPx(3.0) - canvas.drawRoundRect(RectF(padding.toFloat(), padding.toFloat(), (size - padding).toFloat(), (size - padding).toFloat()), - corner.toFloat(), corner.toFloat(), bgPaint) + val padding = dpToPx(1.0) + + canvas.drawCircle( + (size / 2).toFloat() - padding / 2, + (size / 2).toFloat() - padding / 2, + (size / 2).toFloat() - padding, + bgPaint + ) val textPaint = Paint(ANTI_ALIAS_FLAG).apply { typeface = Typeface.defaultFromStyle(Typeface.NORMAL) @@ -108,7 +116,7 @@ class WebsiteDecoder(private val context: Context, glide: Glide) : ResourceDecod setColor(ColorUtil.getForegroundWhiteOrBlack(color)) style = Paint.Style.FILL } - drawTextInCanvasCentre(canvas, textPaint, Utils.getFirstLetter(label).toUpperCase()) + drawTextInCanvasCentre(canvas, textPaint, getFirstLetter(label).toUpperCase()) return icon } diff --git a/android-app/app/src/main/res/layout/activity_main.xml b/android-app/app/src/main/res/layout/activity_main.xml index 49b1a722..f7f26316 100644 --- a/android-app/app/src/main/res/layout/activity_main.xml +++ b/android-app/app/src/main/res/layout/activity_main.xml @@ -48,9 +48,12 @@ android:paddingBottom="8dp" app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager" app:layout_constraintBottom_toBottomOf="parent" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toBottomOf="@+id/lynketHeader" tools:itemCount="5" - tools:listitem="@layout/layout_provider_info_card" /> + tools:listitem="@layout/layout_provider_info_card" + tools:visibility="visible" /> + + + + + + + \ No newline at end of file diff --git a/android-app/app/src/main/res/values/changelog.xml b/android-app/app/src/main/res/values/changelog.xml index dbbd0911..93e29a7e 100644 --- a/android-app/app/src/main/res/values/changelog.xml +++ b/android-app/app/src/main/res/values/changelog.xml @@ -36,6 +36,13 @@ a { You can view this again in the About screen.

+2.1.1 +
    +
  • FIX: Open/Search with Lynket option not using selected search engine
  • +
  • FIX: Aggressive background loading - sometimes tab not minimizing automatically.
  • +
  • IMPROVED: Use Vibrant palette for placeholder icons
  • +
  • IMPROVED: Preview tab icons in home screen
  • +
2.1

General

    diff --git a/android-app/app/src/main/res/values/colors.xml b/android-app/app/src/main/res/values/colors.xml index 12b34a4f..4b321c18 100644 --- a/android-app/app/src/main/res/values/colors.xml +++ b/android-app/app/src/main/res/values/colors.xml @@ -63,4 +63,6 @@ #4c4c4c #727272 #20232C + + #266DD3 diff --git a/android-app/build.gradle b/android-app/build.gradle index eb10bf5e..058d2de6 100644 --- a/android-app/build.gradle +++ b/android-app/build.gradle @@ -19,7 +19,7 @@ apply plugin: 'lynket-build' buildscript { - ext.kotlin_version = '1.3.41' + ext.kotlin_version = '1.3.50' repositories { google() maven { url "https://jitpack.io" } diff --git a/android-app/gradle/wrapper/gradle-wrapper.properties b/android-app/gradle/wrapper/gradle-wrapper.properties index a4e84c19..a54e4230 100644 --- a/android-app/gradle/wrapper/gradle-wrapper.properties +++ b/android-app/gradle/wrapper/gradle-wrapper.properties @@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-5.6.1-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-5.6.2-all.zip