System services not available to Activities before onCreate() [KOTLIN] - java

I have hideKeyboard(view: View) method in util class. In my activity I want to call that method. I created object in activity utils = Utils() and then utils.HideKeyboard(binding.authConstraint) but when Im trying to click on constraintLayout it is throwing error "System services not available to Activities before onCreate()" what am I doing wrong ?
my Util Class
open class Utils():Activity(){
fun hideKeyboard(view: View) {
val inputMethodManager =
getSystemService(Activity.INPUT_METHOD_SERVICE) as InputMethodManager
inputMethodManager.hideSoftInputFromWindow(view.windowToken, 0)
}
my activity
class AuthActivity : ActivityWithDisposableList() {
private lateinit var binding: ActivityAuthBinding
private lateinit var authViewModel: AuthViewModel
val utils = Utils()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = DataBindingUtil.setContentView(this, R.layout.activity_auth)
authViewModel = ViewModelProviders.of(this).get(AuthViewModel::class.java)
binding.authViewModel = authViewModel
binding.lifecycleOwner = this
authViewModel.isKeyboardClosed.observe(this, Observer { isTrue ->
if (isTrue) {
utils.hideKeyboard(binding.authConstraint,this)
binding.usernameInputEditText.clearFocus()
binding.passwordInputEditText.clearFocus()
}
})
}
}

Do not create an instance of an Activity subclass yourself. And do not make some arbitrary class extend Activity, just because you need a Context. Get the Context from a parameter to a constructor or function.
With that in mind, replace your Utils with:
class Utils {
fun hideKeyboard(view: View) {
val inputMethodManager =
view.context.getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager
inputMethodManager.hideSoftInputFromWindow(view.windowToken, 0)
}
}
Here, you are getting the Context from the View that you are supplying.
Note that you could also go with object Utils or have hideKeyboard() be an extension function on View, if you liked.

Related

LiveData, setValue in Activity and observe in fragment. Is it possible?

How to use livedata, do setValue in activity, and read observe in a fragment, is it possible at all? I know that with this code it calls a new instance twice, but how to do it correctly?
viewmodel:
public class PongViewModel extends ViewModel {
private MutableLiveData<String> pongSections;
public MutableLiveData<String> getPongSections() {
if (pongSections == null) {
pongSections = new MutableLiveData<String>();
}
return pongSections;
}
}
MainAcitivity:
// OnCreate
pongViewModel = new ViewModelProvider(this).get(PongViewModel.class);
pongViewModel.getPongSections().setValue("test");
Fragment:
pongViewModel = new ViewModelProvider(this).get(PongViewModel.class);
pongViewModel.getPongSections().observe(this, pongSections -> {
System.out.println("DATA !!!");
});
Use a viewmodel scoped to the activity to get the viewmodel from the activity, since at the moment you are right, you created a new viewmodel instance:
class YourFragment extends Fragment {
private val pongViewModel: PongViewModel by activityViewModels()
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
//This should fire after updating in the activity now
pongViewModel.getPongSections().observe(this, pongSections -> {
System.out.println("DATA !!!");
});
}
}
Documentation source

Implement Geojson points to map and locate me with Mapbox, in Kotlin on Android Studio

