I am working on an app where I need to set security feature by setting passcode/password/pin for the user so that whenever they open the app from the background, the app asks for password/passcode/pin. I read a few articles/solutions like this but they didn't help me. The same functionality we found in normal banking apps in which whenever you open an app, they will ask for passcode/fingerprint.
I have already set up the logic to save the passcode/pin in shared preference but I am not sure about when to ask it. I know that we can't replace splash screen with passcode/pin activity, because sometimes from app's homeScreen/MainActivity, user press home button and when they again open the app from recent apps, the app should ask for passcode/pin to resume the app use.
Any help would be appreciated.
This is an interesting question, I will share my thought on this question and give a solution as well.
Terminology:
App Lock Type: A generic name for pin/pincode/password/passcode, etc. (in the following section, I will use the pin name to demonstrate)
PinActivity: A screen where users input their pin to verify themself
Story:
For apps that require users to input pin, they usually want to make sure sensitive information is not leaked/stolen by other people. So we will categorize app activities into 2 groups.
Normal activities: Doesn't contain any sensitive information, usually before users logged in to the app, such as SplashActivity, LoginActivity, RegistrationActivity, PinActivity, etc.
Secured activities: Contain sensitive information, usually after users logged in, such as MainActivity, HomeActivity, UserInfoActivity, etc.
Conditions:
For secured activities, we must make sure users always input their pin before viewing the content by showing the PinActivity. This activity will be shown in the following scenarios:
[1] When users open a secured activity form a normal activity, such as from SplashActivity to MainActivity
[2] When users open a secured activity by tapping on Notifications, such as they tap on a notification to open MainActivity
[3] When users tap on the app from the Recents screen
[4] When the app starts a secured activity from another place like Services, Broadcast Receiver, etc.
Implementation:
For case [1] [2] and [4], before start a secured activity we will add an extra to the original intent. I will create a file named IntentUtils.kt
IntentUtils.kt
const val EXTRA_IS_PIN_REQUIRED = "EXTRA_IS_PIN_REQUIRED"
fun Intent.secured(): Intent {
return this.apply {
putExtra(EXTRA_IS_PIN_REQUIRED, true)
}
}
Use this class from normal activities, notifications, services, etc.
startActivity(Intent(this, MainActivity::class.java).secured())
For case [3], I will use 2 APIs:
ProcessLifecycleOwner: To detect whether the app go to background. A typical scenario is when users click on Home/Menu key on their devices.
ActivityLifecycleCallbacks: To detect whether an activity is resumed by relying on onActivityResumed(activity) method.
First I create a base activity, all normal activitis must extend from this class
BaseActivity.kt
open class BaseActivity : AppCompatActivity() {
// This method indicates that a pin is required if
// users want to see the content inside.
open fun isPinRequired() = false
}
Second I create a secured activity, all secured activities must extend from this class
SecuredActivity.kt
open class SecuredActivity : BaseActivity() {
override fun isPinRequired() = true
// This is useful when launch a secured activity with
// singleTop, singleTask, singleInstance launch mode
override fun onNewIntent(intent: Intent?) {
super.onNewIntent(intent)
setIntent(intent)
}
}
Third I create a class that extends from the Application, all logic are inside this class
MyApplication.kt
class MyApplication : Application() {
private var wasEnterBackground = false
override fun onCreate() {
super.onCreate()
registerActivityLifecycleCallbacks(ActivityLifecycleCallbacksImpl())
ProcessLifecycleOwner.get().lifecycle.addObserver(LifecycleObserverImpl())
}
private fun showPinActivity() {
startActivity(Intent(this, PinActivity::class.java))
}
inner class LifecycleObserverImpl : LifecycleObserver {
#OnLifecycleEvent(Lifecycle.Event.ON_STOP)
fun onEnterBackground() {
wasEnterBackground = true
}
}
inner class ActivityLifecycleCallbacksImpl : ActivityLifecycleCallbacks {
override fun onActivityResumed(activity: Activity) {
val baseActivity = activity as BaseActivity
if (!wasEnterBackground) {
// Handle case [1] [2] and [4]
val removed = removeIsPinRequiredKeyFromActivity(activity)
if (removed) {
showPinActivity()
}
} else {
// Handle case [3]
wasEnterBackground = false
if (baseActivity.isPinRequired()) {
removeIsPinRequiredKeyFromActivity(activity)
showPinActivity()
}
}
}
private fun removeIsPinRequiredKeyFromActivity(activity: Activity): Boolean {
val key = EXTRA_IS_PIN_REQUIRED
if (activity.intent.hasExtra(key)) {
activity.intent.removeExtra(key)
return true
}
return false
}
override fun onActivityCreated(activity: Activity, savedInstanceState: Bundle?) {}
override fun onActivityStarted(activity: Activity) {}
override fun onActivityPaused(activity: Activity) {}
override fun onActivitySaveInstanceState(activity: Activity, outState: Bundle) {}
override fun onActivityStopped(activity: Activity) {}
override fun onActivityDestroyed(activity: Activity) {}
}
}
Conclusion:
This solution works for those cases that I mentioned before, but I haven't tested the following scenarios:
When start a secured activity has launch mode singleTop|singleTask|singleInstance
When application killed by the system on low memory
Other scenarios that someone might encounter (if yes please let me know in the comments section).
Related
hi guys i'm trying to do auto login in my app but before login done i wonder if the user verified his email or no.
the problem : even if i verified my account the code doesn't see this and said false.
and here is my code.
class SignInActivity : BaseActivity<SignInViewModel, ActivitySignInBinding>(), Navigator {
private lateinit var preferenceManger: PreferenceManger
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
preferenceManger = PreferenceManger(applicationContext)
autoLogin()
binding.vm = viewModel
viewModel.navigator = this
addPrefManger()
}
private fun autoLogin() {
DataUtils.firebaseUser = Firebase.auth.currentUser
if (preferenceManger.getBoolean(Constants.KEY_IS_SIGNED_IN)) {
when {
DataUtils.firebaseUser!!.isEmailVerified -> {
startActivity(Intent(this, HomeActivity::class.java))
finish()
}
else -> {
startActivity(Intent(this, VerificationActivity::class.java))
finish()
}
}
}
}
this line is always false even if i verified my account.
DataUtils.firebaseUser!!.isEmailVerified
While the verification status of the user profile is updated on the server as soon as they've clicked the link, it may take up to an hour before that information is synchronized to the Android app.
If you want to detect the email verification in the app before it is automatically synchronized, you can:
Sign the user out and in again.
Force reloading of the user profile (after the user has clicked the link) by calling reload on the user object. You can put a button in your UI to do this, or automatically call that, for example in the onResume of the activity.
Also see:
How to verify email without the need of signing in again while using FirebaseUI-auth?
Verification email activity not refreshing
Let me describe the application im trying to do using Kotlin, on Android Studio.
Splash Screen => Login Screen => Main App
Splash Screen: Just a photo
Login Screen: Provides different ways of logging (Google, Facebook, etc)
MainActivity: Allows you to log off, in that case, you must return to "Login Screen"
So far I have been working only with Facebook Login.
I managed to place the button, make it work, and get a proper uid. The trouble is that, when that button is clicked, it automatically switches to "Log Out". So, when I go back from my MainActivity to my Login Screen, instead of having the button to Login again, im having a "Log Out" button, when account is actually already logged out.
Is there a way to prevent this button from changing? I have been reading tons of documentation, but havent found anything useful.
Is my idea incorrect? Or is there a better way to do this?
Note that everytime I Leave the LoginScreen, I place a finish(). The reason of this was to try to reset the activity, and make it work as if the program was running from scratch.
Variables defined on LoginScreen
private var mCallBackManager : CallbackManager?= null
private var mFirebaseAuth : FirebaseAuth?= null
On create function
override fun onCreate(savedInstanceState: Bundle?)
{
super.onCreate(savedInstanceState)
setContentView(R.layout.lay_login_screen)
//-----------Inicializadores-Facebook---------------
mFirebaseAuth = FirebaseAuth.getInstance()
FacebookSdk.sdkInitialize(getApplicationContext())
mCallBackManager = CallbackManager.Factory.create()
//--------------------------------------------------
//-----------Boton-Facebook-------------------------
Button_LoginScreen_LoginFace.setOnClickListener()
{
iniciarSesionFacebook();
}
//--------------------------------------------------
}
private fun iniciarSesionFacebook()
{
Button_LoginScreen_LoginFace.registerCallback(mCallBackManager, object : FacebookCallback<LoginResult>
{
override fun onSuccess(result: LoginResult?)
{
d(getString(R.string.TAG_FacebookLogin),"Login Correct")
handleFacebookToken(result!!.accessToken)
}
override fun onCancel()
{
d(getString(R.string.TAG_FacebookLogin),"Login cancelled")
}
override fun onError(error: FacebookException?)
{
d(getString(R.string.TAG_FacebookLogin),"Login Error")
}
})
}
private fun handleFacebookToken(accessToken: AccessToken?)
{
val credential = FacebookAuthProvider.getCredential(accessToken!!.token)
mFirebaseAuth!!.signInWithCredential(credential).addOnFailureListener()
{error->
d(getString(R.string.TAG_FacebookLogin),"Error 1"+error.message)
}
.addOnSuccessListener { resultado->
startActivity(Intent(this, MainActivity::class.java))
finish()
}
}
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?)
{
super.onActivityResult(requestCode, resultCode, data)
mCallBackManager!!.onActivityResult(requestCode,resultCode,data)
}
Finally, this is how I "Log Out" from MainActivity, and return to LoginScreen
class MainActivity : AppCompatActivity()
{
//----------Variables-Globales----------------
private var mFirebaseAuth : FirebaseAuth?= null
//--------------------------------------------
override fun onCreate(savedInstanceState: Bundle?)
{
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
//-------Inicializo-Variables------------------------
mFirebaseAuth = FirebaseAuth.getInstance()
//---------------------------------------------------
Button_MainActity_LogOf.setOnClickListener{
val firebaseaux=mFirebaseAuth
firebaseaux?.signOut()
startActivity(Intent(this, ActivityLoginScreen::class.java))
}
}
}
This is how the LoginScreen looks the first time is ran.
This is how it looks after manually logging of. Note that is not only the appearence of the code, the code itself changes. Now the button doesnt allow you to LogIn.
Just to add some extra information, i have found this property on the docs
Configuration: auto_logout_link
HTML5 Attribute: data-auto-logout-link
Description: If its activated, the button will be replaced by a LogOut button if the user has already logged in
Options: True,False
This es EXACTLY what im looking for. But, from what I can see, that is only ment for webpages and not android. Does somebody know how to touch this configuration in android? or the equivalent adroid option?
I will add the link where I found the property.
https://developers.facebook.com/docs/facebook-login/web/login-button/
Thanks in advance
Ok, after some time I managed to solve the Issue.
I was logging out from the Firebase Authentication, but not from Facebook authentication.
I didnt manage to stop the button from behaving like it does, but at least the behaviour now is correct.
I will share my new LogOut code.
Button_MainActity_LogOf.setOnClickListener{
FirebaseAuth.getInstance().signOut() //Log out from Firebase
if(isFacebookLogin()) //Check if logged in on facebook
{
LoginManager.getInstance().logOut() //Log out from facebook
}
//Here i should log out from other providers
startActivity(Intent(this, ActivityLoginScreen::class.java))
}
private fun isFacebookLogin(): Boolean
{
return AccessToken.getCurrentAccessToken() !=null
}
Currently, I am writing a chat application for Android. Starting from 23 SDK and above, it needs some permissions which user has to allow, such as extreme important (my chat will use a location of creation of a particular chat) and just small features such as uploading images to Firebase storage (it needs the access to phone storage, therefore it needs appropriate permission).
I have the following interface for callbacks.
object PermissionUtils {
interface PermissionAskListener {
fun onPermissionGranted()
/*
User has already granted this permission
The app must had been launched earlier and the user must had "allowed" that permission
*/
fun onPermissionRequest()
/*
The app is launched FIRST TIME..
We don't need to show additional dialog, we just request for the permission..
*/
fun onPermissionPreviouslyDenied()
/*
The app was launched earlier and the user simply "denied" the permission..
The user had NOT clicked "DO NOT SHOW AGAIN"
We need to show additional dialog in this case explaining how "allowing this permission" would be useful to the user
*/
fun onPermissionDisabled()
/*
The app had launched earlier and the user "denied" the permission..
AND ALSO had clicked "DO NOT ASK AGAIN"
We need to show Toask/alertdialog/.. to indicate that the user had denied the permission by checking do not disturb too...
So, you might want to take the user to setting>app>permission page where the user can allow the permission..
*/
}
fun checkForPermission(activity: Activity, permission: String, permissionAskListener: PermissionAskListener) {
//code omitted, here's the logic of calls listener members
}
}
And, I use it in code like this:
//calling from onCreate()
checkForPermission(
this, android.Manifest.permission.READ_EXTERNAL_STORAGE,
object : PermissionAskListener {
override fun onPermissionGranted() {
showToast(getString(R.string.msg_permissions_granted), Toast.LENGTH_LONG)
uplodAnImageToFirebase()
}
override fun onPermissionRequest() {
ActivityCompat.requestPermissions(
this#MainActivity, arrayOf(android.Manifest.permission.READ_EXTERNAL_STORAGE), readStorage
)
}
override fun onPermissionPreviouslyDenied() {
AlertDialog.Builder(this#MainActivity)
.setTitle(getString(R.string.title_permission_required))
.setMessage(getString(R.string.msg_permission_required))
.setCancelable(false)
.setPositiveButton(getString(R.string.action_allow)) { _, _ ->
ActivityCompat.requestPermissions(
this#MainActivity,
arrayOf(Manifest.permission.READ_EXTERNAL_STORAGE),
readStorage
)
}
.setNegativeButton(getString(R.string.action_cancel)) { dialog, _ ->
dialog.cancel()
showToast(getString(R.string.msg_we_cant_give_functionality), Toast.LENGTH_LONG)
}
.show()
}
override fun onPermissionDisabled() {
AlertDialog.Builder(this#MainActivity)
.setTitle(getString(R.string.title_permission_disabled))
.setMessage(getString(R.string.msg_please_enable_permission))
.setCancelable(false)
.setPositiveButton(
getString(R.string.action_go_to_settings)
) { _, _ -> startActivity(Intent(Settings.ACTION_SETTINGS)) }
.setNegativeButton(getString(R.string.action_cancel)) { dialog, _ ->
dialog.cancel()
showToast(getString(R.string.msg_we_cant_give_functionality), Toast.LENGTH_LONG)
}
.show()
}
}
)
As you may see from code, only onPermissionGranted() do something particular, and either onPermissionPreviouslyDenied() and onPermissionDisabled() just saying to user boring and common things, which I want to incapsulate to some class that will create either dialogs for extra important things (like location; if permission denied I'd like to close entire app), and either just upload, which will just block functionality.
I know how to do such requests for permission and other stuff like it. I don't know how to create these classes with the dialogs - create enum that I pass whenever I call onDisabled/onPreviouslyDenied method from activity, or create Builder for it, or create Factory... if you TL;DR case, then just answer: 'How to reduce the same code in my case?'
An alternative solution would be to create a BaseActivity class, and have your other activities in the app sub-class the BaseActivity.
Something like..
class BaseActivity: AppCompatActivity {
override fun onCreate() {
super.onCreate()
checkForPermissions() // do your permission check code
}
}
class MainActivity: BaseActivity {
override fun onCreate() {
super.onCreate() // calls BaseAcivitiy's onCreate, which triggers the checkForPermissions
}
}
As CommonsWare suggested in the comments, there is (here is libraries to try to reduce some of this boilerplate) a lot of good libraries. I've chosen a NoPermission library. Maybe, it seems like an advertising, but I'm sure that this is not the worst library. Have a nice day!!!
I suggest use the following library:
https://github.com/Karumi/Dexter
On the other hand, to avoid duplicate code, you can move to separate class the code related to dialog helper.
For instance: https://github.com/jpgpuyo/MVPvsMVVM/blob/one_dialog/app/src/main/java/upday/mvpvsmvvm/dialoghelper/DialogHelper.java
In my android application from my background service I want to launch activity with transparent background. Because of the fact that the application is transparent I always want it to be displayed on some other activity from my application. So before the launch I would need to somehow check if any activity is currently opened and if it isn't then open home activity and after that my transparent activity. But if application was already opened then I want to open new activity on top of most recent one.
How could I achieve that? Only thing I found is how to check if my application is in foreground or not. But when my app is in background I would still like to open my transparent activity atop most recent activity.
class FirebaseDbApplication : Application() {
override fun onCreate() {
super.onCreate()
}
companion object{
private var sIsChatActivityOpen = false
fun isChatActivityOpen() : Boolean {
return sIsChatActivityOpen
}
fun setChatActivityOpen(isChatActivityOpen: Boolean) {
this.sIsChatActivityOpen = isChatActivityOpen
}
}
}
// In your activity
override fun onResume() {
super.onResume()
FirebaseDbApplication.setChatActivityOpen(true)
}
override fun onPause() {
super.onPause()
FirebaseDbApplication.setChatActivityOpen(false)
}
// For checking is activity is opened or not
if (FirebaseDbApplication.isChatActivityOpen()) {
// Activity is opened
}
else{
// Activity is closed
}
In the below shown diagram, I am having 3 modules(as android library) which extends the base "common components module" and all this 3 modules will be added to a single android application. All 3 modules are independent modules but when it comes as an application, it would require to share some data, launch other module and requires more inter-communication.
So can anyone let me know how we can implement the "Data Sharing Layer" and "Navigation Controller" in this kind of architecture?
Example: Module1 -> Login, Module2 -> Profile Management etc and there could be "n" number of modules based on the application need.
What you are looking for is basically a clean approach on how to communicate with other classes. There is not really a difference in whether or not they are in different modules.
The following sample describes how a LoginActivity could navigate to some profile activity. This is just a basic sample to be improved with what you actually need and intend to do!
Define your interfaces
Write interfaces of what you need. Your Login should be able to open a profile page? Well this sounds like it needs a LoginNavigator!
interface LoginNavigator {
void showProfile();
}
Include those interfaces in your shared components. There is not really a possibility to go without defining interfaces. You can make them more abstract or more fine grained, this is entirely up to you.
Declare your dependencies
Remember how your Login needs a LoginNavigator? The real problem is on how to supply it to your class. You should have a look at dependency injection, since there are frameworks liks dagger-2 that (could) make this easier. For now, we define an interface for a common component, so that we can retrieve the dependencies we need.
interface NavigatorProvider {
LoginNavigator provideNavigator();
}
You may guess it—this method is used to get the actual LoginNavigator that you can use to get the implementation of that interface. Usually you would just declare this dependency in the constructor, but since android is somewhat special you need to get it from somewhere yourself.
Provide your dependencies
The easiest way to go is to just have your application implement this interface (or hold an object that does).
class MyApp extends Application implements NavigatorProvider {
LoginNavigator provideNavigator() {
return new LoginNavigator() {
void showProfile() {
// just some sample code. You should probably not use an
// anonymous class
startActivity(new Intent(this, MyProfileActivity.class));
}
};
}
}
Again, you could also return an object that is implementing this interface. This is just a basic sample.
Use the interface. (And don't care about the implementation)
Now the dependency injection is nearly complete. We have an interface that we need, we have some way to provide the dependency, all that's left is to get it and use it.
class LoginActivity extends Activity {
LoginNavigator mNavigator;
void onCreate() {
// get the dependency
mNavigator = ((NavigatorProvider) getApplicationContext()).provideNavigator();
// use it where needed. (again, just sample code)
findShowProfileView().setOnClickListener(new OnClickListener() {
void onClick(View view) {
mNavigator.showProfile();
}
});
}
}
Now the dependency is provided, and ready to be used.
What this sample shows is how to basically use interfaces to decouple logic. You will still need some point of entry, since android does not allow to implement your own constructors—this is why the application class is used.
I found that solution using Local Broadcast which is implemented in Application Class and send event on Local Broadcast which is received in Application Class.
class AppApplication : Application() {
override fun onCreate() {
super.onCreate()
registerBroadcast()
}
private fun startProfileActivity() {
val intent = newIntent<MyProfileActivity>(this)
intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP)
this.startActivity(intent)
}
private fun registerBroadcast() {
LocalBroadcastManager.getInstance(this)
.registerReceiver(broadCastReceiver,IntentFilter(BROADCAST_VIEW_PROFILE))
}
private fun unregisterBroadcast() {
LocalBroadcastManager.getInstance(this)
.unregisterReceiver(broadCastReceiver)
}
private val broadCastReceiver = object : BroadcastReceiver() {
override fun onReceive(contxt: Context?, intent: Intent?) {
when (intent?.action) {
BROADCAST_VIEW_PROFILE -> {
startProfileActivity()
}
}
}
}
override fun onTerminate() {
super.onTerminate()
unregisterBroadcast()
}
}
When you send the event in an Application like this
private fun viewProfileEventSend() {
// Send Broadcast for view profile to `APP`
val intent = Intent(BROADCAST_VIEW_PROFILE)
LocalBroadcastManager.getInstance(requireContext()).sendBroadcast(intent)
}
Because your module doesn't need to get the instance of Application or any interface.