Popupmenu is closing when screen rotated - android

I am using a popupmenu like below;
popupMenu = PopupMenu(view.context,view);
val menuInflater:MenuInflater = popupMenu!!.menuInflater;
menuInflater.inflate(R.menu.profil_image_degistir_menu,popupMenu!!.menu);
this.profilImage = profilImage as ImageView;
popupMenu!!.setOnMenuItemClickListener(object:PopupMenu.OnMenuItemClickListener{
override fun onMenuItemClick(item: MenuItem?): Boolean {
if(item?.itemId == R.id.action_cam){
camContext = view.context;
kameraIzin = ContextCompat.checkSelfPermission(view.context,Manifest.permission.CAMERA);
if(kameraIzin != PackageManager.PERMISSION_GRANTED){
requestPermissions(arrayOf(Manifest.permission.CAMERA), CAMERA_REQUEST_CODE);
}else{
val intent: Intent = Intent(MediaStore.ACTION_IMAGE_CAPTURE);
startActivityForResult(intent,CAMERA_REQUEST_CODE);
}
}else if(item?.itemId == R.id.action_localStorage){
}
return true;
}
});
popupMenu!!.show();
But when I rotate the screen menu is closing? How to prevent this?Can u help me.

When you rotate the device a configuration change takes place; this causes the activity to be recreated again. To control configuration changes you should use the onSaveInstanceState method. This method execute when a configuration change occur. In this case you should save a boolean value indicating if your popup is open or not.
#Override
public void onSaveInstanceState(Bundle savedInstanceState) {
super.onSaveInstanceState(savedInstanceState);
savedInstanceState.putBoolean("shouldShowPopup", isPopupOpen);
}
Then, on your onCreate method, you should check if the Bundle param is null. If is not null you can get the value we have saved before and show (or not) the popup.

In manifest file you need to add android:configChanges="orientation" in the activity that hosts this popup menu.

Related

Android SQLITE Image issue: Attempt to invoke virtual method 'java.lang.String android.net.Uri.toString()' on a null object reference

