Kotlin list all activities at runtime - android

I'm trying to make a simple debug activity that allows me to start any activity of my project at runtime.
As of right now, I've implemented a RecyclerView which gets its data from a list, in such fashion:
val activitiesList = ArrayList<Activity>()
activitiesList.add(FrameHomeActivity())
activitiesList.add(LeaderHomeActivity())
activitiesList.add(LoginActivity())
...
but this relies on me having to manually update this list when adding a new activity.
I've already managed to attach a clickListener to the RecyclerView, so that each activity can start successfully when an item is touched.
Is there a way to get all the activities in the project "dynamically", so that I don't have to update this code each time a new activity is added?

You don't call constructor of an Activity instead you fire an Intent . You get the Activity from PackageManager then you start it by name.
val activities = packageManager
.getPackageInfo(packageName, PackageManager.GET_ACTIVITIES).activities
val nameList= activities.map { it.name }
nameList here is List<String> it will contains fully qualified name of your Activity classes .
You can use this name to create an Intent to start the activity from adapter.
fun onClickItem(context:Context, activityName:String){
try {
val c = Class.forName(activityName)
val intent = Intent(context, c)
context.startActivity(intent)
} catch (ex: Exception) {
}
}

You can get a list of all activities using this code
getActivity()
.getPackageManager()
.getPackageInfo(getActivity().getPackageName(), PackageManager.GET_ACTIVITIES)
.activities
Edit - getActivity() can be replaced by this when calling from activity else when calling from fragment getActivity() will be required.

Related

Launch Activity from onClick method of Listview (Fragment)

I recently started learning Android development in Kotlin. I did follow this guide and everything went good.
Now I'm trying to merge the content of those two guides:
https://developer.android.com/training/contacts-provider/retrieve-details.html#kotlin
and
https://developer.android.com/guide/components/fragments#Example
in order to display the details of a Contact using Fragments. I'm having trouble to launch an activity in the onItemClick method (the guide uses a ListView):
override fun onItemClick(parent: AdapterView<*>, view: View?, position: Int, id: Long) {
// Get the Cursor
val cursor: Cursor? = (parent.adapter as? CursorAdapter)?.cursor?.apply {
// Move to the selected contact
moveToPosition(position)
// Get the _ID value
contactId = getLong(Companion.CONTACT_ID_INDEX)
// Get the selected LOOKUP KEY
//contactKey = getString(CONTACT_KEY_INDEX)
mContactKey = getString(Companion.CONTACT_KEY_INDEX)
// Create the contact's content Uri
contactUri = ContactsContract.Contacts.getLookupUri(contactId, mContactKey)
/*
* You can use contactUri as the content URI for retrieving
* the details for a contact.
*/
}
val intent = Intent().apply{
setClass(activity,DetailsActivity::class.java)
putExtra("contactID",contactId)
putExtra("mContackKey",mContactKey)
putExtra("contactUri",contactUri)
}
startActivity(intent)
}
If I create the Intent to start the activity as displayed in the guide, I get the compiler error "Inferred type is FragmentActivity?, but context was expected".
I changed then the Intent to either one of the following:
val intent = Intent().apply{
setClass(requireContext(),DetailsActivity::class.java)
putExtra("contactID",contactId)
putExtra("mContackKey",mContactKey)
putExtra("contactUri",contactUri)
}
startActivity(intent)
or
val intent = Intent().apply{
context?.let { setClass(it,DetailsActivity::class.java) }
putExtra("contactID",contactId)
putExtra("mContackKey",mContactKey)
putExtra("contactUri",contactUri)
}
startActivity(intent)
whit this, I do not get the compiler error, but in the Logcat I see the notice "W/ActivityThread: handleWindowVisibility: no activity for token android.os.BinderProxy#9dc9013"
Can you please point me to the correct way to instantiate an activity from the onClick method of a ListView inside a Fragment ? Thank you!
On a related note: do you recommend those guides or is their content obsolete ?
Edit: the full fragment class is here
Try below code for opening new activity from fragment :
activity?.let{
val intent = Intent (it, DetailsActivity::class.java)
intent.putExtra("contactID",contactId);
intent.putExtra("mContackKey",mContactKey);
intent.putExtra("contactUri",contactUri);
it.startActivity(intent)
}
startActivity() is a function in the Context class (of which Activity is a subclass), not of the Fragment class.
So you can make a direct bare call to startActivity() from within the code of a subclass of Context (as any Activity implementation is), but when you are calling it from a Fragment, you have to call it on a context: context.startActivity().
The Fragment.context property is nullable, so you can use requireContext().startActivity(). It won't be null when responding to clicks, so this is safe.

