Good morning everyone,
I'm having problems again in my proccess to create my first app. This time with the SharedPreferences file.
I have 2 activities that must use the same SharedPreferences file. The first one is the MainActivity and the second one an edit layout for the data.
In the MainActivity I have the following method where I use the data to connect to a PLC:
//initialize the SharedPreferences variables for the PLC Data and Connection
m_DataPLC = getSharedPreferences("CFTPreferences",CONTEXT_IGNORE_SECURITY);
m_DataEditorPLC = m_DataPLC.edit();
//Gets the number from the IP Address for the connection with the PLC
//As default, the system takes the value of a static string value
//This is when the SharedPreferences file is empty
m_IpAddress = getResources().getString(R.string.ip_Address);
m_NewIpAddress = m_DataPLC.getString("newIpAddress", m_NewIpAddress);
if(m_NewIpAddress == null)
{
m_DataEditorPLC.putString("newIpAddress", m_IpAddress.toString());
m_DataEditorPLC.commit();
m_OldIpAddress = m_NewIpAddress = m_IpAddress;
}
else
{
m_OldIpAddress = m_IpAddress = m_NewIpAddress;
}
//Start the connection with the PLC
m_Connection = new ModbusConnection(this,m_IpAddress);
inet = m_Connection.loginPLC();
In my second activity, I have to load the same data and be able to modify it. What I do first is the login to the SharedPreferencesFile:
dataPLC = getSharedPreferences("CFTPreferences",CONTEXT_IGNORE_SECURITY);
dataEditorPLC = dataPLC.edit();
Then I do the writing with a click action of a button:
public void setIPAddress()
{
if (!newIpAddress.equals(etIPAddress.getText().toString()))
{
dataEditorPLC.putString("oldIpAdd",ipAddress.toString());
dataEditorPLC.putString("newIpAdd",etIPAddress.getText().toString());
dataEditorPLC.commit();
}
}
I dunno if I'm doing wrong calling the same file twice or if I have to do something extra to mend this. It looks like it does the update, but it doesn't refresh the MainActivity. If someone had some sort of the same problem, I would appreciate the help on this one!!!.
Many thanks in advance!!!
I think you are accessing the value of Shared Preferences in onCreate() of first Activity. That will be your problem. Beacuse when you come back from second activity to first activity, your onCreate() is not called, instead onResume() is called. So the better thing to do is move the code where you access SharedPreferences value to a seperate function and call this function in both onCreate() and onResume().
for e.g.
public void getSharedPrefernces() {
m_DataPLC = getSharedPreferences("CFTPreferences",CONTEXT_IGNORE_SECURITY);
m_NewIpAddress = m_DataPLC.getString("newIpAddress", m_NewIpAddress);
}
Hope this Helps..!! Cheers...
Related
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
I must write a program with android which can find ssid and show it. My problem is how can i make a relation between an image button in first page and an activity or function in other page.
Buy the way i'm beginner and download the code of searching method because of that i can not recognize which one is the main method or function for pass it to setonclick method that i write for image button in first page?
please answer as soon as you can i need it immediately.
If you are trying to call another activity's method from your existing activity then you should use these steps.
First save the context or instance of the callee activity; for this you can use a class that holds your global data. In this class make the object of this activity.
FirstActivity first = null;
When the callee activity is first created, initialize this instance.
public void setFirstActivity(FirstActivity factivity)
{
first = factivity;
}
And when you do need to call this callee activity's method then access this instance from this global class and with the help of this instance, you will have access to the methods of the callee activity.
public FirstActivity getFirstActivity() {
return first ;
}
use it to get access to the methods or instances of the activity.
Click on the ImageButton, and add the name of the function in the On Click property (Simply the name, like myFunction)
In your code, copy-paste this function and replacemyFunction by the name of the function you chosed
public void myFunction(View v)
{
String ssid = ((EditText)findViewById(R.id.EditTextID)).getText().ToString();
Intent intent = new Intent(this, SecondView.class);
intent.putExtra("SSID_KEY", ssid);
startActivity(intent);
}
And replace SecondView by the class that displays your second page and EditTextName by the ID of your EditText.
In the OnCreate function of your second class, you can get your ssid using the following code
#Override
protected void onCreate(Bundle savedInstanceState) {
String ssid = getIntent().getStringExtra("SSID_KEY");
//Do other work here.
}
Also, on top of the class containing the ImageView, don't forget to add these lines:
import android.view.View;
import android.widget.EditText;
I have an intent that appear only if certain data have not been inserted yet. It's like a custom form to insert some data. If the data have been inserted, in the future the apps opened, the intent will not appear anymore. It will open another intent, the default one. (Usually this might occur when the apps opened for the first time)
How do I manage the intent since the default intent could only be one?
For example: If the apps opened for the first time it will startIntent Form
next time the apps opened (assumed the data already inserted) it will startIntent MainActivity
i use sharedpreferences to insert data only once , simply use it this way, in the below code
the intent will be started only once the application is first installed, after that it will start the main activity intent.
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (isFirstTime()) {
// startIntent Form
}
}
private boolean isFirstTime()
{
SharedPreferences preferences = getPreferences(MODE_PRIVATE);
boolean ranBefore = preferences.getBoolean("RanBefore", false);
if (!ranBefore) {
// first time
SharedPreferences.Editor editor = preferences.edit();
editor.putBoolean("RanBefore", true);
editor.commit();
}
return !ranBefore;
}
I assume you are doing something like registration thing, and want to show this form once. you have many options here.
1) As Ahmad(in the comments) says use SharedPreferences, and add some flag into it, which can tell you if the data is available or not. add a check in the very beginning of the activity/onCreate and open respective inten/activity depends on check.
2) you can use Database as well to see this value.
I would suggest you to use SPLASH screen, check this value/registration data into it. And if available start Activity A, else start Registration/default one.
I am working on an application, wherein after say 5 times the app is opened by a user, at 6th attempt the app should ask for feedback from user. I tried using Activity OnStart,OnResume, but its not working out since even after leaving and re-entering activity these methods are called. Also as per android functionality, I cannot quit app so that I can find it out from the first activity called. How do I find how many times the app was launched?
I hope this is not confusing.
Edit
Alternatively is there a way, wherein I can always resume my app from the first activity( or welcome page for eg.), once user presses home to quit the app.
This is actually quite simple. Using SharedPreference or the Database.
during OnCreate add 1 to the numberofTimes counter and commit.
OnCreate (Bundle bundle){
mPref = getPreferences();
int c = mPref.getInt("numRun",0);
c++;
mPref.edit().putInt("numRun",c).commit();
//do other stuff...
}
OnCreate is called regardless of you start the app or you resume the app, but isFinishing() returns true if and only iff the user (or you) called finish() on the app (and it was not being destroyed by the manager)
This way you only increment when you are doing fresh start.
the isFinishing() Method inside of a OnPause method to check to see if the activity is being finish() or just being paused.
#Override
protected void OnPause(){
if(!isFinishing()){
c = mPref.getInt("numRun",0);
c--;
mPref.edit().putInt("numRun",c).commit();
}
//Other pause stuff.
}
This covers all your scenarios:
1. user starts app/activity (+1)-> finishes app, exit with finish()
2. user starts app (+1) -> pause (-1) -> returns (+1)-> finish
3. user starts app (+1) -> pause (-1) -> android kills process (0) -> user returns to app (+1) -> user finish.
every scenario you only increment the "times run" counter once per "run" of the activity
Just:
declare:
private SharedPreferences prefs;
private SharedPreferences.Editor editor;
private int totalCount;
initialize in onCreate():
prefs = getPreferences(Context.MODE_PRIVATE);
editor = prefs.edit();
print or count wherever you want (any where in onCreate() or any specific click as you specified):
totalCount = prefs.getInt("counter", 0);
totalCount++;
editor.putInt("counter", totalCount);
editor.commit();
now print totalCount where you want to count e.g.:
System.out.println("Total Application counter Reach to :"+totalCount);
if you have a starting activity for app launch then you can implement it in following ways
1. Database:- through database you can save your application launch count and retrieve it on create of activity.
Static Variable:- static variable also retain values during application start and end
Application Preference:-you can store value in application preference and use it
problem with 2 and 3 approach is that if you switch off and on again your phone you will loose data. but if you still want to use 2 or 3 approach then 2 approach is very simple and
sample code for 3rd approach here
well you have to extends Application class and create a subclass from that
public class MyApp extends Application{
int visitCount;
onCreate(){
visitCount=0;
}
and you can mention it in your menifest file like
<application name="MyApp">
.....
</application>
and in onCreate of your activity you can get it by
MyApp myApp=(MyApp)getApplicationContext();
Edit1:
subclass your activity and override method
public class myActivity extends Activity{
#Override
onSaveInstanceState(Bundle outState){
super.onSaveInstanceState(outState);
counterFlag=true;
}
}
it is get called when user press home button
and again override onResume() and check whether your counter flag is enabled or not
and create all your activity by subclassing your MyActivity
also if any other activity has exit point on click of back button then you can override
#Override
public void back_pressed(){
}
and do your task accordingly
I think this would be the best option in order to cover all scenarios:
private static boolean valueOfLaunchCountModified = false;
#Override
protected void onCreate(Bundle savedInstanceState) {
if(!valueOfCountModified){
preferences = getPreferences(MODE_PRIVATE);
launchCount= preferences.getInt("launchCount", 0);
if(preferences.edit().putInt("launchCount", ++launchCount).commit()){
valueOfCountModified = true;
if(launchCount == 5){
//Do whatever you want
}
}
}
}
If we remember the definition of a static variable ("...They are associated with the class, rather than with any object. Every instance of the class shares a class variable...") we will discover that is perfect for us.
When onPause method or an orientation change is executed the value of "valueOfLaunchCountModified" doesn't change; however, if the app process is destroyed, the value of "valueOfLaunchCountModified" changes to false.
If you only want to count "true" invocations then extend Application and place counter logic into Application#onCreate. This could be a simple preference
I prefer to use onResume to track launch count since it’s getting called in every scenario (refer to Android Activity Lifecycle) when the activity is shown.
onResume could be called quite frequently depending on usage pattern, so instead of tracking launch count, it would be better to track launch session (as in only 1 launch count would be tracked per hour).
#Synchronized fun appSessionCount(sharedPref: SharedPreferences): Boolean {
val now = LocalDateTime.now(ZoneOffset.UTC)
val firstSeconds = sharedPref.getLong(KEY_FIRST_LAUNCH_DATE, 0)
if (firstSeconds == 0L) {
sharedPref.edit {
putLong(KEY_FIRST_LAUNCH_DATE, now.atZone(ZoneOffset.UTC).toEpochSecond())
}
}
val seconds = sharedPref.getLong(KEY_LAST_SESSION_DATE, 0)
val lastDate = if (seconds > 0) LocalDateTime.ofInstant(Instant.ofEpochSecond(seconds), ZoneOffset.UTC) else null
var count = sharedPref.getLong(KEY_SESSION_COUNT, 0)
// first time or 1 hour ago
if (lastDate == null || Duration.between(lastDate, now).toHours() >= 1) {
sharedPref.edit {
putLong(KEY_SESSION_COUNT, count + 1)
putLong(KEY_LAST_SESSION_DATE, now.atZone(ZoneOffset.UTC).toEpochSecond())
}
return true
}
return false
}
I run the code at onResume of my main activity.
class MainActivity : AppCompatActivity() {
lateinit var sharedPref: SharedPreferences
override fun onCreate(savedInstanceState: Bundle?) {
sharedPref = getSharedPreferences("LuaApp", Context.MODE_PRIVATE)
}
override fun onResume() {
super.onResume()
appSessionCount(sharedPref)
}
}
https://code.luasoftware.com/tutorials/android/android-track-app-launch-count/
I have a PreferenceActivity with a bunch of (Sub)PreferenceScreens. Each such (Sub)PreferenceScreen represents an account and has the account-username as its title.
PreferenceScreen root = mgr.createPreferenceScreen(this);
for (MyAccountClass account : myAccounts) {
final PreferenceScreen accScreen = mgr.createPreferenceScreen(this);
accScreen.setTitle(account.getUsername());
// add Preferences to the accScreen
// (for instance a "change username"-preference)
...
root.add(accScreen);
}
As the user enters sub-PreferenceScreen, and edits the account user-name, I want the outer PreferenceScreen to update it's PreferenceScreen-title for the account in question.
I've tried to add...
usernamePref.setOnPreferenceChangeListener(new OnPreferenceChangeListener() {
public boolean onPreferenceChange(Preference preference, Object newValue) {
accScreen.setTitle(newValue.toString());
return true;
}
});
...but the accScreen.setTitle does not seem to take effect on the outer PreferenceScreen. I've note that calling onContentChanged(); actually makes it work, but I realize that this is probably not the preferred way of doing it.
I suspect I should call postInvalidate() on some view somewhere, but I really can't figure out on what view and when to do it.
PreferenceScreen android:summary update ! may be experiening the same problem as me.
Any help appreciated.
I found a solution to this. I have a hierarchy like this, each of these is a PreferenceScreen:
main settings
-> users list
-> user1 settings
-> user2 settings
...
In the users list, title of the sub-screen is dependent on the user settings. Now when I create the user list, I store the list adapter to a variable in my PreferenceActivity.
PreferenceScreen usersListScreen = ...
userScreenListAdapter = (BaseAdapter)usersListScreen.getRootAdapter();
Now when userX-settings are edited, I set the titles in the usersListScreen and after that call:
userScreenListAdapter.notifyDataSetChanged();
which updates the list UI and the changes are visible.
notifyDataSetChanged() is right solution. But I want to add recursive iterator for all PreferenceScreens, as far as I got a problem to find real parent of a Preference. For complicated structure of Preferences I recommend this killer-code:
private void updateAll_PrefereneScreens(PreferenceGroup group) {
if (group instanceof PreferenceScreen) {
BaseAdapter adapter = (BaseAdapter) ((PreferenceScreen) group).getRootAdapter();
adapter.notifyDataSetChanged();
}
for (int i=0; i<group.getPreferenceCount(); i++) {
Preference pref = group.getPreference(i);
if (pref instanceof PreferenceGroup) {
updateAll_PrefereneScreens((PreferenceGroup) pref);
}
}
}
I call it after every setSummary() to ensure it works properly:
findPreference("KEY").setSummary(str);
updateAll_PrefereneScreens(getPreferenceScreen());
this might be a late answer but still... I'm on it right now :)
The way I achieved it, is that you can hook into the PreferenceFragmentCompat's onResume() method of its lifecycle and manually update the required field by resetting their values.
Note : in this example I also keep track of the index of the edited Preference, just to avoid reset every single ones.
// let's say you need to update title of the parent screen
// when back from sub-screen(s) edition
private int edited = -1;
// this gets called everytime you get back to the parent screen
#Override
public void onResume ()
{
super.onResume();
if ( edited != -1 )
{
PreferenceScreen root = getPreferenceScreen();
Preference preference = root.getPreference( edited );
if ( preference != null )
{
String updatedValue = getPreferenceManager()
.getSharedPreferences()
.getString( "your-preference-key", "your-default-value" );
preference.setTitle( updatedValue );
}
edited = -1;
}
}
// everytime you are about to navigate to a sub-screen
#Override
public boolean onPreferenceTreeClick ( Preference preference )
{
// beware to save it first
if ( preference instanceof MySubScreenPreference )
{
edited = preference.getOrder();
}
return super.onPreferenceTreeClick( preference );
}
As specified in the docs :
onResume
Called when the fragment is visible to the user and actively running. This is generally tied to Activity.onResume of the containing Activity's lifecycle.
Which is good is that it also works of course with FragmentManager's Transactions.
Hope this helps, happy coding ! :)
Experiencing this same problem, but onContentChanged() isn't working for me. My problem is with PreferenceScreens that are more than one level deep from the root.
To follow your example, if you first created an "Accounts" PreferenceScreen, and then added each of your individual account PreferenceScreen objects under that. Like this:
Root Screen
-> "Accounts" screen
-> "foo#example.com" screen
-> edit username
-> edit password
-> etc...
-> "bar#example.com" screen
-> "baz#example.com" screen
-> etc...
If a user edited their username and clicked save, calling PreferenceActivity.onContentChanged() seems to only affect direct descendants of the root PreferenceScreen. The third-generation screens' titles and summaries do not get redrawn, still reflecting old values.
Looking through the code for onContentChanged(), it looks like it just re-bind()s the root Screen to the ListActivity's ListView, although I don't think subsequent PreferenceScreens are ever bound to a ListView (are they?), so we can't manually re-bind anything...
The only workaround I can think of would be to create the sub-menus as isolated PreferenceActivitys instead of PreferenceScreens, so we can intentionally call onContentChanged() on our direct ancestor. But that's even more of a kludge than the current workaround. Any ideas?
PreferenceActivity#onContentChanged() will refresh the whole screen, occurring some flicker effect.
However you can achieve the same goal selectively on a given preference with the PreferenceActivity#onPreferenceTreeClick(...) method.
Note that this method is now deprecated : consider using fragments that seems to solve a lot of issues with custom preferences (I've not tested myself yet). There is a compatibility package for older SDK.
public void onSharedPreferenceChanged(SharedPreferences preferences, String key) {
Log.v(TAG, "onSharedPreferenceChanged(...," + key + ")");
if (key.equals("myPref")) {
onPreferenceTreeClick(getPreferenceScreen(), getPreferenceManager().findPreference("myPref"));
Log.v(TAG, "Do whatever else you need...");
}
//onContentChanged(); // this could be used but occurs screen flickering
}
I'm just putting
((BaseAdapter)getPreferenceScreen().getRootAdapter()).notifyDataSetChanged();
right after updating the summary of my parent preference item.
Source
// Import required classes (Win: CTRL+SHIFT+O & Mac: CMD+SHIFT+O)
public class YourCustomPreference extends PreferenceActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Load the preferences from an XML resource
addPreferencesFromResource(R.xml.preferences);
}
// some logic goes above, when you want to reset value and update
// EditTextPreference value. For convenience, I am going to wrap two
// different task in different methods
private void resetPreferenceValue() {
SharedPreferences sharedPref = PreferenceManager
.getDefaultSharedPreferences(this.getApplicationContext());
// Get preference in editor mode
SharedPreferences.Editor prefEditor = sharedPref.edit();
// set your default value here (could be empty as well)
prefEditor.putString("your_edit_text_pref_key", "DEFAULT-VALUE");
prefEditor.commit(); // finally save changes
// Now we have updated shared preference value, but in activity it
// still hold the old value
this.resetElementValue();
}
private void resetElementValue() {
// First get reference to edit-text view elements
EditTextPreference myPrefText = (EditTextPreference) super
.findPreference("your_edit_text_pref_key");
// Now, manually update it's value to default/empty
myPrefText.setText("DEFAULT-VALUE");
// Now, if you click on the item, you'll see the value you've just
// set here
}
}
This works for me, you have to grab the underlying dialog of your PreferenceScreen and set the title from there, really easy.
somePrefScreen.getDialog().setTitle("Whatever you want");