the problem: if user doesn't upload any image, app crashes, as cannot store previous image...
Overview: my application consists of Category and Editor activities.
Editor activity has a button to upload image and save the activity.
Category activity displays image from Editor activity.
Problem here --> once the user returns back to edit details in Editor activity, app crashes with below error:
Attempt to invoke virtual method 'java.lang.String android.net.Uri.toString()' on a null object reference
If user uploads an image again - same or different, doesn't matter, the app works well.
The goal is to keep the image if the user doesn't want to upload a new one, just like the app keeps the values for EditText fields:
private void saveInventory() {
// Read from input fields
// Use trim to eliminate leading or trailing white space
String nameString = mNameEditText.getText().toString().trim();
String infoString = mAdditionalInfoEditText.getText().toString().trim();
String priceString = mPriceEditText.getText().toString().trim();
String quantityString = mQuantityTextView.getText().toString().trim();
String image = actualUri.toString(); <---- error here
Image is selected in this method:
#Override
public void onActivityResult(int requestCode, int resultCode, Intent resultData) {
// The ACTION_OPEN_DOCUMENT intent was sent with the request code READ_REQUEST_CODE.
// If the request code seen here doesn't match, it's the response to some other intent,
// and the below code shouldn't run at all.
if (requestCode == SELECT_AN_IMAGE_REQUEST && resultCode == Activity.RESULT_OK) {
// The document selected by the user won't be returned in the intent.
// Instead, a URI to that document will be contained in the return intent
// provided to this method as a parameter. Pull that uri using "resultData.getData()"
if (resultData != null) {
actualUri = resultData.getData();
mPhotoImageView.setImageURI(actualUri);
}
}
}
I believe that saving the activity state and restoring it might solve the problem (code below), but I don't know where to paste it in my code....
// Save the activity state when it's going to stop.
// #Override
// protected void onSaveInstanceState(Bundle outState) {
// super.onSaveInstanceState(outState);
// outState.putParcelable("actualUri", actualUri);
// }
// Recover the saved state when the activity is recreated.
// #Override
// protected void onRestoreInstanceState(#NonNull Bundle savedInstanceState) {
// super.onRestoreInstanceState(savedInstanceState);
// actualUri= savedInstanceState.getParcelable("actualUri");
// }
Probably someone knows how to keep same image upon saving the editor activity. Thank you.
P.S. The finish activity quits the editor, and returns to Catalog. All data is saved in Database - apart from image.
#Override public boolean onOptionsItemSelected(MenuItem item) {
// User clicked on a menu option in the app bar overflow menu switch (item.getItemId())
{ // Respond to a click on the "Save" menu option case R.id.action_save:
// Save to database saveInventory();
// Exit activity finish(); return true;
I have solved the isssue.
I had to remove the line that causes an error in 'Save' class, and create a separate validation in this class:
if (actualUri== null) {
Toast.makeText(this, getString(R.string.image_required), Toast.LENGTH_SHORT).show();
return hasAllRequiredValues;
} else {
values.put(InventoryEntry.COLUMN_INVENTORY_IMAGE, actualUri.toString());
}

Avoid an item from Action Bar from being double clicked

I have designed an action bar for my Android app. In this action bar there's a button that launches a Dialog Activity used to configure my app's behavior. If I double click this button fast enough, I'm able to order the Dialog Activity to be launched twice before it actually appears, and then it appears duplicated and visually overlapped and I don't want this. I tried to create some sort of lock-down mechanism but it is not working because my Dialog Activity is launched only after all the code in my Main Activity calling method (onOptionsItemSelected) is executed. Is there a way to avoid this form happening?
My code is:
#Override
public boolean onOptionsItemSelected(MenuItem item) {
//ensure only one element from the option menu is launched at once (if you double click fast you could launch two)
Log.e("test", "onOptionsItemSelected ");
if(optionItemAlreadySelected == false)
{
optionItemAlreadySelected = true;
int id = item.getItemId();
if (id == R.id.action_sound_mode) {
//item.setVisible(false);
Intent intent = new Intent(this, SoundConfigurationActivity.class);
startActivity(intent);
optionItemAlreadySelected = false; //this code is executed before the activity is started!!!
return true;
}
}
return super.onOptionsItemSelected(item);
}
Is there a way to know when the Dialog Activity has already being closed and lock the opportunity to open it once again until then.
Kotlin
It's a screen(Activity, Fragment) based solution to avoid a double tap on menu action.
Add below global variable to your activity/fragment containing onOptionsItemSelected function.
private var previousClickTimeMillis = 0L
Write below function anywhere in the project i.e Utils.
fun singleSafeClick(
previousClickTimeMillis: Long,
block: (previousClickTimeMillis: Long) -> Unit) {
val currentTimeMillis = System.currentTimeMillis()
if (currentTimeMillis < previousClickTimeMillis || currentTimeMillis >= previousClickTimeMillis + OnSingleClickListener.DELAY_MILLIS) {
block(currentTimeMillis)
}
}
Write your triggering code as below.
override fun onOptionsItemSelected(item: MenuItem): Boolean {
when (item.itemId) {
R.id.action_delete -> {
singleSafeClick(previousClickTimeMillis) { tappedTime ->
previousClickTimeMillis = tappedTime
// Write Yyur code here
}
}
}
}
You can use a boolean variable to track the state of your Dialog. When you click the button you set mDialogShown = true to block any other show dialog requests.
Now when the user presses Back button and the Dialog is closed onActivityResult is called.
At this point your are sure that the Dialog was closed.
I assumed your code is inside an Activity:
class MainActivity extend Activity {
static final int SHOW_DIALOG_REQUEST = 1; // The request code
static boolean mDialogShown = false; // True if dialog is currently shown
#Override
public boolean onOptionsItemSelected(MenuItem item) {
int id = item.getItemId();
if (id == R.id.action_sound_mode) {
showDialog();
return true;
}
return super.onOptionsItemSelected(item);
}
private void showDialog() {
if (!mDialogShown) {
mDialogShown = true;
Intent intent = new Intent(this, SoundConfigurationActivity.class);
startActivityForResult(intent, SHOW_DIALOG_REQUEST);
}
}
#Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
// Check which request we're responding to
if (requestCode == SHOW_DIALOG_REQUEST) {
mDialogShown = false;
}
}
}
Documentation
https://developer.android.com/training/basics/intents/result.html
https://developer.android.com/guide/topics/ui/dialogs.html#ActivityAsDialog

How to know when Activity is finished in android?

In my android app I want to change the input method. So I start a new Activity which shows the language settings in the device. Then user can change it. However then I want to know that if the user has changed it. So I wrote a function for that also. My code so far is...
Intent enableIME = new Intent(android.provider.Settings.ACTION_INPUT_METHOD_SETTINGS);
startActivityForResult(enableIME,0);
if(isInputMethodEnabled()){
activateshadow.setBackgroundDrawable(getResources().getDrawable(R.drawable.button_pressed));
activateshadow.setText("Deactivate Shadow");
prefs.edit().putBoolean("Activate", false).commit();
}else{
Toast.makeText(MainActivity.this,"You haven't change the input method to simpleIME.In order to activate you must change it.",Toast.LENGTH_SHORT).show();
}
my is inputMethodEnabled function is....
public boolean isInputMethodEnabled() {
boolean isIME ;
String id = Settings.Secure.getString(getApplicationContext().getContentResolver(), Settings.Secure.DEFAULT_INPUT_METHOD);
String [] name = id.split("/.");
// Toast.makeText(MainActivity.this,"s:"+name[1]+":s",Toast.LENGTH_SHORT).show();
if(name[1].contains("SimpleIME") ){
isIME = true ;
}else{
isIME = false;
}
// Toast.makeText(MainActivity.this,"Returning..."+isIME,Toast.LENGTH_SHORT).show();
return isIME;
}
if(isInputMethodEnabled()) always fails because when the new intent(settings) opens and it take some time to change the input method to simpleIME . How to fix this problem?
You catch when a launched Activity returns in onActivityResult. The requestCode you supplied to startActivityForResult will be a parameter, as will the Activity's result. The Activity may also set other data which you didn't ask about.
public void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode == 555) {//555 is the intent ID you gave in startActivityForResult(enableIME,555);
if (resultCode == /*Result1*/)
//Do something
else {
//Do something else
}
}
}
You need a unique id when calling startActivityForResult(enableIME,0);
startActivityForResult(enableIME, 555);
Better still replace 555 with a named variable.
if u look at android life cycle, when activity is finished whole android call onDestroy() method.
so u can call and override this method.
just need write:
#override
protected void onDestroy(){
// code
super.onDestroy();
}
u can manage and override all of life cycle's parts in android
e.g: onResume to get current activity
i hope this help u

