In Java, I am trying to implement a feature where only the admin (person who knows the device password) can access an information screen and when they click on a button within the app in the MainActivity, the lock screen will appear as a form of authentication and display another activity screen on success. Is this possible?
So far, I noticed that the authentication only displays when I open the app and not when I press the button in an onClickListener. Most of the solutions I've seen are doing it this way. I have the code in MainActivity within an onCreate() method.
MainActivity
ActivityResultLauncher<Intent> activityResultLaunch = registerForActivityResult(
new ActivityResultContracts.StartActivityForResult(),
new ActivityResultCallback<ActivityResult>() {
#Override
public void onActivityResult(ActivityResult result) {
if (result.getResultCode() == Activity.RESULT_OK) {
// There are no request codes
Intent data = result.getData();
} else {
finish();
}
}
});
start_end_button_add.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
authScreen(activityResultLaunch);
}
});
private void authScreen(ActivityResultLauncher<Intent> activityResultLaunch) {
KeyguardManager mKeyguardManager = (KeyguardManager) getSystemService(Context.KEYGUARD_SERVICE);
if (!mKeyguardManager.isKeyguardSecure()) {
// Show a message that the user hasn't set up a lock screen.
} else {
Intent intent = mKeyguardManager.createConfirmDeviceCredentialIntent(null, null);
if (intent != null) {
startActivityForResult.launch(intent, REQUEST_CODE_CONFIRM_DEVICE_CREDENTIALS);
}
}
Intent intent = new Intent(DetectorActivity.this, SearchActivity.class);
startActivity(intent);
finish();
}
Currently, the app immediately goes to the SearchActivity class without having the lock screen displayed in between. Even if I get into the app after PIN code entered is a success, it still doesn't get into the activityResultLaunch success condition as per the new implementation referenced here by user Martin Zeitler.
Reference:
https://mobile-security.gitbook.io/mobile-security-testing-guide/android-testing-guide/0x05f-testing-local-authentication#:~:text=In%20Android%2C%20there%20are%20two,and%20the%20Biometric%20Authentication%20flow.
Related
I am working on a login app using FirebaseAuth, I am implementing two separate Activities, one to work online and other for offline. There is third starter activity that tries to automatically login a user if his data is present on the SharedPreferences. When finish() is called on an activity signInWithEmailAndPassword won't stop.
when internet is not working and "Work Offline" button is clicked , it will lead the user to ActivityOffline. But when internet is restored while the user is on ActivityOffline , out of nowhere ActivityOnline will pop up because of signInWithEmailAndPassword on the starter activity which is already finished.
How can i stop signInWithEmailAndPassword when my Work Offline button is clicked?
public class Starter extends AppCompatActivity {
Button useOffline;
String email;
String password;
FirebaseAuth mAuth = FirebaseAuth.getInstance();
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_starter);
Prefs prefs = new Prefs(this);
email = prefs.getStringEntry("email");
password = prefs.getStringEntry("password");
useOffline = findViewById(R.id.use_offline);
useOffline.setOnClickListener(v -> {
Intent intent = new Intent(Starter.this, OfflineActivity.class);
startActivity(intent);
finish();
});
final Handler handler = new Handler(Looper.getMainLooper());
handler.postDelayed(() -> useOffline.setVisibility(View.VISIBLE), 5000);
if (email.equals("") || password.equals("")) {
Intent intent = new Intent(Starter.this, LoginActivity.class);
startActivity(intent);
finish();
} else {
mAuth.signInWithEmailAndPassword(email, password).addOnCompleteListener(task -> {
if (task.isSuccessful()) {
prefs.setStringEntry("UID", Objects.requireNonNull(FirebaseAuth.getInstance().getCurrentUser()).getUid());
Intent intent = new Intent(Starter.this, OnlineActivity.class);
startActivity(intent);
} else {
prefs.removeEntry("email");
prefs.removeEntry("password");
Intent intent = new Intent(Starter.this, LoginActivity.class);
startActivity(intent);
}
finish();
});
}
}
}
You can set a boolean flag to true when offline button press and check this flag before start online activity.
I have built a small chat app using firebase. I have also implemented firebaseui for logging in. The problem I am facing is, if the user has not signed up. Whenever the app launches it should take me directly to FirebaseUi signup options but what is happening with current code is for a milli second it shows the layout of main activity and then goes to FirebaseUi.
Also when I am pressing back button before exiting it again shows me the layout of main activity. I want the activity to get destroyed and take me to home (android) but it shows me main activity for a millisecond and then the app exits.
Why is this happening?
public class MainActivity extends AppCompatActivity {
private static final int RC_SIGN_IN = 1;
private FirebaseAuth mAuth;
private FirebaseAuth.AuthStateListener mAuthStateListener;
private TextView mUserNameTextView;
private Button mSignOutButton;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mUserNameTextView = (TextView) findViewById(R.id.user_name_text_view);
mSignOutButton = (Button) findViewById(R.id.sign_out_button);
mAuth = FirebaseAuth.getInstance();
FirebaseUser user = mAuth.getCurrentUser();
mAuthStateListener = new FirebaseAuth.AuthStateListener() {
#Override
public void onAuthStateChanged(#NonNull FirebaseAuth firebaseAuth) {
FirebaseUser user = firebaseAuth.getCurrentUser();
if (user != null) {
// User is signed in
mUserNameTextView.setText(user.getDisplayName());
} else {
// User is signed out
startActivityForResult(
AuthUI.getInstance()
.createSignInIntentBuilder()
.setAvailableProviders(Arrays.asList(
new AuthUI.IdpConfig.EmailBuilder().build(),
new AuthUI.IdpConfig.GoogleBuilder().build()))
.build(),
RC_SIGN_IN);
}
}
};
mSignOutButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
signOut();
}
});
}
private void signOut() {
FirebaseAuth.getInstance().signOut();
mUserNameTextView.setText("");
}
#Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (requestCode == RC_SIGN_IN) {
if (resultCode == RESULT_OK) {
// Sign-in succeeded, set up the UI
Toast.makeText(this, "Signed in!", Toast.LENGTH_SHORT).show();
} else if (resultCode == RESULT_CANCELED) {
// Sign in was canceled by the user, finish the activity
Toast.makeText(this, "Sign in canceled", Toast.LENGTH_SHORT).show();
finish();
}
}
}
#Override
protected void onPause() {
super.onPause();
if (mAuthStateListener != null) {
// when app is paused remove the state listener
mAuth.removeAuthStateListener(mAuthStateListener);
}
}
#Override
protected void onResume() {
super.onResume();
// adding the state listener
mAuth.addAuthStateListener(mAuthStateListener);
}
}
I believe you won't be able to make the Main Activity not display for a millisecond upon app start, since you are first opening the Main Activity and then you redirect it (if I understood correctly).
I can however help you with your problem when you press the back button.
Add this to your FirebaseUi code as a normal method:
#Override
public void onBackPressed() {
Intent intent = new Intent(CurrentActivity.this, NextActivity.class);
//replace "CurrentActivity" and "NextActivity" with your activity names
startActivity(intent);
}
This will open the activity you want when the back button is pressed.
When you start new activity after logged in using FirebaseUI then you need to finish(will remove the activity from backstack) the Login Activity when you start activity.
Intent intent = new Intent(ActivityA.this, NextActivity.class);
startActivity(intent);
finish();
In my app, there are so many activities which are referred to as levels. And one activity is Reward activity. when i win level-1, reward activity opens. Now i want to replay the level-1. For this i have used getExtra(). My app crashes when i click the replay button.
Houselevel1.java
public void getReward(){
if(count == 3) {
Intent intent = new Intent("com.creatives.arfa.revealthesecretsgame.Reward");
intent.putExtra("activity", "level1");
startActivity(intent);
}
}
HouseLevel2.java
public void getReward(){
if(count == 3) {
Intent intent = new Intent("com.creatives.arfa.revealthesecretsgame.Reward");
intent.putExtra("activity", "level2");
startActivity(intent);
}
}
Reward.java
public void replayLevel() {
replay = (ImageButton) findViewById(R.id.replay);
Intent intent= getIntent();
activity = intent.getStringExtra("activity");
replay.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View paramView) {
if(activity.equals("level2")){
Intent intent = new Intent("com.creatives.arfa.revealthesecretsgame.HouseLevel2");
startActivity(intent);
}
if(activity.equals("level1")){
Intent intent = new Intent("com.creatives.arfa.revealthesecretsgame.Houselevel1");
startActivity(intent);
}
}
});
}
With the java code you've posted, in the Reward.java file, you're trying to create another Intent Object with the same name as the one declared in the scope right above it. Because of this, the build will never be successful.
Also, when you declare intents, you MUST pass on the activity_name.class file.
Something you can try:
1) HouseLevel1.java
public void getReward(){
if(count == 3) {
Intent intent = new Intent(getApplicationContext(), com.creatives.arfa.revealthesecretsgame.Reward.class);
intent.putExtra("activity", "level1");
startActivity(intent);
}
}
2) HouseLevel2.java
public void getReward(){
if(count == 3) {
Intent intent = new Intent(getApplicationContext(), com.creatives.arfa.revealthesecretsgame.Reward.class);
intent.putExtra("activity", "level2");
startActivity(intent);
}
}
3) Reward.java
public void replayLevel() {
replay = (ImageButton) findViewById(R.id.replay);
Intent intent= getIntent();
activity = intent.getStringExtra("activity");
replay.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View paramView) {
if(activity.equals("level2")){
Intent intent = new Intent(getApplicationContext(), com.creatives.arfa.revealthesecretsgame.HouseLevel2.class);
startActivity(intent);
}
else if(activity.equals("level1")){
Intent intent = new Intent(getApplicationContext(), com.creatives.arfa.revealthesecretsgame.Houselevel1.class);
startActivity(intent);
}
}
});
}
Also, if you're simply using the Reward.java file to get the previous intent's data, perform some calculation, and send some data back to the calling, or parent activity, then you can simply use the startActivityForResult() method, which takes care what what you're trying to do manually.
Here's a small article that might be able to help you with the problem
http://www.vogella.com/tutorials/AndroidIntent/article.html#retrieving-result-data-from-a-sub-activity
If all you want is go from Activity 1 or to 2 to a Reward activity grab something and send that something back to either activity.
What you do is startActivityForResult You pass an Id (constant number) do what you do on the Reward activty, pack what you need to return in a Bundle, and set ActivtyResult to OK and close your activity.
Your app will go back to the Activity1 or 2 whoever call it. On those activties you override the method onActivityResult There you check if the id on which the result is coming from is the Id you sent on the startActivityForResult and if the status is OK.
Then you have whatever was set on the Reward activity. The Reward activity don't need to know from where it came from if only will grab some data. So you can later have an Activity3 that calls the Reward activity and you do not need to modify the Reward activity.
It is explain here check the accepted answer.
How to manage `startActivityForResult` on Android?
My main/startup activity is my SignInActivity and I've included the
Firebase auth listener
mFirebase.addAuthStateListener(new Firebase.AuthStateListener() {
#Override
public void onAuthStateChanged(AuthData authData) {
if (authData != null) {
// go to auth activity
// such as user logging in
} else {
if(!(this instanceof SignInActivity)){
// if i sign out in an auth activity
// i want to trigger this to go back to the SignIn Activity
Intent mIntent = new Intent(getApplicationContext(), SignInActivity.class);
mIntent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
startActivity(mIntent);
finish();
return;
}
}
}
});
In the else statement, the if condition does not work. I know I can't use this inside a nested object. I've tried a few other attempts but I always get flickering and I think it ends up in a infinite loop because the condition is always met. Whats the right way to go about this?
Try this:
mFirebase.addAuthStateListener(new Firebase.AuthStateListener() {
#Override
public void onAuthStateChanged(AuthData authData) {
if (authData != null) {
// go to auth activity
// such as user logging in
} else {
if(!(SignInActivity.this instanceof SignInActivity)){
// if i sign out in an auth activity
// i want to trigger this to go back to the SignIn Activity
Intent mIntent = new Intent(getApplicationContext(), SignInActivity.class);
mIntent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
startActivity(mIntent);
finish();
return;
}
}
}
});
Is it safe to use MainActivity.this
In my Android app, I have a button that when clicked, launches the external application of my choice to play a video (I gather that this is called an "implicit intent"). Here is the relevant Java code from my onCreate method.
Button button = (Button) findViewById(R.id.button);
button.setOnClickListener
(
new Button.OnClickListener()
{
public void onClick(View v)
{
Intent i = new Intent(Intent.ACTION_VIEW);
i.setDataAndType(Uri.parse("https://youtu.be/jxoG_Y6dvU8"), "video/*");
startActivity(i);
}
}
);
I expected this to work, since I've followed tutorials and the Android developers documentation pretty closely, but when I test my app in the AVD, instead of prompting a menu of external applications where I can view my video, the app crashes.
What is causing my app to crash?
Change your onClick method to below code. You should give the option to choose the external player.
#Override
public void onClick(View v) {
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setDataAndType(Uri.parse("https://youtu.be/jxoG_Y6dvU8"), "video/*");
startActivity(Intent.createChooser(intent, "Complete action using"));
}
Change your code to add this check:
Intent i = new Intent(Intent.ACTION_VIEW);
i.setDataAndType(Uri.parse("https://youtu.be/jxoG_Y6dvU8"), "video/*");
// Check there is an activity that can handle this intent
if (i.resolveActivity(getPackageManager()) == null) {
// TODO No activity available. Do something else.
} else {
startActivity(i);
}