I am using the AppCompatDelegate.setDefaultNightMode(mode); to set the Night Mode in my Android Application, whenever the user chooses any mode that preference configuration in the Shared Preferences on their device, now while I use the shared preferences to set the UI mode when the app starts from the Splash Screen Activity, the activity is getting recreated and then there are 2 instances of my app, as the Splash Screen intents to Landing Activity.
Here is my SplashScreen.java
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_splash_screen);
SharedPreferences prefs = getSharedPreferences(UI_MODE, MODE_PRIVATE);
name = prefs.getString("uiMode", "System");
applyUI();
fireSplashScreen();
}
private void applyUI() {
if (name.equals("Dark")){
AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_YES);
}
else if (name.equals("Light")){
AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_NO);
}
else {
AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM);
}
private void fireSplashScreen() {
Intent i = new Intent(SplashScreen.this, Landing.class);
startActivity(i);
finish();
}
What can I do to avoid creating multiple instances of the Landing Activity?
Make sure to call AppCompatDelegate.setDefaultNightMode() as soon as possible. E.g. before calling super.onCreate(). Ideally, you would want to call it in Application.onCreate(). Also, make sure to use the latest version of AppCompat (at least 1.1.0) or you might face this issue anyway.
See this answer for more details.
Related
I'm writing an app with 8 different Activities that are all interconnected(accessible from one another). I'm wondering how I can have the app always reopen from a full close (not just home button click but from closing the background activity) with the same screen it was exited from.
For example, from my Splash Screen I navigate to a home screen. From that home screen I navigate to the Settings. Now, I click the multi-tab viewer and close all apps. How can I have Settings(or wherever I had left off) become the launching activity when the app is reopened?
Thanks in advance!
You can use SharedPreferences to store the class name of the latest Activity. Later, when relaunch, you can redirect to it from the Splash Screen.
You can do that simply by using SharedPreferences. When you launch an activity, it changes it's last activity value in SharedPreferences to point at that activity.
When you close the app and then reopen it, your Main/Launcher activity (Which may be your Splash Screen) then checks for this value and launches the activity according to this value which represents the last activity the app was on.
Here is the code that would fit the Main/Launcher activity:
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
String lastActivity = getDefaults("LAST_ACTIVITY", MainActivity.this);
Intent intentLastActivity;
// use an if statement to find out which activity it is
if(!lastActivity.equals("") || !lastActivity.equals(null)){
if(lastActivity.equals("home")){
intentLastActivity = new Intent(MainActivity.this, HomeActivity.class);
}else if(lastActivity.equals("settings")){
intentLastActivity = new Intent(MainActivity.this, SettingsActivity.class);
// add more else-if statements for the other activities.
}
startActivity(intentLastActivity); // launch the last activity
finish(); // finish/close the current activity.
}
}
public static void setDefaults(String key, String value, Context context) {
SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(context);
SharedPreferences.Editor editor = preferences.edit();
editor.putString(key, value);
editor.apply();
}
public static String getDefaults(String key, Context context) {
SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(context);
return preferences.getString(key, null);
}
Once that code is placed in your Main/Launcher activity. You can then set this last activity value in SharedPreferences in other activities when they are launched.
Code placed in the other activities (in the onCreate method):
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_settings);
// we are at the settings activity. Change last activity value to settings
MainActivity.setDefaults("LAST_ACTIVITY","settings",this);
}
Luckily the methods to change the last activity are accessible in other activities.
If you're able to implement this well in your app. It should work as you want it to.
HOPE THIS HELPS!
I have QR scanner app. There are 3 activities in the app.
1) Main activity - Button to open camera and start scanning
2) QR activity - Scan a QR code
3) Web Activity - On successful scanning, open a web page in the app
Here, the Main activity and QR activity should only launch once, only after the initial install. I read somewhere about using shared preferences. But I am a little confused as to where do I check the variable, as in which activity. Should I check my shared variable in the Main Activity?
This is my first app. Sorry if this is a silly doubt.
It's correct, you have to do it with SharedPreferences.
Here is a good explaination about how to use them
On the first activity shown, you have to add in the onCreate method those lines:
//this retrieve the sharedpreference element
SharedPreference myPref = this.getSharedPreferences(
"prefName", Context.MODE_PRIVATE);
//this retrieve the boolean "firstRun", if it doesn't exists, it places "true"
var firstLaunch = myPref.getBoolean("firstLaunch", true);
//so, if it's not the first run do stuffs
if(!firstLaunch){
//start the next activity
finish();
}
//else, if it's the first run, add the sharedPref
myPref.edit().putBoolean("firstLaunch", false).commit();
hope this helps
To complete #Pier Giorgio Misley answer you can put the "firstLaunch" check on your Main Activity or alternatively put it in another "splash" activity
For putting it in the main activity simply set the ui to some neutral color until you decide if you should finish the activity and launch the Web Activity or show the Main Activity logic
Alternatively, you can create a "splash" screen which can function as a bridge activity (which shows some logo or a nice background color) which check the varible and decide which activity to open Android splash
As pier mentioned, saving that it has been seen once is the way to go. However, I have found on some older devices, shared preferences is not reliable!
I recommend instead using SQLite database.
Create a table as follows
TABLE NAME: SEEN_ACTIVITY
Column 1: ID INT PRIMARY KEY
Column 2: SEEN VARCHAR
Then, once the activity has been launched, check if there is a record for id = '0' in SEEN_ACTIVITY. If not, then insert one as follows, (0, true).
Then, every time the app launches, check to see if the record exists of (0, true). If not, launch the extra activity.
My MainActivity.java
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_splash);
new Handler().postDelayed(new Runnable() {
#Override
public void run() {
SharedPreferences settings = getSharedPreferences("prefName",MODE_PRIVATE);
boolean firstLaunch = settings.getBoolean("firstLaunch", true);
if(firstLaunch == false) {
SharedPreferences.Editor editor=settings.edit();
editor.putBoolean("firstRun",true);
editor.commit();
Intent i = new Intent(SplashActivity.this, MainActivity.class);
startActivity(i);
finish();
}
else {
Intent i = new Intent(SplashActivity.this, ScannerActivity.class);
startActivity(i);
finish();
}
}
}, SPLASH_TIME_OUT);
}
Wrote this code in a new Splash Activity
How do I simply just restart my ENTIRE app instead of trying to worry about saving the instance perfectly in onSaveInstanceState and reinitializing everything perfectly when resumed/restored in onRestoreInstanceState? (this can quickly become error prone)
UPDATE 10.1.16
I chose to do this in onCreate since onRestoreInstanceState behaves oddly sometimes.
This method is based on the fact that the onCreate(Bundle) is null unless the activity is being revived in which case it is whatever onSaveInstanceState(Bundle) set it to.
I set TWO flags. One in onSaveInstanceState in the Bundle so to know that it is a valid Bundle set by me. The other in the class itself to determine if onCreate was called because of recreation or rotation. And so in onCreate I checked to see if onSaveInstanceState is not null, check the Bundle flag, and check bInit (which defaults to false). If both flags are true then it means android dumped and destroyed our apps memory and the safest way to ensure everything is initialized again in a linear-style application is to just restart it and launch the beginning activity.
public class SomeMiddleActivity extends AppCompatActivity
{
private static boolean bInit = false; // only way it will be false again is if android cleared our memory and we are recreating
#Override
public void onSaveInstanceState(Bundle state)
{
// set a flag so that onCreate knows this is valid
state.putBoolean("StateSaved", true);
super.onSaveInstanceState(state);
}
#Override
protected void onCreate(Bundle savedInstanceState)
{
// this must be called first always for some reason
super.onCreate(savedInstanceState);
if (savedInstanceState != null)
{
if (savedInstanceState.getBoolean("StateSaved", false) && !bInit)
{
// we were recreated... start app over
Intent intent = new Intent(getApplicationContext(), Startup.class);
startActivity(intent);
finish();
return;
}
}
bInit = true; // this will stay true until android has cleared our memory
.......
}
Hope this helps someone and although this has worked thus far, if anyone has a different suggestion let me know.
And FYI: the onSaveInstanceState(Bundle, PersistableBundle) version of onSaveInstanceState is never called ever so I dont know why they even implement it. (?)
REFERENCES:
ACCORDING TO ANDROID DOCUMENTATION
onCreate
Bundle: If the activity is being re-initialized after previously being shut down then this Bundle contains the data it most recently supplied in onSaveInstanceState(Bundle). Note: Otherwise it is null.
Try implementing this way
private final String IS_RE_CREATED = "is_re_created";
#Override
public void onSaveInstanceState(Bundle outState) {
outState.putBoolean(IS_RE_CREATED, true);
super.onSaveInstanceState(outState);
}
#Override
protected void onRestoreInstanceState(Bundle savedInstanceState) {
super.onRestoreInstanceState(savedInstanceState);
if (savedInstanceState.containsKey(IS_RE_CREATED)) {
boolean isRecreated = savedInstanceState.getBoolean(IS_RE_CREATED, false);
if (isRecreated) restartApplication(this);
}
}
public void restartApplication(Context context) {
String packageName = context.getPackageName();
PackageManager packageManager = context.getPackageManager();
// Intent to start launcher activity and closing all previous ones
Intent restartIntent = packageManager.getLaunchIntentForPackage(packageName);
restartIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
restartIntent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
context.startActivity(restartIntent);
// Kill Current Process
Process.killProcess(Process.myPid());
System.exit(0);
}
Note: It is not a recommended to forcefully restart application.
How do I simply just restart my app instead of trying to worry about saving the instance
You mean the current activity? Do nothing (Don't implement onSaveInstanceState and onRestoreInstanceState).
The activity gets created automatically when changes happen. If there is no saved instance state, the activity won't restore any data.
Edit:
I think I came across similar issue too few weeks earlier, where I've to kill all the activities in the back stack and open a fresh new activity.
// Start Main Activity
Intent intent = new Intent(this, MainActivity.class);
finishAffinity();
startActivity(intent);
Use finishAffinity(). This works on > API 16.
When you kill all the activities in the back stack and open the main activity, it is kind of similar to restarting your app.
i make app with two activities.
firs has:
2 ExitText(login and password);
button(confirm, save data with SharedPreferences, intent to second activity).
second one:
2 TextView(get login and password with SharedPreferences);
button(clear data on SharedPreferences, intent to firsActivity).
how to make next: while there are some data on SharedPreferences - app will be started from the 2nd screen.
for example, i made:
if (user!=null && pass!=null){ Intent enterIntent = new Intent(MainActivity.this,SecondActivity.class);
startActivity(enterIntent);
}
but, technically it first run the firstActivity and than go to the secondOne. if there are some method to start app with the another activity (not mainOne)?
You won't be able to check to see if there are values in SharedPreferences before entering one Activity.
What you can do is check the value before displaying the UI (before calling setContentView(R.layout.my_layout)), and either continue along, or start the next Activity.
public class MyStartActivity extends Activity {
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
SharedPreferences preferences = getSharedPreferences("my_prefs", Context.MODE_PRIVATE);
if (preferences.contains("my_key")) {
// start next Activity
}
setContentView(R.layout.my_layout);
}
}
If you don't want the first one on the back stack, you can call finish() after starting the second one (or use appropriate flag(s) on intent).
Another approach would be to have only one activity with to fragments and decide dynamically which one set on start. With fragments you can also easily change layouts on button click or on back pressed.
I'm not sure if it will work, because app has to start at the first activity. First activity checks login and pass at share preferences and then you can go to the second activity
try this
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
SharedPreferences preference = getSharedPreferences("my_prefs", Context.MODE_PRIVATE);
if (preference.getSting("key",null)!= null) {
// start new Activity
//finish this activity so it not in back stack
} else {
setContentView(R.layout.my_layout);
}
}
I am making an android application but i can't figure out how i can make the setup screen show up only the first time.
This is how the application is going to work:
User launches the application after installation and is being shown the welcome/setup screen. And once the user is done with the setup, the setup screens will never appear again unless the user reinstalls the application.
How can i make this happen???
Please help and thanks SO much in advance!
Use SharedPreferences to test whether its the first start or not.
Note: The below code was not tested.
In your onCreate (or whereever you want to do things depending on first start or not), add
// here goes standard code
SharedPreferences pref = getSharedPreferences("mypref", MODE_PRIVATE);
if(pref.getBoolean("firststart", true)){
// update sharedpreference - another start wont be the first
SharedPreferences.Editor editor = pref.edit();
editor.putBoolean("firststart", false);
editor.commit(); // apply changes
// first start, show your dialog | first-run code goes here
}
// here goes standard code
Make one helper activity. This will be your launcher activity.It will not contain any layouts, It will just check for first fresh run of an app. If It will first run, then setup activity will be started otherwise MainActivity will be start.
public class HelperActivity extends Activity {
SharedPreferences prefs = null;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Perhaps set content view here
prefs = getSharedPreferences("com.mycompany.myAppName", MODE_PRIVATE);
}
#Override
protected void onResume() {
super.onResume();
if (prefs.getBoolean("firstrun", true)) {
// Do first run stuff here then set 'firstrun' as false
//strat DataActivity beacuase its your app first run
// using the following line to edit/commit prefs
prefs.edit().putBoolean("firstrun", false).commit();
startActivity(new Intent(HelperActivity.ths , SetupActivity.class));
finish();
}
else {
startActivity(new Intent(HelperActivity.ths , MainActivity.class));
finish();
}
}
}