How to delete audio file record and remove checked recyclerview item after user clicks 'Yes' on alert dialog - android-recyclerview

I have an alert dialog that pops up when the user clicks the delete button.
Alert dialog
When the user clicks yes, I want the recyclerview item they selected to be removed and delete the file from storage.
The onClickListener that shows the alert dialog
findViewById<ImageButton>(R.id.btnDelete).setOnClickListener{
val mAlertDialog = AlertDialog.Builder(this#GalleryActivity)
mAlertDialog.setIcon(R.drawable.ic_delete_dialog) //set alertdialog icon
mAlertDialog.setTitle("Delete") //set alertdialog title
mAlertDialog.setMessage("Delete audio?") //set alertdialog message
mAlertDialog.setPositiveButton("Yes") { dialog, id ->
//perform some tasks here
Toast.makeText(this#GalleryActivity, "Deleted", Toast.LENGTH_SHORT).show()
}
mAlertDialog.setNegativeButton("No") { dialog, id ->
//perform som tasks here
Toast.makeText(this#GalleryActivity, "No", Toast.LENGTH_SHORT).show()
}
mAlertDialog.show()
}

Some folks enjoy code to words, so I will do the two
Steps
So let assume you are getting your AudioRecord from local database or remote source, I believe you will already have a function or an endpoints to delete. now let assume the endpoint or database is collecting array of checked item. maybe array of the Id.
So in your
class DeleteRecordFragment: DialogFragment() {
//Define an array collecting list of checked audio let assume
private var checkedAudio : ArrayList<String> = arrayOf()
//Let assume your database to be
private var database = MyDatabase()
as you check each item, you add the ID to > checkedAudio
now time to delete.
inside Yes dialog.
just call the function that delete and pass in the id of the array you are deleting
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
return activity?.let{ it ->
val alertBuilder = AlertDialog.Builder(it)
alertBuilder.setTitle("Delete record")
alertBuilder.setMessage("Are you sure you want to delete?")
alertBuilder.setPositiveButton("Yes",
DialogInterface.OnClickListener{ dialog, id ->
Log.d("delete", "Yes Pressed")
database.deleteSelectedItem(checkedAudio = checkedAudio)
//Now fetch new list from the data source or database.
database.fetchNewAudio()
//for adapter to be aware to sync., you can call
mAdapter.notifyDataSetChanged()
//but if you are using diffUtils, this should sync automatically
}).setNegativeButton("Cancel",
DialogInterface.OnClickListener{dialog, id ->
Log.d("delete", "Cancel Pressed")
})
alertBuilder.create()
} ?: throw IllegalStateException("Exception !! Activity is null !!")
}

Related

Recyclerview list item vanishes after closing and restarting app. KOTLIN

I just implemented an undo function for a snackbar in my to do list app. Everything works perfectly until you close the app and open it again. Once it's opened the list item that was just restored/undone does not show up. However if you exit the app and NOT close it. The list item is still there.
override fun onViewSwiped(position: Int) {
val removedItem = list.removeAt(position)
notifyItemRemoved(position)
updateNotesPositionInDb()
val snackBar = Snackbar
.make((context as Activity).findViewById(R.id.mainLayout), "Task deleted", Snackbar.LENGTH_LONG)
.setAction("Undo"){
undoDeleteTask(removedItem.ID)
list.add(position, removedItem)
notifyItemInserted(position)
updateNotesPositionInDb()
}
.addCallback(object : BaseTransientBottomBar.BaseCallback<Snackbar>() {
override fun onShown(transientBottomBar: Snackbar?) {
}
override fun onDismissed(transientBottomBar: Snackbar?, event: Int) {
deleteTask(removedItem.ID)
updateNotesPositionInDb()
}
})
snackBar.show()
}
fun undoDeleteTask(id: String) {
val db = mHelper.readableDatabase
db.insert(TaskContract.TaskEntry.TABLE, "id=$id", null)
db.close()
}
Let me explain again. When a user swipes the item and they press undo. The item will be added back to the recyclerview. If a user were to exit the app, but not close it. The item will store be there. However if the user were to close the app then open it, the recently restored item is no longer there.
Your Snackbar callback always deletes the item from the database
override fun onDismissed(transientBottomBar: Snackbar?, event: Int) {
deleteTask(removedItem.ID)
updateNotesPositionInDb()
}
From the docs:
public void onDismissed (B transientBottomBar, int event)
Called when the given BaseTransientBottomBar has been dismissed, either through a time-out, having been manually dismissed, or an action being clicked.
So even if the user clicks that undo action, the dismissed callback runs and always deletes from the DB. You need to check that event parameter in the callback, and make sure it doesn't do the delete operation if it was a click that dismissed it:
int: The event which caused the dismissal. One of either: DISMISS_EVENT_SWIPE, DISMISS_EVENT_ACTION, DISMISS_EVENT_TIMEOUT, DISMISS_EVENT_MANUAL or DISMISS_EVENT_CONSECUTIVE.
I'm guessing just an if (event != DISMISS_EVENT_ACTION) will do it!
The reason you're only seeing this on a restart is that I'm guessing deleteTask(removedItem.ID) doesn't update your in-memory list or the RecyclerView's adapter - your flow here seems to be
remove from the in-memory list
if the user clicks undo then restore it
if they don't then remove it from the DB as well
So what happens here is it's removed from the in-memory list, then it's restored, then the data is removed from the DB. So long as the app stays in memory you'll be able to see the data that's in the list. But as soon as you restart the app, it has to restore the list by reading the DB, and the item isn't in there anymore

How to reference a button from different view in Fragment. Kotlin

I want to achieve a simple task. I have an invisible button in one layout and a have a Fragment. When this function is executed I want the button in the other layout to become visible. However this layout with the button is not in the Fragment layout, which means I have to reference that button in the Fragment, but I don't know how to go about that.
This is what Fragment will look like to first time users. The images you see are in a recyclerview which inflates a layout. That layout has the invisible button.
Fragment class
//item subscribed
if (subscribeValueFromPref) {
subscribeAbstract.visibility = View.GONE
// abstractDownload.visibility = View.VISIBLE
} else {
subscribeAbstract.visibility = View.VISIBLE
// abstractDownload.visibility = View.VISIBLE
}
}
When this line of code executed the button in the fragment is gone and the button from the other layout becomes visible. As you can see I put two strokes in front of that code. Once the code has been executed the layout should look like this.
Summary
I want to reference a button in another layout from a fragment class.
Do you even need to communicate between fragments? Your example looks like a single fragment with a subscribe button, and a RecyclerView that contains items which have a button that can be displayed or hidden. You can just make that part of your Adapter's state, like this:
class MyAdapter : RecyclerView.Adapter<ViewHolder>() {
var showDownloadButtons = false
set(value) {
field = value
// call this to update the display (calls onBindViewHolder for items)
notifyDataSetChanged()
}
override fun onBindViewHolder(viewHolder: ViewHolder, position.Int) {
...
// when displaying an item, show or hide the download button as appropriate
viewHolder.downloadButton.visibility = if (showDownloadButtons) VISIBLE else INVISIBLE
}
}
Then in your fragment, when you work out your UI state based on that subscription value, you can just handle your main button and tell the adapter what to display:
if (subscribeValueFromPref) {
subscribeAbstract.visibility = View.GONE
adapter.showDownloadButtons = true
} else {
subscribeAbstract.visibility = View.VISIBLE
adapter.showDownloadButtons = false
}

How Should I implement onbackpressed() in fragment kotlin [duplicate]

I am using The new Navigation Architecture Component in android and I am stuck in clearing the navigation stack after moving to a new fragment.
Example:
I am in the loginFragment and I want this fragment to be cleared from the stack when I navigate to the home fragment so that the user will not be returned back to the loginFragment when he presses the back button.
I am using a simple NavHostFragment.findNavController(Fragment).navigate(R.id.homeFragment) to navigate.
Current Code :
mAuth.signInWithCredential(credential)
.addOnCompleteListener(getActivity(), new OnCompleteListener<AuthResult>() {
#Override
public void onComplete(#NonNull Task<AuthResult> task) {
if (task.isSuccessful()) {
NavHostFragment.findNavController(LoginFragment.this).navigate(R.id.homeFragment);
} else {
Log.w(TAG, "signInWithCredential:failure", task.getException());
}
}
});
I tried using the NavOptions in the navigate(), but the back button is still sending me back to the loginFragment
NavOptions.Builder navBuilder = new NavOptions.Builder();
NavOptions navOptions = navBuilder.setPopUpTo(R.id.homeFragment, false).build();
NavHostFragment.findNavController(LoginFragment.this).navigate(R.id.homeFragment, null, navOptions);
First, add attributes app:popUpTo='your_nav_graph_id' and app:popUpToInclusive="true" to the action tag.
<fragment
android:id="#+id/signInFragment"
android:name="com.glee.incog2.android.fragment.SignInFragment"
android:label="fragment_sign_in"
tools:layout="#layout/fragment_sign_in" >
<action
android:id="#+id/action_signInFragment_to_usersFragment"
app:destination="#id/usersFragment"
app:launchSingleTop="true"
app:popUpTo="#+id/main_nav_graph"
app:popUpToInclusive="true" />
</fragment>
Second, navigate to the destination, using above action as parameter.
findNavController(fragment).navigate(
SignInFragmentDirections.actionSignInFragmentToUserNameFragment())
See the docs for more information.
NOTE: If you navigate using method navigate(#IdRes int resId), you won't get the desired result. Hence, I used method navigate(#NonNull NavDirections directions).
I think your question specifically pertains on how to use the Pop Behavior / Pop To / app:popUpTo (in xml)
In documentation,
Pop up to a given destination before navigating. This pops all non-matching destinations from the back stack until this destination is found.
Example (Simple Job hunting app)
my start_screen_nav graph is like this:
startScreenFragment (start) -> loginFragment -> EmployerMainFragment
-> loginFragment -> JobSeekerMainFragment
if I want to navigate to EmployerMainFragment and pop all including startScreenFragment then the code will be:
<action
android:id="#+id/action_loginFragment_to_employerMainFragment"
app:destination="#id/employerMainFragment"
app:popUpTo="#+id/startScreenFragment"
app:popUpToInclusive="true" />
if I want to navigate to EmployerMainFragment and pop all excluding startScreenFragment then the code will be:
<action
android:id="#+id/action_loginFragment_to_employerMainFragment"
app:destination="#id/employerMainFragment"
app:popUpTo="#+id/startScreenFragment"/>
if I want to navigate to EmployerMainFragment and pop loginFragment but not startScreenFragment then the code will be:
<action
android:id="#+id/action_loginFragment_to_employerMainFragment"
app:destination="#id/employerMainFragment"
app:popUpTo="#+id/loginFragment"
app:popUpToInclusive="true"/>
OR
<action
android:id="#+id/action_loginFragment_to_employerMainFragment"
app:destination="#id/employerMainFragment"
app:popUpTo="#+id/startScreenFragment"/>
In my case i needed to remove everything in the back Stack before i open a new fragment so i used this code
navController.popBackStack(R.id.fragment_apps, true);
navController.navigate(R.id.fragment_company);
the first line removes the back Stack till it reaches the fragment specified in my case it's the home fragment so it's removes all the back stack completely , and when the user clicks back in the fragment_company he closes the app.
Going to add another answer here as none of the above worked for me ... we have multiple nav graphs.
findNavController().navigate(R.id.dashboard_graph,null,NavOptions.Builder().setPopUpTo(findNavController().graph.startDestination, true).build())
This was the only way that I could successfully clear the full back stack. Google really need to make this simpler.
NOTE: Clear task is deprecated, official description is
This method is deprecated. Use setPopUpTo(int, boolean) with the id of the NavController's graph and set inclusive to true.
Old Answer
If you don't wanna go through all that fuzz in code, you can simply check Clear Task in Launch Options in properties of the action.
Edit: As of Android Studio 3.2 Beta 5, Clear Task is no longer visible in Launch Options window, but you can still use it in navigation's XML code, in action tag, by adding
app:clearTask="true"
NavController navController
=Navigation.findNavController(requireActivity(),
R.id.nav_host_fragment);// initialize navcontroller
if (navController.getCurrentDestination().getId() ==
R.id.my_current_frag) //for avoid crash
{
NavDirections action =
DailyInfoSelectorFragmentDirections.actionGoToDestionationFragment();
//for clear current fragment from stack
NavOptions options = new
NavOptions.Builder().setPopUpTo(R.id.my_current_frag, true).build();
navController.navigate(action, options);
}
I finally figure it out thanks to How to disable UP in Navigation for some fragment with the new Navigation Architecture Component?
I had to specify .setClearTask(true) as a NavOption.
mAuth.signInWithCredential(credential)
.addOnCompleteListener(getActivity(), new OnCompleteListener<AuthResult>() {
#Override
public void onComplete(#NonNull Task<AuthResult> task) {
if (task.isSuccessful()) {
Log.d(TAG, "signInWithCredential:success");
NavOptions.Builder navBuilder = new NavOptions.Builder();
NavOptions navOptions = navBuilder.setClearTask(true).build();
NavHostFragment.findNavController(LoginFragment.this).navigate(R.id.homeFragment,null,navOptions);
} else {
Log.w(TAG, "signInWithCredential:failure", task.getException());
}
}
});
use this code
navController.navigateUp();
then call new Fragment
android version 4.1.2
Here is how I am getting it done.
//here the R.id refer to the fragment one wants to pop back once pressed back from the newly navigated fragment
val navOption = NavOptions.Builder().setPopUpTo(R.id.startScorecardFragment, false).build()
//now how to navigate to new fragment
Navigation.findNavController(this, R.id.my_nav_host_fragment)
.navigate(R.id.instoredBestPractice, null, navOption)
For
// Navigation library
def nav_version = "2.3.5"
implementation "androidx.navigation:navigation-fragment-ktx:$nav_version"
implementation "androidx.navigation:navigation-ui-ktx:$nav_version"
This solution work for me
findNavController().popBackStack(R.id.<Current Fragment Id In NavGraph>, true)
findNavController().navigate(R.id.< Your Destination Fragment in NavGraph>)
In my case where I used Navigation component with NavigationView (menu drawer):
1.
mNavController.popBackStack(R.id.ID_OF_FRAGMENT_ROOT_TO_POP, true)
mNavController.navigate(
R.id.DESTINATION_ID,
null,
NavOptions.Builder()
.setPopUpTo(R.id.POP_TO_DESTINATION_ID, true)
.build()
)
I wanted to clear the stack after clicking on logout on side menu drawer!
Hope that helped someone!
You can override the back pressed of the base activity like this :
override fun onBackPressed() {
val navigationController = nav_host_fragment.findNavController()
if (navigationController.currentDestination?.id == R.id.firstFragment) {
finish()
} else if (navigationController.currentDestination?.id == R.id.secondFragment) {
// do nothing
} else {
super.onBackPressed()
}
}
Non of the solutions above works for me.
After spending hours on it, here is my solution:
Note: I have multiple nav_graphs and switching between fragments in different nav_graphs.
Define your action as below in xml:
<action
android:id="#id/your_action_id"
app:destination="#id/the_fragment_id_you_want_to_navigate_to"
app:popUpTo="#id/nav_graph_which_contains_destination_fragment"
app:popUpToInclusive="true" />
Navigate using action above from your Java/Kotlin code:
findNavController(R.id.your_nav_name)?.apply {
navigate(R.id.your_action_id)
backQueue.clear()
}
For Jetpack Compose ❤️
navHostController.navigate(Routes.HOME) {
this.popUpTo(Routes.ONBOARDING) {
this.inclusive = true
}
}
I struggled for a while to prevent the back button from going back to my start fragment, which in my case was an intro message that should only appear once.
The easy solution was to create a global action pointing to the destination that the user should stay on. You have to set app:popUpTo="..." correctly - set it to the destination you want to get popped off. In my case it was my intro message. Also set app:popUpToInclusive="true"
In my case, I am using 2 different activities that have their own respective navigation graphs. My first activity is the host for "nav_graph" and has fragments that deal with authentification and the second is the host for "nav_graph_home". Here you can see the settings I have done for nav_graph.
nav_graph example
Then back in my code for the login fragment, I have this written :
findNavController().navigate(R.id.action_logInFragment_to_nav_graph_home)
After the user logs in and they hit the back button the app will close. Remember to set the pop behavior so it pops till your current navigation graph that contains your login fragment without including it.
Edit:
After this, the up button still appears in the top bar. To avoid this behavior we need to tell the first activity which fragments are considered top level. To do this simply add in the params list of the "setupActionBarWithNavController()" method in addition to the nav host fragment an App bar configuration that contains a set of the home fragment of your first navigation graph and your second. Your code should look something like this:
class MainActivity : AppCompatActivity(R.layout.activity_main) {
private lateinit var navController: NavController
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
// Retrieve NavController from the NavHostFragment
val navHostFragment = supportFragmentManager
.findFragmentById(R.id.nav_host_fragment) as NavHostFragment
navController = navHostFragment.navController
// Set up the action bar for use with the NavController
setupActionBarWithNavController(navController, AppBarConfiguration(setOf(R.id.logInFragment,R.id.homeFragment)))
}
/**
* Handle navigation when the user chooses Up from the action bar.
*/
override fun onSupportNavigateUp(): Boolean {
return navController.navigateUp() || super.onSupportNavigateUp()
}
}
This is my first ever contribution, hope this helps.
For androidx.compose version 1.2.0+
I had a few issues with lower versions but 1.2 plus (beta at the time of writing this), works perfectly.
Better syntax for the navGraph in Compose:
navController.navigate(item.name) {
navController.graph.startDestinationRoute?.let { route ->
// use saveState = false to NOT save the state for the popped route
popUpTo(route) { saveState = true }
}
launchSingleTop = true
restoreState = true
}
I am using button to navigate to other fragments so on each button click I am doing this.
val navOptions = NavOptions.Builder().setLaunchSingleTop(true).setPopUpTo(R.id.homeFragment, false).build()
findNavController(R.id.mainNavHostFragment).navigate(R.id.destination, null, navOptions)
You can do as simple as:
getFragmentManager().popBackStack();
If you want to check the count you can do as:
getFragmentManager().getBackStackEntryCount()

Switch focus to reopened tab in Chrome - selenium

I am trying to achieve the below flow in selenium ,
click on a link to open new tab(child1) from parent window
close the driver (child1) after performing certain actions
Open the same link(child1) again to perform another set of actions
I am able to achieve the first two steps successfully by switching focus. But I am stuck at the third step where I am not able to focus on the same reopened tab. I get the below error,
org.openqa.selenium.StaleElementReferenceException: stale element reference: element is not attached to the page document
public class abc {
String currentWindow = driver.getWindowHandle();
public void action1() {
//Click on main menu that open a new tab
for (String handle : driver.getWindowHandles()) {
if (!handle.equals(currentWindow)) {
driver.switchTo().window(handle);
}
//Perform the actions
driver.close();
driver.switchTo().window(currentWindow);
}
public void action2(){
//Click on main menu which reopen the same tab
for (String handle : driver.getWindowHandles()) {
if (!handle.equalsIgnoreCase(currentWindow)) {
System.out.println("Port Response : switch focus");
driver.switchTo().window(handle);
break;
}
}
//perform a set of actions
driver.close();
driver.switchTo().window(currentWindow);
}
}//end of abc
I want to create the actions more like a reusable independent action so I would like to close all tabs and reopen them again for other set of action. Please let me know if you need any more details on this.

Is it possible to dynamic set menu items in a wxMenu?

Hi: I have in a wxFrame a menubar with this wxMenus: File|Edit|Personal. During the frame creation the wxMenu Personal has two wxMenuItems (Information and Need Help), which tells the user that must fill the form and help abou how to fill it. After the user fills the form with personal information, the menu is populated with new items: send, clear, check and remove, deleting/removing the previous ones... At this point I manage to trap the event, like this:
Bind (wxEVT_MENU_OPEN, &MyFrame::processMenuPersonal, this);
// the method
void MyFrame::processMenuPersonal (wxMenuEvent& event) {
wxMenu *menu = event.GetMenu ();
wxMenuItem *item = menu->FindItem (IDM_PERSONAL_EMPTY);
if (item) {
// here I want to dynamic add items and remove the current ones
}
}
Any ideas about how to do this?
Yes. Just append as submenu and treat it like any conditional statement. Here is a sample
void MyFrame::processMenuPersonal (wxMenuEvent& event) {
wxMenu *menu = event.GetMenu ();
wxMenu *item = menu->GetMenu(YOUR_MENU_INDEX);
menu->AppendSubMenu(item, wxT("User Items"));
if (YouConditionHere) {
item->Append(wxID_ANY, "Item 1")
item->Append(wxID_ANY, "Item 2")
item->Append(wxID_ANY, "Item 3")
}
}
Note that Items 1,2,3 are the information you want to append after processing your data (in your case deleting old entries)