Android shared preferences conditional activity switching

I have an Android app which I use to register users on my web site. My first task is to register a user if my shared preferences file shows there is no registered user information.
If my app has a registered user, I provide the following code to simply and automatically switch to a "homepage" activity:
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.signin);
if( USERPREFERENCES.getString(USERPREFERENCES_USERMAIL, "") == null && USERPREFERENCES.getString(USERPREFERENCES_USERID, "") == null && USERPREFERENCES.getString(USERPREFERENCES_USERNAME, "") == null){
//setContentView(R.layout.signin);
Toast.makeText(SignIn.this, "testing...", Toast.LENGTH_LONG).show();
}else{
Intent intent = new Intent(SignIn.this, Confirmed.class);
startActivity(intent);
}
... other code
So, from my default activity, signin.java, the app will either switch to the Confirmed activity or stay on and display the signin activity.
My problem is, when the system works and I get switched to the the Confirmed activity, I provide a logout onclick listener which is below:
signout.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
// TODO Auto-generated method stub
//USERPREFERENCES.cl
Toast.makeText(Confirmed.this, "signout responding!", Toast.LENGTH_LONG).show();
USERPREFERENCES.edit().clear().commit();
}
});
It responds and clears all my shared preferences variables. But, when I use my menu to manually switch to the sign-in activity, I still get switched back to the Confirmed activity.
This happens even though I can confirm the variables are empty.
This hardly ever will be true:
USERPREFERENCES.getString(USERPREFERENCES_USERMAIL, "") == null
What if you use this instead?
if( USERPREFERENCES.getString(USERPREFERENCES_USERMAIL, null) == null && USERPREFERENCES.getString(USERPREFERENCES_USERID, null) == null && USERPREFERENCES.getString(USERPREFERENCES_USERNAME, null) == null){
//setContentView(R.layout.signin); TRY TO AVOID DOING THIS THING!!!!!
Toast.makeText(SignIn.this, "testing...", Toast.LENGTH_LONG).show();
}else...
Also, as a recommendation... instead of being switching between activities... what if you create just a Signing.java activity and put a ViewFlipper in its layout. That way your app will be not only faster but also easier to maintain.
This is Because When you will switch back to LoginActivity, this will be resumed instead of being created , Means your Login code which you written inOnCreate will not be called because Dthis time Overrider OnResume has been called , not onCreate .
So either write this code again in onResume or call finish() before moving to second activity , so that next time it will call onCreate()
If you navigate back to the first activity, the onCreate is not called again (unless the activity was destroyed for lack of resources). Move the authentication code in onResume.

Problems creating a Popup Window in Android Activity