My question is about Mapbox. In this period I am working on an ANDROID application based on mapbox, using Kotlin and Fragments and my problem concerns the visualization of points on the map itself. That is my need is to be able to show points on the map through a GEOJSON file, for now I have been able to see the map in full in the application, but I cannot find a way to show the points taken from a GeoJson file and locate myself in the map via a button.
I should implement both functions in the fragment, so my problem is precisely that of not being able to show the points of a geojson file and find a way to locate myself in the map itself. I await help if there is someone able to help me with this problem, I also leave the code of the fragment class in kotlin.
Thanks everyone in advance !!
FRAGMENT HOME
class HomeFragment : Fragment() {
private var mapView: MapView? = null
#Nullable
override fun onCreateView(
inflater: LayoutInflater,
#Nullable container: ViewGroup?,
#Nullable savedInstanceState: Bundle?
): View? {
Mapbox.getInstance(
context!!.applicationContext,
"MIO CODICE MAPBOX"
)
val view: View = inflater.inflate(R.layout.fragment_home, container, false)
mapView = view.findViewById<View>(R.id.mapView) as MapView
mapView!!.onCreate(savedInstanceState)
return view
}
override fun onResume() {
super.onResume()
mapView!!.onResume()
}
override fun onPause() {
super.onPause()
mapView!!.onPause()
}
override fun onSaveInstanceState(outState: Bundle) {
super.onSaveInstanceState(outState)
mapView!!.onSaveInstanceState(outState)
}
override fun onLowMemory() {
super.onLowMemory()
mapView!!.onLowMemory()
}
override fun onDestroyView() {
super.onDestroyView()
mapView!!.onDestroy()
}
}
HOST ACTIVITY :
class HostActivity : AppCompatActivity() {
lateinit var googleSignInClient: GoogleSignInClient
private lateinit var navController: NavController
private val mAuth: FirebaseAuth = FirebaseAuth.getInstance()
private val db: FirebaseFirestore = FirebaseFirestore.getInstance()
private lateinit var drawerLayout: DrawerLayout
private lateinit var navViewBinding: DrawerHeaderLayoutBinding
override fun onCreate(savedInstanceState: Bundle?) {
setTheme(R.style.AppTheme)
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_host)
val toolbar = customToolbar
setSupportActionBar(toolbar)
val gso = GoogleSignInOptions.Builder(GoogleSignInOptions.DEFAULT_SIGN_IN)
.requestIdToken(getString(R.string.default_web_client_id))
.requestEmail()
.build()
googleSignInClient = GoogleSignIn.getClient(this, gso)
drawerLayout = drawer_layout
navViewBinding = DrawerHeaderLayoutBinding.inflate(layoutInflater, navView, true)
val navHost =
supportFragmentManager.findFragmentById(R.id.navHostFragment) as NavHostFragment
navController = navHost.navController
val navInflater = navController.navInflater
val graph = navInflater.inflate(R.navigation.main_graph)
navController.addOnDestinationChangedListener { _, destination, _ ->
if (destination.id == R.id.onBoarding ||
destination.id == R.id.authFragment ||
destination.id == R.id.loginFragment ||
destination.id == R.id.signUpFragment
) {
toolbar.visibility = View.GONE
drawerLayout.setDrawerLockMode(DrawerLayout.LOCK_MODE_LOCKED_CLOSED)
} else {
toolbar.visibility = View.VISIBLE
drawerLayout.setDrawerLockMode(DrawerLayout.LOCK_MODE_UNLOCKED)
}
}
if (!Prefs.getInstance(this)!!.hasCompletedWalkthrough!!) {
if (mAuth.currentUser == null) {
graph.startDestination = R.id.authFragment
} else {
getUserData()
graph.startDestination = R.id.homeFragment
}
} else {
graph.startDestination = R.id.onBoarding
}
navController.graph = graph
NavigationUI.setupActionBarWithNavController(this, navController, drawerLayout)
navView.setupWithNavController(navController)
navView.setNavigationItemSelectedListener {
it.isChecked
drawerLayout.closeDrawers()
when (it.itemId) {
R.id.action_logout -> {
MyApplication.currentUser!!.active = false
FirestoreUtil.updateUser(MyApplication.currentUser!!) {
mAuth.signOut()
}
googleSignInClient.signOut()
MyApplication.currentUser = null
navController.navigate(R.id.action_logout)
}
}
true
}
}
private fun getUserData() {
val ref = db.collection("users").document(mAuth.currentUser!!.uid)
ref.get().addOnSuccessListener {
val userInfo = it.toObject(UserModel::class.java)
navViewBinding.user = userInfo
MyApplication.currentUser = userInfo
MyApplication.currentUser!!.active = true
FirestoreUtil.updateUser(MyApplication.currentUser!!) {
}
}.addOnFailureListener {
val intent = Intent(this, MyApplication::class.java)
startActivity(intent)
finish()
}
}
override fun onSupportNavigateUp(): Boolean {
return NavigationUI.navigateUp(navController, drawerLayout)
}
}
You'll need to use a GeoJsonSource. https://github.com/mapbox/mapbox-android-demo/search?q=GeoJsonSource shows how the demo app uses the source.
https://github.com/mapbox/mapbox-android-demo/blob/master/MapboxAndroidDemo/src/main/java/com/mapbox/mapboxandroiddemo/examples/basics/KotlinSupportMapFragmentActivity.kt (Its XML layout file)
Putting icons on the map. https://docs.mapbox.com/android/maps/examples/marker-symbol-layer/. Do all of the icon setup inside of the fragment's onStyleLoaded() callback as seen at https://github.com/mapbox/mapbox-android-demo/blob/master/MapboxAndroidDemo/src/main/java/com/mapbox/mapboxandroiddemo/examples/basics/KotlinSupportMapFragmentActivity.kt#L51-L55
https://github.com/mapbox/mapbox-android-demo/search?q=loadGeojson shows how the demo app loads from a GeoJson file. You could use coroutines instead of building out the AsyncTask.
Although it's in Java, https://docs.mapbox.com/android/maps/examples/show-a-users-location-on-a-fragment/ shows how to combine the Maps SDK's LocationComponent with a fragment.
Regarding moving the camera to the device's last known location when a button is clicked, see my answer at https://stackoverflow.com/a/64159178/6358488

