I am building an app in which when the user first installs it, In the MainActivity(Launcher Activity) the app asks what type of user he is(Let's say Driver or Rider)? Based on the user selection he is directed to the corresponding login screen. After the user is logged in he directed to HomeActivity. If he closes the app without logging out, next time he opens the app he should directly see the HomeActivity(based on what usertype he selected). For one user the code in the MainActivity would be like:
#Override
protected void onStart() {
super.onStart();
// Check if user is signed in (non-null) and update UI accordingly.
FirebaseAuth mAuth = FirebaseAuth.getInstance();
FirebaseUser currentUser = mAuth.getCurrentUser();
if (currentUser != null) {
sendToStart();
}
}
private void sendToStart() {
Intent startIntent = new Intent(MainActivity.this,DriverHomeActivity.class);
startActivity(startIntent);
finish();
}
I am new to Android and Firebase. I don't how this should be done for two types users.
Create one login screen, in that screen the user can choose work type(two buttons or radio buttons).
If he chooses Driver, then create a Driver node in the database with the information he wrote and direct him to the home activity:
FirebaseUser user=FirebaseAuth.getInstance().getCurrentUser();
DatabaseReference ref=FirebaseDatabase.getInstance().getReference().child("Drivers").child(user.getUid());
ref.child("name").setValue(name);
Intent startIntent = new Intent(MainActivity.this,DriverHomeActivity.class);
startActivity(startIntent);
finish();
Also do the same for Rider.
If the user, closes the app without logging out. In the first activity that should open (ex: splash activity), check if there is a current user and check type of user:
FirebaseUser user = FirebaseAuth.getInstance().getCurrentUser();
DatabaseReference driver = FirebaseDatabase.getInstance().getReference().child("Drivers");
DatabaseReference rider = FirebaseDatabase.getInstance().getReference().child("Riders");
if (user != null) {
driver.child(user.getUid()).addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
if (dataSnapshot.exists()) {
Intent i = new Intent(SplashActivity.this, DriverHomeActivity.class);
startActivity(i);
finish();
} else {
Intent intent = new Intent(SplashActivity.this, RiderHomeActivity.class);
startActivity(intent);
finish();
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
Here in the splash activity, it will check if there is a current user(if user is logged in) user.getUid() will return the current userid and then it will check if this userid is under the Drivers node, and then it directs him to the driver's home activity.
Your issue can be solved by the usage of SharedPreference mechanism.
Here you can keep the login status in SharedPreference like below:-.
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences();
prefs.edit().putBoolean("login", true).commit();
when the user won't logged out and coming back, you should check the login status in splash screen using the SharedPreference variable and moved the user to corresponding activity.
Boolean loginStatus = prefs.getBoolean("login", false);
if (loginStatus) {
// Move to Home activity
} else {
//Move to login screen
}
Little bit documentation here
You might consider keeping a LauncherActivity in your android application which will decide which activity to launch further. A sample LauncherActivity might look like the following.
public class LauncherActivity extends AppCompatActivity {
private boolean loggedIn = false;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
boolean isLoggedInAsDriver = getSharedPreferences.getBoolean("is_driver", false);
startApplication(isLoggedInAsDriver);
finish();
}
private void startApplication(boolean isLoggedInAsDriver) {
Intent intent;
if (isLoggedInAsDriver) {
intent = new Intent(LauncherActivity.this, DriverActivity.class);
startActivity(intent);
finish();
} else {
intent = new Intent(LauncherActivity.this, RiderActivity.class);
startActivity(intent);
finish();
}
}
}
Your application can have only one launcher, so mark the LauncherActivity as the launcher of your android application in the AndroidManifest.xml like the following.
<activity
android:name=".LauncherActivity"
android:label="Your app name"
android:theme="#style/AppTheme.NoActionBar">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
Now you may have noticed that I have used a flag (i.e. isLoggedInAsDriver) to start the desired activity from the LauncherActivity. This is a preference data that you need to store in your SharedPreferences when you can determine if the user is a driver or rider. Then each time your application launches via your LauncherActivity, check the stored value and get the user type. Based on the user type, launch the second activity.
use this code in manifest
and also
you should save user type with user ID in database and at start app check this first and then show home page.
<activity android:name=".MainActivity/>
<activity android:name=".DriverHomeActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
Related
I have a website that has a web version and a webview. It is necessary to open directly in the app (if installed) the link to recover password that the user receives in his email.
After adding the code below to AndroidManifest.xml, I was able to open the app by clicking on the link.
<intent-filter>
<action android:name="android.intent.action.VIEW"/>
<category android:name="android.intent.category.DEFAULT"/>
<category android:name="android.intent.category.BROWSABLE"/>
<data android:host="*" android:scheme="myapp"/>
</intent-filter>
Link I created to test:
Click
However, it always opens the home page in the app, instead of opening the specific page in the link.
Do I need to make any further modifications to the app to interpret this type of link?
MainActivity:
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
webView = findViewById(R.id.webview);
webView.loadUrl("https://www.site.app");
webView.setWebViewClient(new WebViewClient() {
#Override
public boolean shouldOverrideUrlLoading(WebView wv, String url) {
}
#Override
public void onPageFinished(WebView view, String url) {
}
});
}
SOLUTION:
Intent intent = getIntent();
Uri data = intent.getData();
if (data != null && data.toString().startsWith("myapp:")) {
urlIntent = data.toString().replace("myapp", "https");
} else {
urlIntent = "https://www.site.app";
}
webView.loadUrl(urlIntent);
Deeplinks normally just triggers the app to be opened, they don't help you navigate or open a specific location unless you define Fragment or Activity based intent-filters or manually handle.
You should get the data which includes URL from intent first then give it to WebView as a URL. You can do that inside either onNewIntent or onCreate methods like so;
override fun onNewIntent(intent: Intent){
super.onNewIntent(intent)
val uri = intent.data
}
then load it in WebView like;
webview.loadUrl(uri.toString())
I am building an Android application where one activity is a login screen. When the app is opened, if a user has already logged in, I would like to skip the LoginActivity and have the user be directed to another one. When a user logs into my app (using Google Firebase), I save their username and other data in their device's shared preferences. When they log out, their shared preferences are cleared.
The way I currently have my manifest file is such that the only activity that can be opened when the app is started is the LoginActivity:
<activity android:name=".LoginActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
In the LoginActivity's OnCreate() method, if there is a username saved in the shared preferences (meaning a user is logged in), I immediately change activities:
public class LoginActivity extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
SharedPreferences userData = getApplicationContext().
getSharedPreferences("userdata", 0);
String n = userData.getString("username", "");
if (!userData.getString("username", "").equals(""))
{
Intent myIntent = new Intent(LoginActivity.this, TabbedActivity.class);
startActivity(myIntent);
}
}
However, there is a problem with this approach. Many times, the LoginActivity is still shown for a split second before starting the TabbedActivity. I would like to fix this so that the LoginActivity is actually never seen at all if a user is logged in.
I assume that the approach I'm taking is all wrong and there is a much cleaner way of doing it such that the correct activity is immediately opened. Any help on this would be greatly appreciated.
A possible approach:
Create a style for splash theme:
<style name="SplashTheme" parent="Theme.AppCompat.NoActionBar">
<item name="android:statusBarColor">#color/background_splash</item>
<item name="android:windowBackground">#drawable/background_splash</item>
</style>
Create a background drawable (drawable/background_splash.xml) for splash:
<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item>
<color android:color="#color/background_splash"/>
</item>
<item>
<bitmap
android:src="#drawable/ic_splash_logo"
android:gravity="center"/>
</item>
</layer-list>
In your manifest, set the SplashTheme as your application/launcher-activity theme:
<application
...
android:theme="#style/SplashTheme">
<!-- or -->
<activity
...
android:theme="#style/SplashTheme">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
In your LoginActivity in onCreate() set your normal app theme and content view if the user is not logged in and also set app theme in the MainActivity (or set it in the Manifest)
public class LoginActivity extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
val isUserLoggedIn = ...
if (!isUserLoggedIn)
{
setTheme(R.style.AppTheme)
setContentView(R.layout.activity_login)
}
else {
//Navigate to Main Activity
}
}
}
Splash screen reference
I'm not sure if this is the best approach, but you could create a Loading activity that start the activity needed in any situation like.
public class LoadingActivity extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
SharedPreferences userData = getApplicationContext().
getSharedPreferences("userdata", 0);
String n = userData.getString("username", "");
if (!userData.getString("username", "").equals(""))
{
Intent myIntent = new Intent(LoginActivity.this, TabbedActivity.class);
startActivity(myIntent);
}else{
Intent myIntent = new Intent(OtherActivity.this, TabbedActivity.class);
startActivity(myIntent);
}
}
And about the view add a gif or a logo meanwhile.
I have an implementation, but no shared preferences, and I don't see the login screen at any time.
My structure is as follows: I have an initial splash screen, then a main activty, which is where I override the onStart method to check if the user has an open section if it is passed to the home activity.
I hope it helps you, if you don't tell me and I'll add more code.
public override fun onStart() {
super.onStart()
// if user is loged goto Home Activity
if (firebaseUser != null) {
// Name, email address, and profile photo Url
val name = firebaseUser?.displayName
val email = firebaseUser?.email
val photoUrl = firebaseUser?.photoUrl
val uid = firebaseUser?.uid
val emailVerified = firebaseUser!!.isEmailVerified
goToActivity<HomeActivity>()
}
}
I have an app with two activities: MainActivity, which contains a URL entry field where the user can enter a YouTube video URL and press a submit button, to start the second activity, VideoActivity, which displays some information about this video (fetched from another web server).
The app also has a feature to receive intent via the Youtube application. When user presses the share button within the Youtube app, my app appears in the share list. Upon pressing share from the Youtube app, MainActivity should be brought to the front, and the URL should be posted within the MainActivity's URL field.
However, this only happens correctly on the first share. If the app is in the background when user shares from Youtube app, they are taken to whatever the last visible activity was, whether it is MainActivity or VideoActivity, (and even if it is MainActivity, the URL is not posted into the URL field, but the field is left in whatever state it was in when the app was last visible).
Here is my current AndroidManifest.xml:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.youcmt.youdmcapp">
<uses-permission android:name="android.permission.INTERNET"/>
<application
android:allowBackup="true"
android:icon="#mipmap/ic_launcher"
android:label="#string/app_name"
android:roundIcon="#mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="#style/AppTheme">
<activity android:name=".MainActivity"
android:launchMode="singleTop">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.SEND"/>
<category android:name="android.intent.category.DEFAULT"/>
<data android:mimeType="text/plain"/>
</intent-filter>
</activity>
<activity
android:name=".VideoActivity"
android:parentActivityName=".MainActivity"/>
<service
android:name=".FetchVideoService"
android:exported="false"/>
</application>
</manifest>
Here is my MainActivity.java code:
public class MainActivity extends AppCompatActivity {
private ResponseReceiver mReceiver;
private EditText mUrlEditText;
private Button mSearchButton;
private ProgressBar mProgressBar;
#Override
protected void onCreate(Bundle savedInstanceState) {
setContentView(R.layout.activity_main);
super.onCreate(savedInstanceState);
mUrlEditText = findViewById(R.id.url_search_et);
Intent intent = getIntent();
if (intent.getType()!=null &&
intent.getType().equals("text/plain")) {
Bundle extras = getIntent().getExtras();
String value = extras.getString(Intent.EXTRA_TEXT);
if(value!=null)
{
mUrlEditText.setText(value);
}
}
mProgressBar = findViewById(R.id.progress_bar);
mSearchButton = findViewById(R.id.search_button);
mSearchButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
try {
askForVideo(mUrlEditText.getText().toString());
mSearchButton.setVisibility(View.INVISIBLE);
mProgressBar.setVisibility(View.VISIBLE);
} catch (Exception e) {
mUrlEditText.setText("");
mUrlEditText.setHint(e.getMessage());
e.printStackTrace();
}
}
});
}
#Override
protected void onResume() {
super.onResume();
//register the ResponseReceiver
mReceiver = new ResponseReceiver();
IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction(FETCH_VIDEO_INFO);
registerReceiver(mReceiver, intentFilter);
}
private void askForVideo (String url) throws Exception {
try {
Intent intent = FetchVideoService.newIntent(this, url);
startService(intent);
} catch (Exception e) {
mUrlEditText.setText(e.getMessage());
}
}
public class ResponseReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
int status = intent.getIntExtra(EXTRA_VIDEO_STATUS, FAIL);
mProgressBar.setVisibility(View.INVISIBLE);
mSearchButton.setVisibility(View.VISIBLE);
if(status==FAIL)
{
mUrlEditText.setText("");
mUrlEditText.setHint("Error retrieving video!");
}
else if(status==SUCCESS) {
Video video = intent.getParcelableExtra(EXTRA_VIDEO);
Intent videoActivityIntent =
VideoActivity.newIntent(getApplicationContext(), video);
startActivity(videoActivityIntent);
}
}
}
#Override
protected void onPause() {
unregisterReceiver(mReceiver);
super.onPause();
}
}
I do not think any of the other files will be useful in understanding the problem. Although this seems like something many app creators should have to deal with, I can find no answers to this problem. Please comment if you feel I should add any additional information and thank you in advance for any help!
Update: testing demonstrates that after the first use of "Share" from YouTube (and considering app remains in the background), the MainActivity no longer receives any new intent on further shares. However, my app is still brought to the foreground somehow. This is very confusing to me.
When you share from another app, your MainActivity is brought to the front and onNewIntent() is called on it. You don't override onNewIntent() so you never see the share Intent.
I have a class called ShowBoardList where I check if user has logged in. If user hasn't logged in, then I want to return to the MainActivity which provides the user with buttons to login into different services.
My AndroidManifests.xml looks like this:
<application
<activity android:name="im.chaitanya.TaskTimer.MainActivity" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity android:name="im.chaitanya.TaskTimer.WebViewActivity" >
</activity>
<activity android:name="im.chaitanya.TaskTimer.ShowBoardList"
android:label="Your Tasks">
</activity>
</application>
ShowBoardList.java looks like this:
...
Intent mainActivityIntent = new Intent(ShowBoardList.this, im.chaitanya.TaskTimer.MainActivity.class);
Intent intent = getIntent();
String url = intent.getStringExtra(WebViewActivity.EXTRA_MESSAGE); //url can be null here
Keys keys = new Keys(); //this gets an API key
SharedPreferences settings = getSharedPreferences("mySettings", 0);
String savedToken = settings.getString("token", "Empty");
if (MyUtils.equalsWithNulls(url,"tasktimer://oauthresponse#token=")) {
Log.d("From ME:", "I've reached inside url check");
mainActivityIntent.putExtra(caller, "ShowBoardList");
//caller is a String. I'm storing the name of the current activity (ShowBoardList) in it.
//So that the main activity (which I'm trying to call) will know where the call came from.
startActivity(mainActivityIntent);
}
if(savedToken.equals("Empty") || savedToken.equals("")) {
String searchString = "#token=";
int tokenIndex = url.indexOf(searchString) + searchString.length(); //Since url can be null there can be an error here
String token = url.substring(tokenIndex);
savedToken = token;
SharedPreferences.Editor editor = settings.edit();
editor.putString("token", token);
editor.apply();
}
...
Condtion equalsWithNulls checks if url is null OR equal to the string in the argument. I have log statements there to check whether control reaches inside the if statement. The main activity however doesn't start.
Edit: onCreate() of MainActivity.java looks like this:
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
SharedPreferences settings = getSharedPreferences("mySettings", 0);
String token = settings.getString("token", "Empty");
Intent intent = new Intent(this, ShowBoardList.class);
if(token != "Empty") {
startActivity(intent);
}
intent = getIntent();
String callerActivity = intent.getStringExtra(ShowBoardList.caller);
View coordinatorLayoutView = findViewById(R.id.snackbarPosition);
if (callerActivity!=null && callerActivity == "ShowBoardList") {
Snackbar
.make(coordinatorLayoutView, "Permission Denied", Snackbar.LENGTH_LONG)
.show();
}
setContentView(R.layout.activity_main);
}
Try to define your new Intent wherever you required.
Intent newIntent = new Intent(ShowBoardList.this, im.chaitanya.TaskTimer.MainActivity.class);
newIntent .putExtra(caller, "ShowBoardList");
startActivity(newIntent );
My solution is based on Sourabh's comment on the question. I realised from my logs that the activity was indeed being started.
What I didn't realise was that when startActivity() is called, the calling activity (in this case ShowBoardList) is paused and when ShowBoardList was being called again, it would resume from after startActivity().
Therefore the solution here was to call finish() and then return immediately after the startActivity() which ensures that onCreate is called the next time. I hope that makes sense if anyone is in the same situation.
These questions helped me understand more about finish():
about finish() in android
onCreate flow continues after finish()
So I want to launch a service from a shortcut. I know that this is not possible to do directly, so I've set up a activity with the sole purpose of starting the service.
The aim of my service is to send an intent to another app and then 5 seconds later send another so I've used a CountDownTimer to do this.
However, when I launch the Activity that starts the service from the shortcut (this is getting confusing) it launches the apps UI. I don't want this, as I want it to be a background service.
What am I doing wrong. I've only just got into development, so it could be something obvious, but I've been battling with this for a few days now.
For some reason when I run it from the service it just launches the app straight away...
When I run it straight from the invisible activity it runs properly for the 1st 5 seconds fine and then loads the app...
I can't figure out why it's loading the app at all.
I've included as much info as I can that would be relevant.
Any help is appreciated!
My service:
public class Pop1_5Service extends IntentService {
public Pop1_5Service() {
super("Pop1_5Service");
}
#Override
protected void onHandleIntent(Intent intent) {
// Normally we would do some work here, like download a file.
// For our sample, we just sleep for 5 seconds.
new CountDownTimer(5000, 2500) {
public void onTick(long millisUntilFinished) {
Intent i = new Intent(INTENT_ACTION);
Bundle b = new Bundle();
b.putInt(BUNDLE_VERSION_CODE, 1);
b.putString(BUNDLE_STRING_NAME, "POP1");
b.putString(BUNDLE_STRING_VALUE, "1");
i.putExtra(BUNDLE_NAME, b);
sendBroadcast(i); }
public void onFinish() {
Intent i = new Intent(INTENT_ACTION);
Bundle b = new Bundle();
b.putInt(BUNDLE_VERSION_CODE, 1);
b.putString(BUNDLE_STRING_NAME, "POP1");
b.putString(BUNDLE_STRING_VALUE, "1");
i.putExtra(BUNDLE_NAME, b);
sendBroadcast(i); }
}
}.start();
}
}
Activity that launches service:
public class Pop1_5Activity extends Activity
{
#Override
public void onCreate(Bundle savedInstanceState){
super.onCreate(savedInstanceState);
Intent intent = new Intent(this, Pop1_5Service.class);
startService(intent);
finish();
}
}
Subsection of Manifest:
<activity
android:name=".Pop1_5Activity"
android:theme="#android:style/Theme.NoDisplay">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<service android:name=".Pop1_5Service" />
And the 'Create a Shortcut' Activity:
public class CreateShortcutActivity extends Activity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Intent shortcutintent = new Intent(this, Pop1_5Activity.class);
ShortcutIconResource iconResource = Intent.ShortcutIconResource.fromContext(this, R.drawable.ic_launcher);
Intent intent = new Intent();
intent.putExtra(Intent.EXTRA_SHORTCUT_INTENT, shortcutintent);
intent.putExtra(Intent.EXTRA_SHORTCUT_NAME, "Pop1_5");
intent.putExtra(Intent.EXTRA_SHORTCUT_ICON_RESOURCE, iconResource);
setResult(RESULT_OK, intent);
finish();
}
}
From the look of things, it looks like CreateShortcutActivity does nothing.
Your LAUNCHER is Pop1_5Activity, so when the user presses the app icon, this Activity will run, and it launches the Service.
All the code you have showed us are "invisible", the two Activities finish() themselves, and the Service is a Service.
You might want to look at how your BroadcastReceiver handles your broadcast. For instance, does it create another Activity through PendingIntent? Is the Activity created invisible?
Maybe you should try creating a pending Service instead of pending Activity in the BroadcastReceiver.