Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Closing the app when user cancels immediate in app update

Tags:

java

android

Currently i'm implementing the in app update feature from android. I'm using the immediate update method, the problem that I'm facing is that when the UI prompting the user to update shows and the user does not click the update button instead they click the cross button. The UI for the app update just closes and user can continue using the app.

What I want is that when user click the cross button the app immediately closes, until user updates the app then they can use the app as usual. I also uses the java code for the android development.

public class LoginActivity extends AppCompatActivity {

private LoginViewModel loginViewModel;
public static final String MyPREFERENCES = "LoginPrefs" ;
public static final String Name = "nameKey";
public static final String User = "userKey";
public static final String con = "closed";
public static String error = "";
public static int userFlag = 0;
SharedPreferences sharedpreferences;
SharedPreferences.Editor editor;

public TextInputEditText usernameEditText;
public TextInputEditText passwordEditText;

private AppUpdateManager mAppUpdateManager;
private int RC_APP_UPDATE = 999;
private int inAppUpdateType;
private com.google.android.play.core.tasks.Task<AppUpdateInfo> appUpdateInfoTask;
private InstallStateUpdatedListener installStateUpdatedListener;

@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_login);
    loginViewModel = ViewModelProviders.of(this, new LoginViewModelFactory())
            .get(LoginViewModel.class);

    usernameEditText = findViewById(R.id.user);
    passwordEditText = findViewById(R.id.pass);
    final Button loginButton = findViewById(R.id.submitBTN);
    final TextInputLayout userL = findViewById(R.id.userL);
    final TextInputLayout passL = findViewById(R.id.passL);
    final JellyToggleButton jtb = findViewById(R.id.jtb);


    // Creates instance of the manager.
    mAppUpdateManager = AppUpdateManagerFactory.create(this);
    // Returns an intent object that you use to check for an update.
    appUpdateInfoTask = mAppUpdateManager.getAppUpdateInfo();
    //lambda operation used for below listener
    //For flexible update
    installStateUpdatedListener = installState -> {
        if (installState.installStatus() == InstallStatus.DOWNLOADED) {
            popupSnackbarForCompleteUpdate();
        }
    };
    mAppUpdateManager.registerListener(installStateUpdatedListener);

    inAppUpdateType = AppUpdateType.IMMEDIATE; //1
    inAppUpdate();

    if(userFlag==1){
        jtb.setChecked(true);
    }

    userL.setHint("Enter username");
    sharedpreferences = getSharedPreferences(MyPREFERENCES, MODE_PRIVATE);
    loginViewModel.getLoginFormState().observe(this, new Observer<LoginFormState>() {
        @Override
        public void onChanged(@Nullable LoginFormState loginFormState) {
            if (loginFormState == null) {
                return;
            }
            loginButton.setEnabled(loginFormState.isDataValid());
            if (loginFormState.getUsernameError() != null) {
                usernameEditText.setError(getString(loginFormState.getUsernameError()));

                loginButton.startAnimation(AnimationUtils.loadAnimation(LoginActivity.this,R.anim.shake));
            }
            if (loginFormState.getPasswordError() != null) {
                passwordEditText.setError(getString(loginFormState.getPasswordError()));
                loginButton.startAnimation(AnimationUtils.loadAnimation(LoginActivity.this,R.anim.shake));
            }
        }
    });

    loginViewModel.getLoginResult().observe(this, new Observer<LoginResult>() {
        @Override
        public void onChanged(@Nullable LoginResult loginResult) {
            if (loginResult == null) {
                return;
            }

            if (loginResult.getError() != null) {
                showLoginFailed(loginResult.getError());
            }
            if (loginResult.getSuccess() != null) {
                updateUiWithUser(loginResult.getSuccess());
                Intent i = new Intent(LoginActivity.this, user_dashboard.class);
                startActivity(i);
            }
            setResult(Activity.RESULT_OK);

            //Complete and destroy login activity once successful

        }
    });

    TextWatcher afterTextChangedListener = new TextWatcher() {
        @Override
        public void beforeTextChanged(CharSequence s, int start, int count, int after) {
            // ignore
        }

        @Override
        public void onTextChanged(CharSequence s, int start, int before, int count) {
            // ignore
        }

        @Override
        public void afterTextChanged(Editable s) {
            loginViewModel.loginDataChanged(usernameEditText.getText().toString(),
                    passwordEditText.getText().toString());
        }
    };
    usernameEditText.addTextChangedListener(afterTextChangedListener);
    passwordEditText.addTextChangedListener(afterTextChangedListener);
    passwordEditText.setOnEditorActionListener(new TextView.OnEditorActionListener() {

        @Override
        public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
            if (actionId == EditorInfo.IME_ACTION_DONE) {
                loginViewModel.login(usernameEditText.getText().toString(),
                        passwordEditText.getText().toString());
            }
            return false;
        }
    });

    loginButton.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {

            if(userFlag==0) {
                loginViewModel.login(usernameEditText.getText().toString(),
                        passwordEditText.getText().toString());
                getStaffData();
            }
            else if(userFlag==1){
                loginWorker();
            }

        }
    });

    jtb.setOnStateChangeListener(new JellyToggleButton.OnStateChangeListener() {
        @Override
        public void onStateChange(float process, State state, JellyToggleButton jtb) {
            if (state.equals(State.LEFT)) {

                userL.setHint("Enter username");
                error = "Username cannot be empty";
                userFlag = 0;
            }
            if (state.equals(State.RIGHT)) {

                userL.setHint("Enter badge ID");
                error = "Badge ID cannot be empty";
                userFlag = 1;
            }
        }
    });
}