How to notify and update list when item has been deleted using Groupie RecyclerView library and Kotlin

I have a RecyclerView implemented with the Groupie library and I can delete an item from the list fine, however need to update the view to see the change. I'd like to have something like notifyDataSetChanged() instead, so the list updates immediately. I'm a bit confused at this stage though, tried a few different ways to get an interface from the class that hosts my view holder to be triggered from the fragment that holds the adapter but I think I'm stuck now if I could get some help please.
class RecyclerProductItem(
private val activity: MainActivity,
private val product: Product, private val adapterListener: AdapterListener
) : Item<GroupieViewHolder>() {
companion object {
var clickListener: AdapterListener? = null
}
override fun bind(viewHolder: GroupieViewHolder, position: Int) {
viewHolder.apply {
with(viewHolder.itemView) {
clickListener = adapterListener
ivTrash.setOnClickListener(object : View.OnClickListener {
override fun onClick(v: View?) {
if (clickListener != null) {
Toast.makeText(context, "delete method to be added here", Toast.LENGTH_SHORT).show()
clickListener?.onClickItem(position)
}
}
})
}
}
}
override fun getLayout() = R.layout.recyclerview_item_row
interface AdapterListener {
fun onClickItem(position: Int)
}
}
Here it's my fragment. I tried to add a section to the adapter to see if it would allow me to retrieve a listener for it, but as my listener should be triggered under a specific item within the layout, this may not be the best solution, although couldn't make this work either.
class ProductsListFragment : Fragment(), RecyclerProductItem.AdapterListener {
private lateinit var adapter: GroupAdapter<GroupieViewHolder>
private val section = Section()
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
return inflater.inflate(R.layout.fragment_products_list, container, false)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
val linearLayoutManager = LinearLayoutManager(activity)
recyclerView.layoutManager = linearLayoutManager
adapter = GroupAdapter()
adapter.add(section)
recyclerView.adapter = adapter
loadProducts()
}
private fun loadProducts() {
GetProductsAPI.postData(object : GetProductsAPI.ThisCallback {
override fun onSuccess(productList: List<JsonObject>) {
for (jo in productList) {
val gson = GsonBuilder().setPrettyPrinting().create()
val product: Product =
gson.fromJson(jo, Product::class.java)
adapter.add(
RecyclerProductItem(
activity as MainActivity,
Product(
product.id,
product.title,
product.description,
product.price
),adapterListenerToBePassedHere
)
) // This part is where I should be giving the listener, but get a red line since not sure how to get it to include it here.
}
}
})
}
companion object {
fun newInstance(): ProductsListFragment {
return ProductsListFragment()
}
}
override fun onClickItem(position: Int) {
adapter.notifyItemRemoved(position)
}
}
Many thanks.
I think you are missing this concept from the groupie Readme:
Modifying the contents of the GroupAdapter in any way automatically sends change notifications. Adding an item calls notifyItemAdded(); adding a group calls notifyItemRangeAdded(), etc.
So to remove an item, call section.remove(item). However, in your onClickItem function you currently only pass the position. Pass the item like clickListener?.onClickItem(this#RecyclerProductItem) instead.
Even more ideally and safely you should remove by product.id, e.g. clickListener?.onClickItem(this#RecyclerProductItem.product.id) then in onClickItem() you just search for the item with that product id and remove it. Let me know if I'm not clear.
Based on #carson's reply, this is what worked for me. Had to add the items to the section, the section to the adapter and then remove the item from the section based on the adapter position once that listener is clicked, passing the method that implements the listener as one of the arguments to complete the GroupAdapter.
class RecyclerProductItem(
private val activity: MainActivity,
private val product: Product, private val adapterListener: AdapterListener
) : Item<GroupieViewHolder>() {
companion object {
var clickListener: AdapterListener? = null
}
override fun bind(viewHolder: GroupieViewHolder, position: Int) {
viewHolder.apply {
with(viewHolder.itemView) {
tvTitle.text = product.title
clickListener = adapterListener
ivTrash.setOnClickListener(object : View.OnClickListener {
override fun onClick(v: View?) {
if (clickListener != null) {
Toast.makeText(context, "delete method to be added here", Toast.LENGTH_SHORT).show()
clickListener?.onClickItem(this#RecyclerProductItem.product.id, adapterPosition)
}
}
})
}
}
}
override fun getLayout() = R.layout.recyclerview_item_row
interface AdapterListener {
fun onClickItem(id: Int, position: Int)
}
}
And
private fun loadProducts() {
GetProductsAPI.postData(object : GetProductsAPI.ThisCallback,
RecyclerProductItem.AdapterListener {
override fun onSuccess(productList: List<JsonObject>) {
Log.i(LOG_TAG, "onSuccess $LOG_TAG")
for (jo in productList) {
val gson = GsonBuilder().setPrettyPrinting().create()
val product: Product =
gson.fromJson(jo, Product::class.java)
val linearLayoutManager = LinearLayoutManager(activity)
recyclerView.layoutManager = linearLayoutManager
adapter = GroupAdapter()
section.add(
RecyclerProductItem(
activity as MainActivity,
Product(
product.id,
product.title,
product.description,
product.price
), this
)
)
adapter.add(section)
recyclerView.adapter = adapter
}
}
override fun onFailure() {
Log.e(LOG_TAG, "onFailure $LOG_TAG")
}
override fun onError() {
Log.e(LOG_TAG, "onError $LOG_TAG")
}
override fun onClickItem(id: Int, position: Int) {
section.remove(adapter.getItem(position))
}
})
}

Bind custom DialogFragment to service initialized into Application scope

I'm working to an application that makes some webcalls and I want to add a DialogFragment (with a ProgressBar at the center) that shows when I'm waiting for the call to complete.
Webcalls are extended from AsyncTask with a private library that gives an interface (DialogInterface) to set what to do when the dialog it's shown and hidden.
For the DialogFragment I've found some nice code from here:
https://code.luasoftware.com/tutorials/android/android-show-loading-and-prevent-touch/
It works without problems from an Activity class, but the DialogInterface has to be set to the WebService builder which is initialized into the Application class
This is the init sequence in the Application class
WebService.init(
WebServiceBuilder(this)
.setBaseUrlTest("http://google.it/")
.setUrlType(UrlType.TEST)
.setDialogInterface(object : DialogInterface {
override fun showDialog(context: Context?) {
if (context != null)
val busyDialog = BusyDialogFragment.show(supportFragmentManager)
}
override fun hideDialog() {
//code that hide the DialogFragment
}
})
Obviously in the Application class I cannot get the FragmentManager so this code doesn't works.
You can register an ActivityLifecycleCallbacks in the Application class to know which activity is currently running, and get the FragmentManager:
class MyApplication : Application() {
private var currentActivityRef : WeakReference<FragmentActivity?>? = null
private val supportFragmentManager : FragmentManager?
get() = currentActivityRef?.get()?.supportFragmentManager
override fun onCreate() {
super.onCreate()
registerActivityLifecycleCallbacks(object : ActivityLifecycleCallbacks {
override fun onActivityStarted(activity: Activity?) {
currentActivityRef = WeakReference(activity as? FragmentActivity)
}
override fun onActivityStopped(activity: Activity?) {
currentActivityRef = null
}
override fun onActivityPaused(activity: Activity?) {}
override fun onActivityResumed(activity: Activity?) {}
override fun onActivityDestroyed(activity: Activity?) {}
override fun onActivitySaveInstanceState(activity: Activity?,
outState: Bundle?) {
}
override fun onActivityCreated(activity: Activity?,
savedInstanceState: Bundle?) {
}
})
}
}

Finish android activity from another with Kotlin

I'm trying to finish an activity from another (android) with kotlin. I know the wat to do it with java is with the following code (https://stackoverflow.com/a/10379275/7280257)
at the first activity:
BroadcastReceiver broadcast_reciever = new BroadcastReceiver() {
#Override
public void onReceive(Context arg0, Intent intent) {
String action = intent.getAction();
if (action.equals("finish_activity")) {
finish();
// DO WHATEVER YOU WANT.
}
}
};
registerReceiver(broadcast_reciever, new IntentFilter("finish_activity"));
On the other activity:
Intent intent = new Intent("finish_activity");
sendBroadcast(intent);
For some reason converting the java activity to kotlin doesn't give a valid output, if someone could give me the correct syntax to do it properly with kotlin I will appreciate it
kotlin output (first activity) [OK]:
val broadcast_reciever = object : BroadcastReceiver() {
override fun onReceive(arg0: Context, intent: Intent) {
val action = intent.action
if (action == "finish_activity") {
finish()
// DO WHATEVER YOU WANT.
}
}
}
registerReceiver(broadcast_reciever, IntentFilter("finish_activity"))
kotlin output (2nd activity) [OK]
val intent = Intent("finish_activity")
sendBroadcast(intent)
ERROR: http://i.imgur.com/qaQ2YHv.png
FIX: THE CODE SHOWN IS RIGHT, YOU JUST NEED TO PLACE IT INSIDE THE onCreate FUNCTION
Simple code to finish a particular activity from another:
class SplashActivity : AppCompatActivity(), NavigationListner {
class MyClass{
companion object{
var activity: Activity? = null
}
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
MyClass.activity = this#SplashActivity
}
override fun navigateFromScreen() {
val intent = Intent(this,LoginActivity::class.java)
startActivity(intent)
}
}
Now call SplashActivity.MyClass.activity?.finish() from another activity to finish above activity.
The error Expecting member declaration is there because you wrote a statement (the function call) inside a class. In that scope, declarations (functions, inner classes) are expected.
You have to place your statements inside functions (and then call those from somewhere) in order for them to be executed.

Categories