I'm trying to create a popup window that only appears the first time the application starts. I want it to display some text and have a button to close the popup. However, I'm having troubles getting the PopupWindow to even work. I've tried two different ways of doing it:
First I have an XML file which declares the layout of the popup called popup.xml (a textview inside a linearlayout) and I've added this in the OnCreate() of my main Activity:
PopupWindow pw = new PopupWindow(findViewById(R.id.popup), 100, 100, true);
pw.showAtLocation(findViewById(R.id.main), Gravity.CENTER, 0, 0);
Second I did the exact same with this code:
final LayoutInflater inflater = (LayoutInflater)this.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
PopupWindow pw = new PopupWindow(inflater.inflate(R.layout.popup, (ViewGroup) findViewById(R.layout.main) ), 100, 100, true);
pw.showAtLocation(findViewById(R.id.main_page_layout), Gravity.CENTER, 0, 0);
The first throws a NullPointerException and the second throws a BadTokenException and says "Unable to add window -- token null is not valid"
What in the world am I doing wrong? I'm extremely novice so please bear with me.
To avoid BadTokenException, you need to defer showing the popup until after all the lifecycle methods are called (-> activity window is displayed):
findViewById(R.id.main_page_layout).post(new Runnable() {
public void run() {
pw.showAtLocation(findViewById(R.id.main_page_layout), Gravity.CENTER, 0, 0);
}
});
Solution provided by Kordzik will not work if you launch 2 activities consecutively:
startActivity(ActivityWithPopup.class);
startActivity(ActivityThatShouldBeAboveTheActivivtyWithPopup.class);
If you add popup that way in a case like this, you will get the same crash because ActivityWithPopup won't be attached to Window in this case.
More universal solusion is onAttachedToWindow and onDetachedFromWindow.
And also there is no need for postDelayed(Runnable, 100). Because this 100 millis does not guaranties anything
#Override
public void onAttachedToWindow() {
super.onAttachedToWindow();
Log.d(TAG, "onAttachedToWindow");
showPopup();
}
#Override
public void onDetachedFromWindow() {
super.onDetachedFromWindow();
Log.d(TAG, "onDetachedFromWindow");
popup.dismiss();
}
The accepted answer did not work for me. I still received BadTokenException. So I just called the Runnable from a Handler with delay as such:
new Handler().postDelayed(new Runnable() {
public void run() {
showPopup();
}
}, 100);
use class Context eg. MainActivity.this instead of getApplicationContext()
There are two scenarios when this exception could occur. One is mentioned by kordzik. Other scenario is mentioned here: http://blackriver.to/2012/08/android-annoying-exception-unable-to-add-window-is-your-activity-running/
Make sure you handle both of them
the solution is to set the spinner mode to dialog as below:
android:spinnerMode="dialog"
or
Spinner(Context context, int mode)
tnxs RamallahDroid
See This.
Depending on the use case, for types of pop-up to display a message, setting the pop-up type to TYPE_TOAST using setWindowLayoutType() avoids the issue, as this type of pop-up is not dependent on the underlying activity.
Edit: One of the side effects: no interaction in the popup window for API <= 18, as the touchable / focusable events would be removed by the system. ( http://www.jianshu.com/p/634cd056b90c )
I end up with using TYPE_PHONE (as the app happens to have the permission SYSTEM_ALERT_WINDOW, otherwise this won't work too).
You can check the rootview if it has the token. You can get the parent layout defined from your activity xml, mRootView
if (mRootView != null && mRootView.getWindowToken() != null) {
popupWindow.showAtLocation();
}
Check that findViewById returns something - you might be calling it too early, before the layout is built
Also you may want to post logcat output for the exceptions you're getting
You can also try to use this check:
public void showPopupProgress (){
new Handler().post(new Runnable() {
#Override
public void run() {
if (getWindow().getDecorView().getWindowVisibility() == View.GONE) {
showPopupProgress();
return;
}
popup.showAtLocation(.....);
}
});
}
If you show a PopupWindow in another PopupWindow, do not use the view in first POP, use the origin parent view.
pop.showAtLocation(parentView, ... );
I had the same problem (BadTokenException) with AlertDialog on dialog.show(). I was making an AlertDialog by following some example. In my case the reason of that problem was a string
dialog.getWindow().setType(WindowManager.LayoutParams.TYPE_TOAST)
Everything became working after I removed it.
Maybe it's time for a newer solution. This methods checks 5 times every 50ms if the parent view for the PopupWindow has a token. I use it inside my customized PopupWindow.
private fun tryToShowTooltip(tooltipLayout: View) {
Flowable.fromCallable { parentView.windowToken != null }
.map { hasWindowToken ->
if (hasWindowToken) {
return#map hasWindowToken
}
throw RetryException()
}
.retryWhen { errors: Flowable<Throwable> ->
errors.zipWith(
Flowable.range(1, RETRY_COUNT),
BiFunction<Throwable, Int, Int> { error: Throwable, retryCount: Int ->
if (retryCount >= RETRY_COUNT) {
throw error
} else {
retryCount
}
})
.flatMap { retryCount: Int ->
Flowable.timer(retryCount * MIN_TIME_OUT_MS, TimeUnit.MILLISECONDS)
}
}
.onErrorReturn {
false
}
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe({ hasWindowToken ->
if (hasWindowToken && !isShowing) {
showAtLocation(tooltipLayout, Gravity.NO_GRAVITY, 100, 100)
}
}, { t: Throwable? ->
//error logging
})
}
with
companion object {
private const val RETRY_COUNT = 5
private const val MIN_TIME_OUT_MS = 50L
}
class RetryException : Throwable()
You can specify the y-offset to account for the status bar from the pw.showAtLocation method...

Categories

Resources