From ecc504779d6b0a586436dc140690bceb6ee77dea Mon Sep 17 00:00:00 2001 From: VaiTon Date: Tue, 27 Jul 2021 16:17:09 +0200 Subject: [PATCH] ref: minors, use receiver args fix: fixed NPE in ProductEditNutritionFactsFragment.kt --- .../openfood/features/ImagesManageActivity.kt | 3 - .../scrachx/openfood/features/MainActivity.kt | 43 ++-- .../allergensalert/AllergensAlertFragment.kt | 4 +- .../compare/ProductCompareActivity.kt | 27 +-- .../openfood/features/login/LoginActivity.kt | 4 +- .../product/edit/ProductEditActivity.kt | 11 +- .../ProductEditNutritionFactsFragment.kt | 12 +- .../product/view/ProductViewActivity.kt | 6 +- .../nutrition/NutritionProductFragment.kt | 4 +- .../productlist/ProductListActivity.kt | 4 - .../productlist/ProductListAdapter.kt | 45 ++-- .../features/scan/ContinuousScanActivity.kt | 6 +- .../scanhistory/ScanHistoryActivity.kt | 10 +- .../features/search/ProductSearchActivity.kt | 37 +--- .../searchbycode/SearchByCodeFragment.kt | 8 +- .../openfood/features/shared/BaseActivity.kt | 25 ++- .../openfood/network/OpenFoodAPIClient.kt | 209 +++++++++--------- .../openfood/network/services/ProductsAPI.kt | 10 +- .../scrachx/openfood/utils/ContextExt.kt | 8 + .../github/scrachx/openfood/utils/Utils.kt | 44 ++-- 20 files changed, 241 insertions(+), 279 deletions(-) diff --git a/app/src/main/java/openfoodfacts/github/scrachx/openfood/features/ImagesManageActivity.kt b/app/src/main/java/openfoodfacts/github/scrachx/openfood/features/ImagesManageActivity.kt index 1e5bdacd8556..ca50bab02acf 100644 --- a/app/src/main/java/openfoodfacts/github/scrachx/openfood/features/ImagesManageActivity.kt +++ b/app/src/main/java/openfoodfacts/github/scrachx/openfood/features/ImagesManageActivity.kt @@ -59,7 +59,6 @@ import openfoodfacts.github.scrachx.openfood.models.Product import openfoodfacts.github.scrachx.openfood.models.ProductImageField import openfoodfacts.github.scrachx.openfood.models.findByCode import openfoodfacts.github.scrachx.openfood.network.ApiFields -import openfoodfacts.github.scrachx.openfood.network.OpenFoodAPIClient import openfoodfacts.github.scrachx.openfood.network.services.ProductsAPI import openfoodfacts.github.scrachx.openfood.utils.* import openfoodfacts.github.scrachx.openfood.utils.FileDownloader.download @@ -83,8 +82,6 @@ class ImagesManageActivity : BaseActivity() { @Inject lateinit var productsApi: ProductsAPI - @Inject - lateinit var client: OpenFoodAPIClient @Inject lateinit var picasso: Picasso diff --git a/app/src/main/java/openfoodfacts/github/scrachx/openfood/features/MainActivity.kt b/app/src/main/java/openfoodfacts/github/scrachx/openfood/features/MainActivity.kt index 24e4a0b51c52..33caaf70b73c 100644 --- a/app/src/main/java/openfoodfacts/github/scrachx/openfood/features/MainActivity.kt +++ b/app/src/main/java/openfoodfacts/github/scrachx/openfood/features/MainActivity.kt @@ -35,8 +35,9 @@ import android.widget.SearchView import android.widget.Toast import androidx.appcompat.app.AlertDialog import androidx.browser.customtabs.CustomTabsIntent -import androidx.core.app.ActivityCompat +import androidx.core.app.ActivityCompat.shouldShowRequestPermissionRationale import androidx.core.content.ContextCompat +import androidx.core.content.ContextCompat.checkSelfPermission import androidx.core.content.edit import androidx.core.net.toUri import androidx.fragment.app.Fragment @@ -60,6 +61,7 @@ import com.mikepenz.materialdrawer.model.interfaces.IProfile import dagger.hilt.android.AndroidEntryPoint import io.reactivex.disposables.CompositeDisposable import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.Job import kotlinx.coroutines.launch import kotlinx.coroutines.rx2.await import kotlinx.coroutines.withContext @@ -117,9 +119,7 @@ import openfoodfacts.github.scrachx.openfood.utils.NavigationDrawerListener.Comp import openfoodfacts.github.scrachx.openfood.utils.NavigationDrawerListener.Companion.ITEM_SEARCH_BY_CODE import openfoodfacts.github.scrachx.openfood.utils.NavigationDrawerListener.Companion.ITEM_USER import openfoodfacts.github.scrachx.openfood.utils.NavigationDrawerListener.Companion.ITEM_YOUR_LISTS -import openfoodfacts.github.scrachx.openfood.utils.Utils.hideKeyboard import openfoodfacts.github.scrachx.openfood.utils.Utils.isApplicationInstalled -import openfoodfacts.github.scrachx.openfood.utils.Utils.isNetworkConnected import openfoodfacts.github.scrachx.openfood.utils.Utils.scheduleProductUploadJob import java.io.FileNotFoundException import java.io.IOException @@ -163,6 +163,9 @@ class MainActivity : BaseActivity(), NavigationDrawerListener { private var searchMenuItem: MenuItem? = null private var userSettingsURI: Uri? = null + private var historySyncJob: Job? = null + + private val loginThenUpdate = registerForActivityResult(LoginContract()) { isLoggedIn -> if (isLoggedIn) updateConnectedState() } private val loginThenOpenContributions = registerForActivityResult(LoginContract()) @@ -177,7 +180,7 @@ class MainActivity : BaseActivity(), NavigationDrawerListener { _binding = ActivityMainBinding.inflate(layoutInflater) setContentView(binding.root) - hideKeyboard(this) + hideKeyboard() setSupportActionBar(binding.toolbarInclude.toolbar) supportActionBar?.setDisplayHomeAsUpEnabled(false) swapToFragment(HomeFragment.newInstance()) @@ -244,8 +247,8 @@ class MainActivity : BaseActivity(), NavigationDrawerListener { .withHasStableIds(true) .withAccountHeader(headerResult) //set the AccountHeader we created earlier for the header .withOnDrawerListener(object : Drawer.OnDrawerListener { - override fun onDrawerSlide(drawerView: View, slideOffset: Float) = hideKeyboard(this@MainActivity) - override fun onDrawerOpened(drawerView: View) = hideKeyboard(this@MainActivity) + override fun onDrawerSlide(drawerView: View, slideOffset: Float) = hideKeyboard() + override fun onDrawerOpened(drawerView: View) = hideKeyboard() override fun onDrawerClosed(drawerView: View) = Unit }) .addDrawerItems( @@ -432,12 +435,16 @@ class MainActivity : BaseActivity(), NavigationDrawerListener { // Adds nutriscore and quantity values in old history for schema 5 update val mSharedPref = getSharedPreferences("prefs", 0) + val isOldHistoryDataSynced = mSharedPref.getBoolean("is_old_history_data_synced", false) - if (!isOldHistoryDataSynced && isNetworkConnected(this)) { - apiClient.syncOldHistory() + if (!isOldHistoryDataSynced && this.isNetworkConnected()) { + historySyncJob?.cancel() + historySyncJob = lifecycleScope.launch { apiClient.syncOldHistory() } } + binding.bottomNavigationInclude.bottomNavigation.selectNavigationItem(0) binding.bottomNavigationInclude.bottomNavigation.installBottomNavigation(this) + handleIntent(intent) if (isFlavors(OFF)) { @@ -457,24 +464,24 @@ class MainActivity : BaseActivity(), NavigationDrawerListener { } private fun checkThenStartScanActivity() { - if (ContextCompat.checkSelfPermission(this@MainActivity, Manifest.permission.CAMERA) != - PackageManager.PERMISSION_GRANTED - ) { - if (ActivityCompat.shouldShowRequestPermissionRationale(this@MainActivity, Manifest.permission.CAMERA)) { - MaterialAlertDialogBuilder(this@MainActivity) + when { + checkSelfPermission(this, Manifest.permission.CAMERA) == PackageManager.PERMISSION_GRANTED -> { + startScanActivity() + } + shouldShowRequestPermissionRationale(this, Manifest.permission.CAMERA) -> { + MaterialAlertDialogBuilder(this) .setTitle(R.string.action_about) .setMessage(R.string.permission_camera) .setPositiveButton(android.R.string.ok) { _, _ -> requestCameraThenOpenScan.launch(Manifest.permission.CAMERA) } .show() - - } else { + } + else -> { requestCameraThenOpenScan.launch(Manifest.permission.CAMERA) } - } else { - startScanActivity() } + } private fun updateProfileForCurrentUser() { @@ -848,7 +855,7 @@ class MainActivity : BaseActivity(), NavigationDrawerListener { if (tempBarcode.isNotEmpty()) { dialog.dismiss() - if (isNetworkConnected(this@MainActivity)) { + if (this@MainActivity.isNetworkConnected()) { contentResolver.openInputStream(selected)!!.use { val image = ProductImage( tempBarcode, diff --git a/app/src/main/java/openfoodfacts/github/scrachx/openfood/features/allergensalert/AllergensAlertFragment.kt b/app/src/main/java/openfoodfacts/github/scrachx/openfood/features/allergensalert/AllergensAlertFragment.kt index 65b9073393aa..76058bd9cd63 100644 --- a/app/src/main/java/openfoodfacts/github/scrachx/openfood/features/allergensalert/AllergensAlertFragment.kt +++ b/app/src/main/java/openfoodfacts/github/scrachx/openfood/features/allergensalert/AllergensAlertFragment.kt @@ -39,7 +39,7 @@ import openfoodfacts.github.scrachx.openfood.repositories.ProductRepository import openfoodfacts.github.scrachx.openfood.utils.LocaleManager import openfoodfacts.github.scrachx.openfood.utils.NavigationDrawerListener import openfoodfacts.github.scrachx.openfood.utils.NavigationDrawerListener.NavigationDrawerType -import openfoodfacts.github.scrachx.openfood.utils.Utils +import openfoodfacts.github.scrachx.openfood.utils.isNetworkConnected import javax.inject.Inject /** @@ -146,7 +146,7 @@ class AllergensAlertFragment : NavigationBaseFragment() { }.show() } - } else if (Utils.isNetworkConnected(requireContext())) { + } else if (requireContext().isNetworkConnected()) { val lt = LoadToast(context) .setText(requireActivity().getString(R.string.toast_retrieving)) .setBackgroundColor(ResourcesCompat.getColor(requireContext().resources, R.color.blue, requireContext().theme)) diff --git a/app/src/main/java/openfoodfacts/github/scrachx/openfood/features/compare/ProductCompareActivity.kt b/app/src/main/java/openfoodfacts/github/scrachx/openfood/features/compare/ProductCompareActivity.kt index 1d34dc74e455..6608f72cbdd9 100644 --- a/app/src/main/java/openfoodfacts/github/scrachx/openfood/features/compare/ProductCompareActivity.kt +++ b/app/src/main/java/openfoodfacts/github/scrachx/openfood/features/compare/ProductCompareActivity.kt @@ -29,12 +29,8 @@ import openfoodfacts.github.scrachx.openfood.listeners.CommonBottomListenerInsta import openfoodfacts.github.scrachx.openfood.listeners.CommonBottomListenerInstaller.selectNavigationItem import openfoodfacts.github.scrachx.openfood.models.Product import openfoodfacts.github.scrachx.openfood.models.ProductImageField -import openfoodfacts.github.scrachx.openfood.network.OpenFoodAPIClient import openfoodfacts.github.scrachx.openfood.repositories.ProductRepository -import openfoodfacts.github.scrachx.openfood.utils.LocaleManager -import openfoodfacts.github.scrachx.openfood.utils.PhotoReceiverHandler -import openfoodfacts.github.scrachx.openfood.utils.Utils -import openfoodfacts.github.scrachx.openfood.utils.isHardwareCameraInstalled +import openfoodfacts.github.scrachx.openfood.utils.* import java.util.* import javax.inject.Inject import kotlin.collections.ArrayList @@ -46,8 +42,6 @@ class ProductCompareActivity : BaseActivity() { private val viewModel: ProductCompareViewModel by viewModels() - @Inject - lateinit var client: OpenFoodAPIClient @Inject lateinit var productRepository: ProductRepository @@ -118,24 +112,7 @@ class ProductCompareActivity : BaseActivity() { fullProductClickListener = { val barcode = it.code - if (Utils.isNetworkConnected(activity)) { - Utils.hideKeyboard(activity) - - client.openProduct(barcode, activity) - } else { - MaterialAlertDialogBuilder(activity) - .setTitle(R.string.device_offline_dialog_title) - .setMessage(R.string.connectivity_check) - .setPositiveButton(R.string.txt_try_again) { _, _ -> - if (Utils.isNetworkConnected(activity)) { - client.openProduct(barcode, activity) - } else { - Toast.makeText(activity, R.string.device_offline_dialog_title, Toast.LENGTH_SHORT).show() - } - } - .setNegativeButton(R.string.dismiss) { d, _ -> d.dismiss() } - .show() - } + openProduct(barcode) } } diff --git a/app/src/main/java/openfoodfacts/github/scrachx/openfood/features/login/LoginActivity.kt b/app/src/main/java/openfoodfacts/github/scrachx/openfood/features/login/LoginActivity.kt index d8740269cc3e..809ead5364fa 100644 --- a/app/src/main/java/openfoodfacts/github/scrachx/openfood/features/login/LoginActivity.kt +++ b/app/src/main/java/openfoodfacts/github/scrachx/openfood/features/login/LoginActivity.kt @@ -48,8 +48,8 @@ import openfoodfacts.github.scrachx.openfood.customtabs.WebViewFallback import openfoodfacts.github.scrachx.openfood.databinding.ActivityLoginBinding import openfoodfacts.github.scrachx.openfood.features.shared.BaseActivity import openfoodfacts.github.scrachx.openfood.network.services.ProductsAPI -import openfoodfacts.github.scrachx.openfood.utils.Utils import openfoodfacts.github.scrachx.openfood.utils.getLoginPreferences +import openfoodfacts.github.scrachx.openfood.utils.hideKeyboard import java.io.IOException import java.net.HttpCookie import javax.inject.Inject @@ -87,7 +87,7 @@ class LoginActivity : BaseActivity() { // Disable login button viewModel.canLogIn.postValue(false) - Utils.hideKeyboard(this) + hideKeyboard() // Start checks val login = binding.loginInput.text.toString() diff --git a/app/src/main/java/openfoodfacts/github/scrachx/openfood/features/product/edit/ProductEditActivity.kt b/app/src/main/java/openfoodfacts/github/scrachx/openfood/features/product/edit/ProductEditActivity.kt index 958c8fdfa86d..ac1a5ff07f69 100644 --- a/app/src/main/java/openfoodfacts/github/scrachx/openfood/features/product/edit/ProductEditActivity.kt +++ b/app/src/main/java/openfoodfacts/github/scrachx/openfood/features/product/edit/ProductEditActivity.kt @@ -66,11 +66,7 @@ import openfoodfacts.github.scrachx.openfood.network.OpenFoodAPIClient import openfoodfacts.github.scrachx.openfood.network.OpenFoodAPIClient.Companion.PNG_EXT import openfoodfacts.github.scrachx.openfood.network.OpenFoodAPIClient.Companion.addToHistory import openfoodfacts.github.scrachx.openfood.network.services.ProductsAPI -import openfoodfacts.github.scrachx.openfood.utils.OfflineProductService -import openfoodfacts.github.scrachx.openfood.utils.Utils.hideKeyboard -import openfoodfacts.github.scrachx.openfood.utils.clearCameraCache -import openfoodfacts.github.scrachx.openfood.utils.getLoginPreferences -import openfoodfacts.github.scrachx.openfood.utils.getProductState +import openfoodfacts.github.scrachx.openfood.utils.* import java.io.IOException import java.util.* import javax.inject.Inject @@ -89,9 +85,6 @@ class ProductEditActivity : BaseActivity() { @Inject lateinit var daoSession: DaoSession - @Inject - lateinit var client: OpenFoodAPIClient - @Inject lateinit var productsApi: ProductsAPI @@ -330,7 +323,7 @@ class ProductEditActivity : BaseActivity() { scheduleProductUpload(this, sharedPreferences) Toast.makeText(this, R.string.productSavedToast, Toast.LENGTH_SHORT).show() - hideKeyboard(this) + hideKeyboard() // Report analytics if (editingMode) { diff --git a/app/src/main/java/openfoodfacts/github/scrachx/openfood/features/product/edit/nutrition/ProductEditNutritionFactsFragment.kt b/app/src/main/java/openfoodfacts/github/scrachx/openfood/features/product/edit/nutrition/ProductEditNutritionFactsFragment.kt index 760f489c4b79..c77a3cf469f3 100644 --- a/app/src/main/java/openfoodfacts/github/scrachx/openfood/features/product/edit/nutrition/ProductEditNutritionFactsFragment.kt +++ b/app/src/main/java/openfoodfacts/github/scrachx/openfood/features/product/edit/nutrition/ProductEditNutritionFactsFragment.kt @@ -32,6 +32,7 @@ import android.widget.AdapterView.OnItemSelectedListener import androidx.core.net.toFile import androidx.core.widget.doAfterTextChanged import androidx.fragment.app.viewModels +import androidx.lifecycle.Lifecycle import androidx.lifecycle.lifecycleScope import com.afollestad.materialdialogs.MaterialDialog import com.google.android.material.textfield.TextInputLayout @@ -402,8 +403,15 @@ class ProductEditNutritionFactsFragment : ProductEditFragment() { .resize(requireContext().dpsToPixel(50), requireContext().dpsToPixel(50)) .centerInside() .into(binding.btnAddImageNutritionFacts, object : Callback { - override fun onSuccess() = afterNutritionImgLoaded() - override fun onError(ex: Exception) = afterNutritionImgLoaded() + override fun onSuccess() { + if (!lifecycle.currentState.isAtLeast(Lifecycle.State.STARTED)) return + afterNutritionImgLoaded() + } + + override fun onError(ex: Exception) { + if (!lifecycle.currentState.isAtLeast(Lifecycle.State.STARTED)) return + afterNutritionImgLoaded() + } }) } diff --git a/app/src/main/java/openfoodfacts/github/scrachx/openfood/features/product/view/ProductViewActivity.kt b/app/src/main/java/openfoodfacts/github/scrachx/openfood/features/product/view/ProductViewActivity.kt index c2ba074c6128..4c8630dfc9d2 100644 --- a/app/src/main/java/openfoodfacts/github/scrachx/openfood/features/product/view/ProductViewActivity.kt +++ b/app/src/main/java/openfoodfacts/github/scrachx/openfood/features/product/view/ProductViewActivity.kt @@ -56,7 +56,6 @@ import openfoodfacts.github.scrachx.openfood.listeners.CommonBottomListenerInsta import openfoodfacts.github.scrachx.openfood.listeners.OnRefreshListener import openfoodfacts.github.scrachx.openfood.models.ProductState import openfoodfacts.github.scrachx.openfood.models.eventbus.ProductNeedsRefreshEvent -import openfoodfacts.github.scrachx.openfood.network.OpenFoodAPIClient import openfoodfacts.github.scrachx.openfood.utils.Utils import openfoodfacts.github.scrachx.openfood.utils.requireProductState import org.greenrobot.eventbus.EventBus @@ -68,9 +67,6 @@ class ProductViewActivity : BaseActivity(), IProductView, OnRefreshListener { private var _binding: ActivityProductBinding? = null private val binding get() = _binding!! - @Inject - lateinit var client: OpenFoodAPIClient - @Inject lateinit var sharedPreferences: SharedPreferences @@ -157,7 +153,7 @@ class ProductViewActivity : BaseActivity(), IProductView, OnRefreshListener { } override fun onRefresh() { - client.openProduct(productState!!.product!!.code, this) + openProduct(productState!!.product!!.code) } override fun onNewIntent(intent: Intent) { diff --git a/app/src/main/java/openfoodfacts/github/scrachx/openfood/features/product/view/nutrition/NutritionProductFragment.kt b/app/src/main/java/openfoodfacts/github/scrachx/openfood/features/product/view/nutrition/NutritionProductFragment.kt index 9ff2472961be..7864960e3273 100644 --- a/app/src/main/java/openfoodfacts/github/scrachx/openfood/features/product/view/nutrition/NutritionProductFragment.kt +++ b/app/src/main/java/openfoodfacts/github/scrachx/openfood/features/product/view/nutrition/NutritionProductFragment.kt @@ -548,7 +548,9 @@ class NutritionProductFragment : BaseFragment(), CustomTabActivityHelper.Connect val dialog = MaterialDialog.Builder(requireActivity()).run { title(R.string.calculate_nutrition_facts) customView(R.layout.dialog_calculate_calories, false) - dismissListener { Utils.hideKeyboard(requireActivity()) } + dismissListener { + requireActivity().hideKeyboard() + } build() }.apply { show() } diff --git a/app/src/main/java/openfoodfacts/github/scrachx/openfood/features/productlist/ProductListActivity.kt b/app/src/main/java/openfoodfacts/github/scrachx/openfood/features/productlist/ProductListActivity.kt index 680b94c80da5..16cc3f07a435 100644 --- a/app/src/main/java/openfoodfacts/github/scrachx/openfood/features/productlist/ProductListActivity.kt +++ b/app/src/main/java/openfoodfacts/github/scrachx/openfood/features/productlist/ProductListActivity.kt @@ -38,7 +38,6 @@ import openfoodfacts.github.scrachx.openfood.models.HistoryProductDao import openfoodfacts.github.scrachx.openfood.models.Product import openfoodfacts.github.scrachx.openfood.models.entities.ListedProduct import openfoodfacts.github.scrachx.openfood.models.entities.ProductLists -import openfoodfacts.github.scrachx.openfood.network.OpenFoodAPIClient import openfoodfacts.github.scrachx.openfood.utils.* import openfoodfacts.github.scrachx.openfood.utils.SortType.* import java.io.File @@ -52,9 +51,6 @@ class ProductListActivity : BaseActivity(), SwipeController.Actions { private var _binding: ActivityYourListedProductsBinding? = null private val binding get() = _binding!! - @Inject - lateinit var client: OpenFoodAPIClient - @Inject lateinit var daoSession: DaoSession diff --git a/app/src/main/java/openfoodfacts/github/scrachx/openfood/features/productlist/ProductListAdapter.kt b/app/src/main/java/openfoodfacts/github/scrachx/openfood/features/productlist/ProductListAdapter.kt index 6321f6b7ab9b..1f651392ccb0 100644 --- a/app/src/main/java/openfoodfacts/github/scrachx/openfood/features/productlist/ProductListAdapter.kt +++ b/app/src/main/java/openfoodfacts/github/scrachx/openfood/features/productlist/ProductListAdapter.kt @@ -12,6 +12,7 @@ import androidx.core.content.res.ResourcesCompat import androidx.recyclerview.widget.RecyclerView import com.squareup.picasso.Callback import com.squareup.picasso.Picasso +import kotlinx.coroutines.runBlocking import openfoodfacts.github.scrachx.openfood.R import openfoodfacts.github.scrachx.openfood.features.productlist.ProductListAdapter.ProductViewHolder import openfoodfacts.github.scrachx.openfood.features.shared.views.CustomTextView @@ -19,15 +20,17 @@ import openfoodfacts.github.scrachx.openfood.models.entities.ListedProduct import openfoodfacts.github.scrachx.openfood.network.OpenFoodAPIClient class ProductListAdapter( - private val context: Context, - private val client: OpenFoodAPIClient, - val products: MutableList, - private val isLowBatteryMode: Boolean + private val context: Context, + private val client: OpenFoodAPIClient, + val products: MutableList, + private val isLowBatteryMode: Boolean ) : RecyclerView.Adapter() { override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) = - ProductViewHolder(LayoutInflater.from(context) - .inflate(R.layout.your_listed_products_item, parent, false)) + ProductViewHolder( + LayoutInflater.from(context) + .inflate(R.layout.your_listed_products_item, parent, false) + ) override fun onBindViewHolder(holder: ProductViewHolder, position: Int) { holder.imgProgressBar.visibility = View.VISIBLE @@ -38,26 +41,28 @@ class ProductListAdapter( if (!isLowBatteryMode && products[position].imageUrl.isNotEmpty()) { Picasso.get() - .load(products[position].imageUrl) - .placeholder(R.drawable.placeholder_thumb) - .error(R.drawable.ic_no_red_24dp) - .fit() - .centerCrop() - .into(holder.imgProduct, object : Callback { - override fun onSuccess() { - holder.imgProgressBar.visibility = View.GONE - } + .load(products[position].imageUrl) + .placeholder(R.drawable.placeholder_thumb) + .error(R.drawable.ic_no_red_24dp) + .fit() + .centerCrop() + .into(holder.imgProduct, object : Callback { + override fun onSuccess() { + holder.imgProgressBar.visibility = View.GONE + } - override fun onError(ex: Exception) { - holder.imgProgressBar.visibility = View.GONE - } - }) + override fun onError(ex: Exception) { + holder.imgProgressBar.visibility = View.GONE + } + }) } else { holder.imgProduct.background = ResourcesCompat.getDrawable(context.resources, R.drawable.placeholder_thumb, context.theme) holder.imgProgressBar.visibility = View.INVISIBLE } holder.itemView.setOnClickListener { - client.openProduct(products[position].barcode, it.context as Activity) + val activity = it.context as Activity + runBlocking { client.openProduct(products[position].barcode, activity) } + } } diff --git a/app/src/main/java/openfoodfacts/github/scrachx/openfood/features/scan/ContinuousScanActivity.kt b/app/src/main/java/openfoodfacts/github/scrachx/openfood/features/scan/ContinuousScanActivity.kt index 7126df0aa427..2315d76a78cd 100644 --- a/app/src/main/java/openfoodfacts/github/scrachx/openfood/features/scan/ContinuousScanActivity.kt +++ b/app/src/main/java/openfoodfacts/github/scrachx/openfood/features/scan/ContinuousScanActivity.kt @@ -88,7 +88,6 @@ import openfoodfacts.github.scrachx.openfood.models.entities.allergen.AllergenNa import openfoodfacts.github.scrachx.openfood.models.entities.analysistagconfig.AnalysisTagConfig import openfoodfacts.github.scrachx.openfood.models.eventbus.ProductNeedsRefreshEvent import openfoodfacts.github.scrachx.openfood.network.ApiFields.StateTags -import openfoodfacts.github.scrachx.openfood.network.OpenFoodAPIClient import openfoodfacts.github.scrachx.openfood.repositories.ProductRepository import openfoodfacts.github.scrachx.openfood.utils.* import org.greenrobot.eventbus.EventBus @@ -103,9 +102,6 @@ class ContinuousScanActivity : BaseActivity(), IProductView { private var _binding: ActivityContinuousScanBinding? = null internal val binding get() = _binding!! - @Inject - lateinit var client: OpenFoodAPIClient - @Inject lateinit var daoSession: DaoSession @@ -791,7 +787,7 @@ class ContinuousScanActivity : BaseActivity(), IProductView { // When user search from "having trouble" edit text if (actionId != EditorInfo.IME_ACTION_SEARCH) return false - Utils.hideKeyboard(this@ContinuousScanActivity) + hideKeyboard() hideSystemUI() // Check for barcode validity diff --git a/app/src/main/java/openfoodfacts/github/scrachx/openfood/features/scanhistory/ScanHistoryActivity.kt b/app/src/main/java/openfoodfacts/github/scrachx/openfood/features/scanhistory/ScanHistoryActivity.kt index ef53baa1f9f4..5484f505f95f 100644 --- a/app/src/main/java/openfoodfacts/github/scrachx/openfood/features/scanhistory/ScanHistoryActivity.kt +++ b/app/src/main/java/openfoodfacts/github/scrachx/openfood/features/scanhistory/ScanHistoryActivity.kt @@ -40,7 +40,6 @@ import openfoodfacts.github.scrachx.openfood.features.productlist.CreateCSVContr import openfoodfacts.github.scrachx.openfood.features.shared.BaseActivity import openfoodfacts.github.scrachx.openfood.listeners.CommonBottomListenerInstaller.installBottomNavigation import openfoodfacts.github.scrachx.openfood.listeners.CommonBottomListenerInstaller.selectNavigationItem -import openfoodfacts.github.scrachx.openfood.network.OpenFoodAPIClient import openfoodfacts.github.scrachx.openfood.utils.* import openfoodfacts.github.scrachx.openfood.utils.SortType.* import java.io.File @@ -55,9 +54,6 @@ class ScanHistoryActivity : BaseActivity() { private lateinit var binding: ActivityHistoryScanBinding private val viewModel: ScanHistoryViewModel by viewModels() - @Inject - lateinit var client: OpenFoodAPIClient - @Inject lateinit var picasso: Picasso @@ -71,7 +67,7 @@ class ScanHistoryActivity : BaseActivity() { private val adapter by lazy { ScanHistoryAdapter(isLowBatteryMode = isDisableImageLoad() && isBatteryLevelLow(), picasso) { - openProductActivity(it.barcode) + openProduct(it.barcode) } } @@ -240,10 +236,6 @@ class ScanHistoryActivity : BaseActivity() { invalidateOptionsMenu() } - private fun openProductActivity(barcode: String) { - client.openProduct(barcode, this) - } - private fun exportAsCSV() { Toast.makeText(this, R.string.txt_exporting_history, Toast.LENGTH_LONG).show() diff --git a/app/src/main/java/openfoodfacts/github/scrachx/openfood/features/search/ProductSearchActivity.kt b/app/src/main/java/openfoodfacts/github/scrachx/openfood/features/search/ProductSearchActivity.kt index 49419f27dd51..c97ee97fdad7 100644 --- a/app/src/main/java/openfoodfacts/github/scrachx/openfood/features/search/ProductSearchActivity.kt +++ b/app/src/main/java/openfoodfacts/github/scrachx/openfood/features/search/ProductSearchActivity.kt @@ -10,9 +10,7 @@ import android.util.Log import android.view.Menu import android.view.MenuItem import android.view.View -import android.view.inputmethod.InputMethodManager import android.widget.SearchView -import android.widget.Toast import androidx.annotation.StringRes import androidx.browser.customtabs.CustomTabsIntent import androidx.core.app.ActivityCompat.shouldShowRequestPermissionRationale @@ -45,7 +43,6 @@ import openfoodfacts.github.scrachx.openfood.listeners.RecyclerItemClickListener import openfoodfacts.github.scrachx.openfood.models.Search import openfoodfacts.github.scrachx.openfood.models.SearchInfo import openfoodfacts.github.scrachx.openfood.models.SearchProduct -import openfoodfacts.github.scrachx.openfood.network.OpenFoodAPIClient import openfoodfacts.github.scrachx.openfood.repositories.ProductRepository import openfoodfacts.github.scrachx.openfood.utils.* import openfoodfacts.github.scrachx.openfood.utils.SearchType.* @@ -58,9 +55,6 @@ class ProductSearchActivity : BaseActivity() { private var _binding: ActivityProductBrowsingListBinding? = null private val binding get() = _binding!! - @Inject - lateinit var client: OpenFoodAPIClient - @Inject lateinit var productRepository: ProductRepository @@ -322,7 +316,7 @@ class ProductSearchActivity : BaseActivity() { SEARCH -> { if (isBarcodeValid(searchQuery)) { - client.openProduct(searchQuery, this) + lifecycleScope.launch { openProduct(searchQuery) } } else { client.searchProductsByName(searchQuery, pageAddress) .startSearch(R.string.txt_no_matching_products, R.string.txt_broaden_search) @@ -513,35 +507,10 @@ class ProductSearchActivity : BaseActivity() { } } }) + binding.productsRecyclerView.addOnItemTouchListener(RecyclerItemClickListener(this) { _, position -> val product = adapter.getProduct(position) ?: return@RecyclerItemClickListener - val barcode = product.code - - if (Utils.isNetworkConnected(this)) { - client.openProduct(barcode, this) - try { - val viewWithFocus = currentFocus - if (viewWithFocus != null) { - val imm = getSystemService(INPUT_METHOD_SERVICE) as InputMethodManager - imm.hideSoftInputFromWindow(viewWithFocus.windowToken, 0) - } - } catch (e: NullPointerException) { - Log.e(LOG_TAG, "addOnItemTouchListener", e) - } - } else { - MaterialAlertDialogBuilder(this) - .setTitle(R.string.device_offline_dialog_title) - .setMessage(R.string.connectivity_check) - .setPositiveButton(R.string.txt_try_again) { _, _ -> - if (Utils.isNetworkConnected(this)) { - client.openProduct(barcode, this) - } else { - Toast.makeText(this@ProductSearchActivity, R.string.device_offline_dialog_title, Toast.LENGTH_SHORT).show() - } - } - .setNegativeButton(R.string.dismiss) { d, _ -> d.dismiss() } - .show() - } + openProduct(product.code) return@RecyclerItemClickListener }) diff --git a/app/src/main/java/openfoodfacts/github/scrachx/openfood/features/searchbycode/SearchByCodeFragment.kt b/app/src/main/java/openfoodfacts/github/scrachx/openfood/features/searchbycode/SearchByCodeFragment.kt index 05432b335830..735052cb6777 100644 --- a/app/src/main/java/openfoodfacts/github/scrachx/openfood/features/searchbycode/SearchByCodeFragment.kt +++ b/app/src/main/java/openfoodfacts/github/scrachx/openfood/features/searchbycode/SearchByCodeFragment.kt @@ -6,14 +6,16 @@ import android.view.View import android.view.ViewGroup import android.widget.TextView import androidx.appcompat.app.AppCompatActivity +import androidx.lifecycle.lifecycleScope import dagger.hilt.android.AndroidEntryPoint +import kotlinx.coroutines.launch import openfoodfacts.github.scrachx.openfood.R import openfoodfacts.github.scrachx.openfood.databinding.FragmentFindProductBinding import openfoodfacts.github.scrachx.openfood.features.shared.NavigationBaseFragment import openfoodfacts.github.scrachx.openfood.network.OpenFoodAPIClient import openfoodfacts.github.scrachx.openfood.utils.NavigationDrawerListener import openfoodfacts.github.scrachx.openfood.utils.NavigationDrawerListener.NavigationDrawerType -import openfoodfacts.github.scrachx.openfood.utils.Utils +import openfoodfacts.github.scrachx.openfood.utils.hideKeyboard import openfoodfacts.github.scrachx.openfood.utils.isBarcodeValid import javax.inject.Inject @@ -60,14 +62,14 @@ class SearchByCodeFragment : NavigationBaseFragment() { } private fun checkBarcodeThenSearch() { - Utils.hideKeyboard(requireActivity()) + requireActivity().hideKeyboard() val barCodeTxt = binding.editTextBarcode.text.toString() if (barCodeTxt.isEmpty()) { binding.editTextBarcode.error = resources.getString(R.string.txtBarcodeRequire) } else if (!isBarcodeValid(barCodeTxt)) { binding.editTextBarcode.error = resources.getString(R.string.txtBarcodeNotValid) } else { - client.openProduct(barCodeTxt, requireActivity()) + lifecycleScope.launch { client.openProduct(barCodeTxt, requireActivity()) } } } diff --git a/app/src/main/java/openfoodfacts/github/scrachx/openfood/features/shared/BaseActivity.kt b/app/src/main/java/openfoodfacts/github/scrachx/openfood/features/shared/BaseActivity.kt index 83ee9767842c..26bc4d1382ad 100644 --- a/app/src/main/java/openfoodfacts/github/scrachx/openfood/features/shared/BaseActivity.kt +++ b/app/src/main/java/openfoodfacts/github/scrachx/openfood/features/shared/BaseActivity.kt @@ -24,15 +24,22 @@ import androidx.activity.result.contract.ActivityResultContracts import androidx.annotation.CallSuper import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatDelegate +import androidx.lifecycle.lifecycleScope +import com.google.android.material.dialog.MaterialAlertDialogBuilder import dagger.hilt.EntryPoints +import kotlinx.coroutines.launch import openfoodfacts.github.scrachx.openfood.R import openfoodfacts.github.scrachx.openfood.features.scan.ContinuousScanActivity import openfoodfacts.github.scrachx.openfood.hilt.AppEntryPoint -import openfoodfacts.github.scrachx.openfood.utils.MY_PERMISSIONS_REQUEST_CAMERA -import openfoodfacts.github.scrachx.openfood.utils.getLoginPreferences +import openfoodfacts.github.scrachx.openfood.network.OpenFoodAPIClient +import openfoodfacts.github.scrachx.openfood.utils.* +import javax.inject.Inject abstract class BaseActivity : AppCompatActivity() { + @Inject + lateinit var client: OpenFoodAPIClient + protected val requestCameraThenOpenScan = registerForActivityResult(ActivityResultContracts.RequestPermission()) { if (it) startScanActivity() } @@ -70,5 +77,19 @@ abstract class BaseActivity : AppCompatActivity() { .apply { addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP) } .let { startActivity(it) } } + + protected fun openProduct(barcode: String) { + if (isNetworkConnected()) { + hideKeyboard() + lifecycleScope.launch { client.openProduct(barcode, this@BaseActivity) } + } else { + MaterialAlertDialogBuilder(this) + .setTitle(R.string.device_offline_dialog_title) + .setMessage(R.string.connectivity_check) + .setPositiveButton(R.string.txt_try_again) { _, _ -> openProduct(barcode) } + .setNegativeButton(R.string.dismiss) { d, _ -> d.dismiss() } + .show() + } + } } diff --git a/app/src/main/java/openfoodfacts/github/scrachx/openfood/network/OpenFoodAPIClient.kt b/app/src/main/java/openfoodfacts/github/scrachx/openfood/network/OpenFoodAPIClient.kt index 3e42fb7d0b99..a91265596f6d 100644 --- a/app/src/main/java/openfoodfacts/github/scrachx/openfood/network/OpenFoodAPIClient.kt +++ b/app/src/main/java/openfoodfacts/github/scrachx/openfood/network/OpenFoodAPIClient.kt @@ -11,13 +11,9 @@ import com.fasterxml.jackson.databind.JsonNode import dagger.hilt.android.qualifiers.ApplicationContext import io.reactivex.Completable import io.reactivex.Single -import io.reactivex.android.schedulers.AndroidSchedulers -import io.reactivex.disposables.CompositeDisposable -import io.reactivex.disposables.Disposable -import io.reactivex.rxkotlin.addTo -import io.reactivex.rxkotlin.toObservable import io.reactivex.schedulers.Schedulers import kotlinx.coroutines.Dispatchers.IO +import kotlinx.coroutines.Dispatchers.Main import kotlinx.coroutines.rx2.await import kotlinx.coroutines.rx2.rxCompletable import kotlinx.coroutines.rx2.rxSingle @@ -60,7 +56,6 @@ class OpenFoodAPIClient @Inject constructor( private val sentryAnalytics: SentryAnalytics, private val localeManager: LocaleManager ) { - private var historySyncDisp = CompositeDisposable() suspend fun getProductStateFull( barcode: String, @@ -137,30 +132,32 @@ class OpenFoodAPIClient @Inject constructor( * @param barcode product barcode * @param activity */ - fun openProduct(barcode: String, activity: Activity): Disposable = - rawApi.getProductByBarcode( - barcode, - getAllFields(), - localeManager.getLanguage(), - getUserAgent(Utils.HEADER_USER_AGENT_SEARCH) - ) - .observeOn(AndroidSchedulers.mainThread()) - .doOnError { - if (it is IOException) { - Toast.makeText(activity, R.string.something_went_wrong, Toast.LENGTH_LONG).show() - } else { - productNotFoundDialogBuilder(activity, barcode).show() - } - }.subscribe { state -> - if (state.status == 0L) { - productNotFoundDialogBuilder(activity, barcode) - .onNegative { _, _ -> activity.onBackPressed() } - .show() - } else { - addToHistory(state.product!!).subscribe() - startProductViewActivity(activity, state) - } + suspend fun openProduct(barcode: String, activity: Activity) { + val state = try { + rawApi.getProductByBarcode( + barcode, + getAllFields(), + localeManager.getLanguage(), + getUserAgent(Utils.HEADER_USER_AGENT_SEARCH) + ).await() + } catch (err: Exception) { + when (err) { + is IOException -> Toast.makeText(activity, R.string.something_went_wrong, Toast.LENGTH_LONG).show() + else -> productNotFoundDialogBuilder(activity, barcode).show() + } + return + } + withContext(Main) { + if (state.status == 0L) { + productNotFoundDialogBuilder(activity, barcode) + .onNegative { _, _ -> activity.onBackPressed() } + .show() + } else { + addToHistory(state.product!!).subscribe() + startProductViewActivity(activity, state) } + } + } /** * @param barcode @@ -194,6 +191,7 @@ class OpenFoodAPIClient @Inject constructor( */ private fun getUploadableMap(image: ProductImage): Map { val lang = image.language + val imgMap = hashMapOf(PRODUCT_BARCODE to image.barcodeBody, "imagefield" to image.fieldBody) if (image.imgFront != null) { imgMap["""imgupload_front"; filename="front_$lang$PNG_EXT"""] = image.imgFront!! @@ -212,7 +210,7 @@ class OpenFoodAPIClient @Inject constructor( } // Attribute the upload to the connected user - addUserInfo().forEach { (key, value) -> + getUserInfo().forEach { (key, value) -> imgMap[key] = RequestBody.create(MIME_TEXT, value) } return imgMap @@ -228,7 +226,7 @@ class OpenFoodAPIClient @Inject constructor( * Add a product to ScanHistory asynchronously */ fun addToHistory(product: Product) = rxCompletable(IO) { - daoSession.historyProductDao.addToHistorySync(product, localeManager.getLanguage()) + daoSession.historyProductDao.addToHistory(product, localeManager.getLanguage()) } fun getProductsByContributor(contributor: String, page: Int) = @@ -284,53 +282,48 @@ class OpenFoodAPIClient @Inject constructor( * @param brand search query for product * @param page page numbers */ - fun getProductsByBrand(brand: String, page: Int): Single = + fun getProductsByBrand(brand: String, page: Int): Single = rxSingle(IO) { rawApi.getProductByBrands(brand, page, fieldsToFetchFacets) + } + + fun postImg(image: ProductImage, setAsDefault: Boolean = false) = rxCompletable { + try { + val body = rawApi.saveImage(getUploadableMap(image)).await() - fun postImg(image: ProductImage, setAsDefault: Boolean = false): Completable { - return rawApi.saveImage(getUploadableMap(image)) - .flatMapCompletable { body: JsonNode -> - if (!body.isObject) { + when { + !body.isObject -> { throw IOException("body is not an object") - } else { - if (body[Keys.STATUS].asText().contains(ApiFields.Defaults.STATUS_NOT_OK)) { - throw IOException(body["error"].asText()) - } else { - if (setAsDefault) { - setDefaultImageFromServerResponse(body, image) - } else { - Completable.complete() - } - } } - }.doOnError { - daoSession.toUploadProductDao.insertOrReplace( - ToUploadProduct( - image.barcode, - image.filePath, - image.imageField.toString() - ) - ) + ApiFields.Defaults.STATUS_NOT_OK in body[Keys.STATUS].asText() -> { + throw IOException(body["error"].asText()) + } + setAsDefault -> setDefaultImageFromServerResponse(body, image) } + } catch (err: Exception) { + daoSession.toUploadProductDao.insertOrReplace( + ToUploadProduct( + image.barcode, + image.filePath, + image.imageField.toString() + ) + ) + } } private fun setDefaultImageFromServerResponse(body: JsonNode, image: ProductImage): Completable { - val queryMap = hashMapOf( + val queryMap = getUserInfo() + listOf( IMG_ID to body["image"][IMG_ID].asText(), "id" to body["imagefield"].asText() ) - return rawApi.editImage(image.barcode, addUserInfo(queryMap)) - .flatMapCompletable { jsonNode: JsonNode -> - if ("status ok" == jsonNode[Keys.STATUS].asText()) { - return@flatMapCompletable Completable.complete() - } else { - throw IOException(jsonNode["error"].asText()) - } - } + + return rawApi.editImage(image.barcode, queryMap).flatMapCompletable { node -> + if (node[Keys.STATUS].asText() != "status ok") throw IOException(node["error"].asText()) + else Completable.complete() + } } suspend fun editImage(code: String, imgMap: MutableMap) = withContext(IO) { - rawApi.editImages(code, addUserInfo(imgMap)) + rawApi.editImages(code, imgMap + getUserInfo()) } /** @@ -339,14 +332,14 @@ class OpenFoodAPIClient @Inject constructor( * @param code code of the product */ suspend fun unSelectImage(code: String, field: ProductImageField, language: String) = withContext(IO) { - val imgMap = hashMapOf(IMAGE_STRING_ID to getImageStringKey(field, language)) - return@withContext rawApi.unSelectImage(code, addUserInfo(imgMap)) + val imgMap = getUserInfo() + (IMAGE_STRING_ID to getImageStringKey(field, language)) + return@withContext rawApi.unSelectImage(code, imgMap) } fun getProductsByOrigin(origin: String, page: Int) = rawApi.getProductsByOrigin(origin, page, fieldsToFetchFacets) - fun syncOldHistory() { + suspend fun syncOldHistory() = withContext(IO) { val fields = listOf( Keys.IMAGE_SMALL_URL, Keys.PRODUCT_NAME, @@ -356,41 +349,38 @@ class OpenFoodAPIClient @Inject constructor( Keys.NUTRITION_GRADE_FR, Keys.BARCODE ).joinToString(",") - historySyncDisp.clear() - - Single.fromCallable { daoSession.historyProductDao.loadAll() } - .flatMapObservable { it.toObservable() } - .flatMapCompletable { historyProduct -> - rawApi.getProductByBarcode( - historyProduct.barcode, - fields, - localeManager.getLanguage(), - getUserAgent(Utils.HEADER_USER_AGENT_SEARCH) - ).flatMapCompletable { state -> - if (state.status != 0L) { - val product = state.product!! - val hp = HistoryProduct( - product.productName, - product.brands, - product.getImageSmallUrl(localeManager.getLanguage()), - product.code, - product.quantity, - product.nutritionGradeFr, - product.ecoscore, - product.novaGroups - ) - Log.d("syncOldHistory", hp.toString()) - hp.lastSeen = historyProduct.lastSeen - daoSession.historyProductDao.insertOrReplace(hp) - Completable.complete() - } else Completable.error(IOException("Could not sync history. Error with product ${state.code} ")) - } - }.subscribe { - context.getSharedPreferences("prefs", 0).edit { - putBoolean("is_old_history_data_synced", true) - } - }.addTo(historySyncDisp) + daoSession.historyProductDao.loadAll().forEach { historyProduct -> + val state = rawApi.getProductByBarcode( + historyProduct.barcode, + fields, + localeManager.getLanguage(), + getUserAgent(Utils.HEADER_USER_AGENT_SEARCH) + ).await() + + if (state.status == 0L) throw IOException("Could not sync history. Error with product ${state.code} ") + else { + val product = state.product!! + val hp = HistoryProduct( + product.productName, + product.brands, + product.getImageSmallUrl(localeManager.getLanguage()), + product.code, + product.quantity, + product.nutritionGradeFr, + product.ecoscore, + product.novaGroups + ) + Log.d("syncOldHistory", hp.toString()) + hp.lastSeen = historyProduct.lastSeen + daoSession.historyProductDao.insertOrReplace(hp) + } + } + + + context.getSharedPreferences("prefs", 0).edit { + putBoolean("is_old_history_data_synced", true) + } } fun getInfoAddedIncompleteProductsSingle(contributor: String, page: Int) = @@ -434,8 +424,10 @@ class OpenFoodAPIClient @Inject constructor( const val PNG_EXT = ".png" suspend fun HistoryProductDao.addToHistory(newProd: OfflineSavedProduct): Unit = withContext(IO) { - val savedProduct: HistoryProduct? = - queryBuilder().where(HistoryProductDao.Properties.Barcode.eq(newProd.barcode)).unique() + val savedProduct: HistoryProduct? = queryBuilder() + .where(HistoryProductDao.Properties.Barcode.eq(newProd.barcode)) + .unique() + val details = newProd.productDetails val hp = HistoryProduct( newProd.name, @@ -450,16 +442,17 @@ class OpenFoodAPIClient @Inject constructor( ) if (savedProduct != null) hp.id = savedProduct.id insertOrReplace(hp) + return@withContext } /** * Add a product to ScanHistory synchronously */ - suspend fun HistoryProductDao.addToHistorySync(product: Product, language: String): Unit = + suspend fun HistoryProductDao.addToHistory(product: Product, language: String): Unit = withContext(IO) { - val historyProducts: HistoryProduct? = queryBuilder() + val savedProduct: HistoryProduct? = queryBuilder() .where(HistoryProductDao.Properties.Barcode.eq(product.code)) .unique() @@ -474,7 +467,7 @@ class OpenFoodAPIClient @Inject constructor( product.novaGroups ) - if (historyProducts != null) hp.id = historyProducts.id + if (savedProduct != null) hp.id = savedProduct.id insertOrReplace(hp) return@withContext @@ -485,7 +478,9 @@ class OpenFoodAPIClient @Inject constructor( * Fill the given [Map] with user info (username, password, comment) * * @param imgMap The map to fill + * */ + @Deprecated("Use the += operator with getUserInfo()") private fun addUserInfo(imgMap: MutableMap = mutableMapOf()): Map { val settings = context.getLoginPreferences() @@ -501,6 +496,8 @@ class OpenFoodAPIClient @Inject constructor( return imgMap } + private fun getUserInfo() = addUserInfo() + /** * Uploads comment by users * diff --git a/app/src/main/java/openfoodfacts/github/scrachx/openfood/network/services/ProductsAPI.kt b/app/src/main/java/openfoodfacts/github/scrachx/openfood/network/services/ProductsAPI.kt index e9e4bcea4ad1..303143332d01 100644 --- a/app/src/main/java/openfoodfacts/github/scrachx/openfood/network/services/ProductsAPI.kt +++ b/app/src/main/java/openfoodfacts/github/scrachx/openfood/network/services/ProductsAPI.kt @@ -115,11 +115,11 @@ interface ProductsAPI { @GET("brand/{brand}/{page}.json") - fun getProductByBrands( - @Path("brand") brand: String, - @Path("page") page: Int, - @Query("fields") fields: String - ): Single + suspend fun getProductByBrands( + @Path("brand") brand: String, + @Path("page") page: Int, + @Query("fields") fields: String + ): Search /** * call API service to return products using Additives diff --git a/app/src/main/java/openfoodfacts/github/scrachx/openfood/utils/ContextExt.kt b/app/src/main/java/openfoodfacts/github/scrachx/openfood/utils/ContextExt.kt index dee3715a2a44..fe6ae22e8f6f 100644 --- a/app/src/main/java/openfoodfacts/github/scrachx/openfood/utils/ContextExt.kt +++ b/app/src/main/java/openfoodfacts/github/scrachx/openfood/utils/ContextExt.kt @@ -1,5 +1,6 @@ package openfoodfacts.github.scrachx.openfood.utils +import android.app.Activity import android.content.Context import android.content.Intent import android.content.IntentFilter @@ -8,6 +9,7 @@ import android.content.pm.PackageManager import android.os.BatteryManager import android.util.Log import android.util.TypedValue +import android.view.inputmethod.InputMethodManager import androidx.preference.PreferenceManager import openfoodfacts.github.scrachx.openfood.features.PreferencesFragment import kotlin.math.ceil @@ -64,3 +66,9 @@ fun Context.getVersionName(): String = try { } fun Context.isHardwareCameraInstalled() = isHardwareCameraInstalled(this) + +fun Activity.hideKeyboard() { + val view = currentFocus ?: return + (getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager) + .hideSoftInputFromWindow(view.windowToken, 0) +} \ No newline at end of file diff --git a/app/src/main/java/openfoodfacts/github/scrachx/openfood/utils/Utils.kt b/app/src/main/java/openfoodfacts/github/scrachx/openfood/utils/Utils.kt index ba927cbd7f92..15caa786e8ce 100644 --- a/app/src/main/java/openfoodfacts/github/scrachx/openfood/utils/Utils.kt +++ b/app/src/main/java/openfoodfacts/github/scrachx/openfood/utils/Utils.kt @@ -35,7 +35,6 @@ import android.text.style.ClickableSpan import android.util.Log import android.view.View import android.view.ViewGroup -import android.view.inputmethod.InputMethodManager import android.widget.ImageView import androidx.annotation.ColorInt import androidx.annotation.DrawableRes @@ -74,11 +73,6 @@ object Utils { const val NO_DRAWABLE_RESOURCE = 0 const val FORCE_REFRESH_TAXONOMIES = "force_refresh_taxonomies" - fun hideKeyboard(activity: Activity) { - val view = activity.currentFocus ?: return - (activity.getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager) - .hideSoftInputFromWindow(view.windowToken, 0) - } @JvmStatic fun compressImage(fileUrl: String): String? { @@ -170,23 +164,6 @@ object Utils { isUploadJobInitialised = true } - /** - * Check if the user is connected to a network. This can be any network. - * - * @param context of the application. - * @return true if connected or connecting. False otherwise. - */ - @Suppress("DEPRECATION") - fun isNetworkConnected(context: Context): Boolean { - val cm = context.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager - return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { - val capability = cm.getNetworkCapabilities(cm.activeNetwork) - capability?.hasCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET) ?: false - } else { - cm.activeNetworkInfo?.isConnectedOrConnecting ?: false - } - } - fun makeOrGetPictureDirectory(context: Context): File { // determine the profile directory var dir = context.filesDir @@ -352,4 +329,23 @@ internal fun RequestCreator.into(target: ImageView, onSuccess: () -> Unit) { fun @receiver:ColorInt Int.darken(ratio: Float) = ColorUtils.blendARGB(this, Color.BLACK, ratio) -fun @receiver:ColorInt Int.lighten(ratio: Float) = ColorUtils.blendARGB(this, Color.WHITE, ratio) \ No newline at end of file +fun @receiver:ColorInt Int.lighten(ratio: Float) = ColorUtils.blendARGB(this, Color.WHITE, ratio) + +/** + * Check if the user is connected to a network. This can be any network. + * + * @return `true` if connected or connecting; + * + * `false` otherwise. + * + */ +@Suppress("DEPRECATION") +fun Context.isNetworkConnected(): Boolean { + val cm = getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager + return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { + val capability = cm.getNetworkCapabilities(cm.activeNetwork) + capability?.hasCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET) ?: false + } else { + cm.activeNetworkInfo?.isConnectedOrConnecting ?: false + } +} \ No newline at end of file