Is it possible to store data from two different parcelable data classes in one single intent to start a new Activitiy with it?

after researching a lot, I wasn't able to find the real solution for my problem. The most answers are based on attaching single parcelable object to an intent and starting a new activity from it.
In my case, I use data from two different parcelable classes. I have two different RecyclerView Adapter classes that live in the same activity.
So, when the user clicks on a data set from the first (parent) Adapter class, the View shows all available (child) data sets from the second Adapter class.
I want to save the parent object data set where the user clicked first, as well the child object where the user performed a long click at last. The both objects need to be attached on a same intent and when done, a new activity should be started. The data from the both objects (parent and child) has to be passed in the new acticity.
For the sake of clearness, I'm posting only the relevant parts of the code.
This is the part of the parent adapter class where I need to save the object data to a sort of "global" intent, my code would produce only a local intent instead (that's why it's commented only).
itemView.setOnClickListener {
notifyItemChanged(selectedPos)
selectedPos = adapterPosition
notifyItemChanged(selectedPos)
listener.onHeadItemClicked(weKopf.WENR, p1)
// save parent parcelable object data to "global" intent
// val intent = Intent(context, StorageGoodsActivity::class.java)
// intent.putExtra("weKopf", weKopf)
}
Here is the part of the code where I want to attach the child object data to the same intent and then start the new activity.
itemView.setOnLongClickListener{
notifyItemChanged(selectedPos)
selectedPos = adapterPosition
notifyItemChanged(selectedPos)
listener.onPositionLongClicked(wePos.lfdNr.toInt())
/**
* setting-up for passing objects between activities
*/
val intent = Intent(context, StorageGoodsActivity::class.java)
//intent.putExtra("weKopf", weKopf)
intent.putExtra("wePos", wePos)
context.startActivity(intent)
true
What is the most elegant way to do this? My idea was to store the intent into a shared component, but this intent should live as long as the user clicks the back button to return to the old activity and chooses new parent-child constellation after that. If so, will this created item be thrown away, or it still lives in the shared component?
I'm quite new with Android Studio and Kotlin and I appreciate every help or advice. Thank you so much in advance.
You can have as many extras as you want in an Intent, so long as each gets its own name. The only limitation is a limitation on the total size of an Intent (about 2 MB iirc).
The solution of this problem was sort of small work around. It was impossible to attach the data objects from both adapter classes in the same intent, because the new activity can be started from only one adapter.
In this case, I passed the data object from the first (parent) adapter to a SharedPreferences object, the object was converted with Gson into his JSON representation.
itemView.setOnClickListener {
notifyItemChanged(selectedPos)
selectedPos = adapterPosition
notifyItemChanged(selectedPos)
listener.onHeadItemClicked(weKopf.WENR, p1)
/**
* store object in SharedPreferences
*/
val sharedPreferences:SharedPreferences = context.getSharedPreferences("StoreHeadObj", MODE_PRIVATE)
val editor : SharedPreferences.Editor = sharedPreferences.edit()
val gson = Gson()
val json = gson.toJson(weKopf)
editor.putString("JSONString", json)
editor.commit()
The second (child) object was passed on an intent and the new Activity was started.
itemView.setOnLongClickListener{
notifyItemChanged(selectedPos)
selectedPos = adapterPosition
notifyItemChanged(selectedPos)
listener.onPositionLongClicked(wePos.lfdNr.toInt())
/**
* setting-up for passing objects between activities
*/
val intentPos = Intent(context, StorageGoodsActivity::class.java)
intentPos.putExtra("wePos",wePos)
context.startActivity(intentPos)
true
}
In the new Activity, I'm retrieving the data from both objects. First of all, I'm converting the JSON String to a data object, after that I'm taking the data out of the Intent. So, both of the parcelable data objects are now ready to use in the new Activity.
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(getContentView())
/**
* get data from clicked weKopf-Object using Gson, at the end clear SharedPreferences
*/
val sharedPreferences:SharedPreferences = getSharedPreferences("StoreHeadObj", Context.MODE_PRIVATE)
val gson = Gson()
val json:String = sharedPreferences.getString("JSONString", "")
val weKopf:WeKopf = gson.fromJson(json, WeKopf::class.java)
/**
* get data from long-clicked wePos-Object using Intent
*/
val extras = getIntent().getExtras()
val wePos = extras.getParcelable<WePos>("wePos")
}

How to launch an Android Activity when multiple pages of the App have Same Activity name in Kotlin?

I have a screen consisting list of Results. Clicking on each of the result takes me to different item but with the same Activity Name(TestResultActivityDetails).
How can I launch each Result as different Activity Launcher?
Is there any option to use index on list items to launch Activity?
val intent = Intent(TestResultActivityDetails::class.java)
startActivity(intent)
Here, TestResultActivityDetails::class.java is the activity name which is shared with all the list of Results.
To pass data to an Activity you need to use Intent extras. Your posted code would become as this:
val intent = Intent(TestResultActivityDetails::class.java).apply {
/* You pass data as extras, with a key and the actual value.
* KEY VALUE */
putExtras("clickedIndex", index)
}
startActivity(intent)
Then, in TestResultActivityDetails you would do:
override fun onCreate(saveInstance: Bundle?) {
...
/* You get your data back from the Intent extras, using previous key.
* KEY FALLBACK if not found */
val index = intent.extras?.getInt("clickedIndex") ?: -1
}

Sending information between Activities in Kotlin

fun kullaniciOlustur2(view: View){
val intent = Intent(applicationContext,KullaniciOlustur2::class.java)
intent.putExtra("input",makeUsername.text.toString())
intent.putExtra("input2",makeUserphone.text.toString())
startActivity(intent)
}
Mainactivity2 starts here..before this, I was using
val intent = intent
val received: String = intent.getStringExtra("input")
makeUsername.text = received
But this method doesn't work anymore.
I tried using getIntent() but couldn't get anything
val intent = getIntent()
Try this code
Activity 1
val intent = Intent(FirstActivity.this,SecondActivity::class.java) //not application context
intent.putExtra("input",makeUsername.text.toString())
intent.putExtra("input2",makeUserphone.text.toString())
startActivity(intent)
Activity 2
inside onCreate() method use
val stringOne = getIntent().getStringExtra("input")
Or more cleaner way is
val extras = getIntent().getExtras()
if (null != extras) {
val value = extras.getString("input")
//The key argument here must match that used in the other activity
}
and please check similar answers in Java, you may be able to get the Idea here already told in another answer.
I also Use Anko to remove this kind of boilerplate code
I recommend using Kotlin Anko, there are plenty of methods that will help you to remove this boilerplate code
check Anko Intents here
Use Below Code:
The Code is same as Java .Just difference is kotlin do not have
1. No semicolon at the end.
2. to call another activity kotlin use ::
ex.KotlinActivity::class.java
startActivity(Intent(this, KotlinActivity::class.java).putExtra("DataTrasfer", ""))
To Get Value:
intent.getStringExtra("DataTrasfer")

How can I get access to a fragment method from another activity

I have a fragment which summons a custom CursorAdaper and display it on a ListView
. The thing is I want to change the cursor by changeCursor() from another activity when I add new data, How can I get access to the CursorAdapter displayed on the fragment?
Essentially you have to pass data from one Activity to another and let the fragment of you choice receive the data (each fragment of a given Activity may get the Intent that started/restarted/resumed the Activity).
Consider this code
-- pass data:
String[] myListEntries = getNewListContents();
Intent updateList = new Intent(this, ActivityThatListFragmentBelongsTo.class);
updateList.putExtra("updated_list", myListEntries);
startActivity(updateList);
-- receive data (in fragment):
#Override
public void onResume() {
Intent wasStartedWithData = getActivity().getIntent();
String[] updatedList = wasStartedWithData.getStringArrayExtra("updated_list");
// pass updatedList to adapter
}
Now you might actually have more complicated data than an Array of Strings. In this case
you could create a class that implements 'Parcelable' (http://developer.android.com/reference/android/os/Parcelable.html) and call
putExtra(Parcelable parcel) / getParcelableExtra(String name) on the Intent.

Categories

Resources