@Override
protected void onDestroy() {
    mAppUpdateManager.unregisterListener(installStateUpdatedListener);
    finishAndRemoveTask();
    super.onDestroy();
}

@Override
protected void onResume() {
    try {
        mAppUpdateManager.getAppUpdateInfo().addOnSuccessListener(appUpdateInfo -> {
            if (appUpdateInfo.updateAvailability() ==
                    UpdateAvailability.DEVELOPER_TRIGGERED_UPDATE_IN_PROGRESS) {
                // If an in-app update is already running, resume the update.
                try {
                    mAppUpdateManager.startUpdateFlowForResult(
                            appUpdateInfo,
                            inAppUpdateType,
                            this,
                            RC_APP_UPDATE);
                } catch (IntentSender.SendIntentException e) {
                    e.printStackTrace();
                }
            }
        });


        mAppUpdateManager.getAppUpdateInfo().addOnSuccessListener(appUpdateInfo -> {
            //For flexible update
            if (appUpdateInfo.installStatus() == InstallStatus.DOWNLOADED) {
                popupSnackbarForCompleteUpdate();
            }
        });
    } catch (Exception e) {
        e.printStackTrace();
    }

    super.onResume();
}

@Override //For flexible update
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
    super.onActivityResult(requestCode, resultCode, data);
    if (requestCode == RC_APP_UPDATE) {
        //when user clicks update button
        if (resultCode == RESULT_OK) {
            Toast.makeText(LoginActivity.this, "App download starts...", Toast.LENGTH_LONG).show();
        } else if (resultCode == RESULT_CANCELED) {
            //if you want to request the update again just call checkUpdate()
            Toast.makeText(LoginActivity.this, "App download canceled.", Toast.LENGTH_LONG).show();
        } else if (resultCode == RESULT_IN_APP_UPDATE_FAILED) {
            Toast.makeText(LoginActivity.this, "App download failed.", Toast.LENGTH_LONG).show();
        }
    }
}

private void updateUiWithUser(LoggedInUserView model) {

    String welcome = getString(R.string.welcome);
    // TODO : initiate successful logged in experience
    Toast.makeText(getApplicationContext(), welcome, Toast.LENGTH_LONG).show();
}

private void showLoginFailed(@StringRes Integer errorString) {
    Toast.makeText(getApplicationContext(), errorString, Toast.LENGTH_SHORT).show();
}

private void getStaffData() {
    String username = usernameEditText.getText().toString();
    APIInterface apiInterface3 = APIClient.getClient().create(APIInterface.class);
    Call<loginList> call3 = apiInterface3.staffData(username);
    call3.enqueue(new Callback<loginList>() {
        @Override
        public void onResponse(Call<loginList> call, Response<loginList> response) {
            loginList list = response.body();
            if (list!=null && list.getStatusCode()==1) { //response received.
                if(list.getStaffList().size()>0){
                    Log.d("check-in", list.getStatusCode() + " " + list.getStaffList().get(0).getName());
                    Toast.makeText(LoginActivity.this,"Logged in",Toast.LENGTH_SHORT).show();

                    final String name = list.getStaffList().get(0).getName();
                    final String badge = list.getStaffList().get(0).getBadge();
                    SharedPreferences.Editor editor = sharedpreferences.edit();
                    editor.putString(Name,name);
                    editor.putString(User,badge);
                    editor.putInt(con,1);
                    editor.apply();
                }
                else if(list.getStaffList().size()==0){

                }
            }
        }

        @Override
        public void onFailure(Call<loginList> call, Throwable t) {
            Log.d("fail",t.toString());
        }
    });
}

private void loginWorker(){
    String username = usernameEditText.getText().toString();
    String password = passwordEditText.getText().toString();

    APIInterface apiInterface3 = APIClient.getClient().create(APIInterface.class);
    Call<loginList> call3 = apiInterface3.loginWorker(username,password);
    call3.enqueue(new Callback<loginList>() {
        @Override
        public void onResponse(Call<loginList> call, Response<loginList> response) {
            loginList list = response.body();
            Log.d("response", response.body().toString());
            if (list!=null && list.getStatusCode()==1) { //response received.
                if(list.getLoginList().size()>0){
                    Log.d("check-in", list.getStatusCode() + " " + list.getLoginList().get(0).getName());
                    Toast.makeText(LoginActivity.this,"Logged in",Toast.LENGTH_SHORT).show();
                    List<login> item = response.body().getLoginList();
                    final String name = list.getLoginList().get(0).getName();
                    SharedPreferences.Editor editor = sharedpreferences.edit();
                    editor.putString(Name,name);
                    editor.putInt(con,1);
                    editor.apply();

                }
                String welcome = getString(R.string.welcome);
                Toast.makeText(getApplicationContext(), welcome, Toast.LENGTH_SHORT).show();
                Intent i = new Intent(LoginActivity.this, user_dashboard.class);
                startActivity(i);
            }
            else
            Toast.makeText(LoginActivity.this, "wrong ID or password",Toast.LENGTH_SHORT).show();
        }

        @Override
        public void onFailure(Call<loginList> call, Throwable t) {
            Log.d("fail",t.toString());
        }
    });
    editor = sharedpreferences.edit();
    editor.putString(User, username);
    editor.commit();
}

@Override
public void onBackPressed() {
    new MaterialAlertDialogBuilder(LoginActivity.this,R.style.MyDialogTheme)
            .setTitle("Exit")
            .setMessage("Confirm to exit?")
            .setBackground(getDrawable(R.drawable.alert_dialog))
            // Specifying a listener allows you to take an action before dismissing the dialog.
            // The dialog is automatically dismissed when a dialog button is clicked.
            .setPositiveButton("Ok", new DialogInterface.OnClickListener() {
                public void onClick(DialogInterface dialog, int which) {
                    // Continue with delete
                    finishAffinity();

                }
            })


            .setNegativeButton(android.R.string.no, null)

            .show();


}

private void inAppUpdate() {
    try {
        // Checks that the platform will allow the specified type of update.
        appUpdateInfoTask.addOnSuccessListener(new OnSuccessListener<AppUpdateInfo>() {
            @Override
            public void onSuccess(AppUpdateInfo appUpdateInfo) {
                if (appUpdateInfo.updateAvailability() == UpdateAvailability.UPDATE_AVAILABLE
                        // For a flexible update, use AppUpdateType.FLEXIBLE
                        && appUpdateInfo.isUpdateTypeAllowed(inAppUpdateType)) {
                    // Request the update.
                    try {
                        mAppUpdateManager.startUpdateFlowForResult(
                                // Pass the intent that is returned by 'getAppUpdateInfo()'.
                                appUpdateInfo,
                                // Or 'AppUpdateType.FLEXIBLE' for flexible updates.
                                inAppUpdateType,
                                // The current activity making the update request.
                                LoginActivity.this,
                                // Include a request code to later monitor this update request.
                                RC_APP_UPDATE);

                    } catch (IntentSender.SendIntentException ignored) {

                    }
                }
            }
        });
    } catch (Exception e) {
        e.printStackTrace();
    }

}

private void popupSnackbarForCompleteUpdate() {
    try {
        Snackbar snackbar =
                Snackbar.make(
                        findViewById(R.id.coordinatorL),
                        "An update has just been downloaded.\nRestart to update",
                        Snackbar.LENGTH_INDEFINITE);

        snackbar.setAction("INSTALL", view -> {
            if (mAppUpdateManager != null){
                mAppUpdateManager.completeUpdate();
            }
        });
        snackbar.setActionTextColor(getResources().getColor(R.color.orange));
        snackbar.show();

    } catch (Resources.NotFoundException e) {
        e.printStackTrace();
    }
}
}

image

The image I borrowed from google, on the left image as can be seen there is a cross button on top right user can click to close the update process

like image 980
Gustode Avatar asked Jan 31 '26 03:01

Gustode


1 Answers

The most important point I will emphasize is that you should not force users to update the app until it is absolutely necessary (like some security issues etc). Forcing updates to users is considered a very bad user experience.

To the question you asked, you have the answer in your question itself. If you check the code you have something like this in your onActivityResult method-

if (requestCode == RC_APP_UPDATE) {
        //when user clicks update button
        if (resultCode == RESULT_OK) {
            Toast.makeText(LoginActivity.this, "App download starts...", Toast.LENGTH_LONG).show();
        } else if (resultCode == RESULT_CANCELED) {
            //if you want to request the update again just call checkUpdate()
            Toast.makeText(LoginActivity.this, "App download canceled.", Toast.LENGTH_LONG).show();
        } else if (resultCode == RESULT_IN_APP_UPDATE_FAILED) {
            Toast.makeText(LoginActivity.this, "App download failed.", Toast.LENGTH_LONG).show();
        }
    }

In case when the user cancels resultCode == RESULT_CANCELED or the update fails resultCode == RESULT_IN_APP_UPDATE_FAILED, you can take whatever action you want. You can finish the activity or whatever is suitable in your situation.

like image 171
Akshay Jain Avatar answered Feb 02 '26 16:02

Akshay Jain