I'm in trouble.
I would like to call my MainActivity's method from my LifeCycleManager ( which is a LifecycleObserver ) and perform simple XML modifications. But that crash because I'm trying to access to an XML value and main_activity.xml isn't fully created yet
First this is my LifeCycleManager :
class LifeCycleManager(context: Context) : LifecycleObserver {
companion object {
var notif = NotificationManager()
var onForeground = false
var main = MainActivity()
// If we are on MainActivity return true , otherwise return false
fun isOnHomeController(context: Context?): Boolean {
if (context == null || context !is MainActivity)
return false
return true
}
var mContext = context
// WHEN THE APP BECOME ACTIVE
#OnLifecycleEvent(Lifecycle.Event.ON_START)
fun appComeBackFromBackground() {
onForeground = true
notif.cancelAllLocalNotifications(mContext)
if (isOnHomeController(mContext)) {
// Call the refresh dialog and print it
main.appComeBackFromBackground("refresh", null)
}
}
}
Here is my MainActivity method :
fun appComeBackFromBackground(status: String?, elementsadded: Int?)
{
Log.e("enterForeground", "App come back in Foreground")
if (status == null) return
when (status)
{
"refresh" ->{
val text = findViewById<TextView>(R.id.refreshtext)
text.setText("test")
}
else -> {
return
}
}
}
As you can see when my MainActivity Start , my Lifecycle get the event and go directly in my appComeBackFromBackground() method.
In it I'm calling my MainActivity method where I want to modify an element from activity_main.xml
That crash with theses :
java.lang.RuntimeException: Unable to start activity ComponentInfo{com.mControllers.MainActivity}: java.lang.RuntimeException: Failed to call observer method
Caused by: java.lang.NullPointerException: Attempt to invoke virtual method 'android.view.Window$Callback android.view.Window.getCallback()' on a null object reference
What can I do please? I really need to do it like this ( LifecycleObserver to MainActivity ). Thank's
EDIT :
I tried to call my MainActivity's method with delay like this :
Handler().postDelayed({
main.appComeBackFromBackground("refresh", null)
}, 5000)
And it crash too , even if MainActivity layout is loaded
I am writing this in Java,
(This is the method if you don't want to save the data in preferences or storage)
Application Class:
public class TestApplication extends Application implements ActivityLifecycleCallbacks {
private static String mStringToSet;
#Override
public void onCreate() {
super.onCreate();
//App is not from background, a new instance of your app is created
mStringToSet="new_instance";
registerActivityLifecycleCallbacks(this);
}
public static String getStringToSet() {
return mStringToSet;
}
#Override
public void onActivityDestroyed(Activity activity) {
if(activity instanceof MainActivity){
//This is only called when the activity is destroyed not the application, so if the context is your desired activity then set the string here
mStringToSet="refresh";
}
}
//Implement other life cycle methods**
In your activity:
public class MainActivity extends AppCompatActivity {
private final String TAG="ACTIVITY";
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
if("refresh".equals(TestApplication.getStringToSet())){
Toast.makeText(this,"From Background",Toast.LENGTH_SHORT).show();
(TextView)findViewById(R.id.YOUR_TEXT_VIEW).setText("refresh")
}else{
Log.d(TAG,TestApplication.getStringToSet());
Toast.makeText(this,"Not from background",Toast.LENGTH_SHORT).show();
(TextView)findViewById(R.id.YOUR_TEXT_VIEW).setText("new_instance")
}
}
}
//Implement other life cycle methods
}
Related
I'm trying to make something like a timed session when a user logs in in the app. I want to set up a timer which, when finished, change the activity with an intent, going back to the login page. I'm new to Android programming and I don't know how something like this should be implemented.
What I tried so far (and currently is working as intended, I just don't know if it's the correct thing to do, because it seems odd to have a function from another activity running in the background like this) is:
Countdowntimer is setted up in the login activity.
When the "login" button is pressed the timer starts.
OnTick() changes the activity with an intent.
OnFinish() is setted up in the login activity and, when it runs, it changes activity with an intent going back to the login page, even if the user is on a completely different activity.
As I said before, this works just fine, it just seems strange and I can't find anything that tells how this should work.
Using Intent Service Start your Timer & Add Event Bus Dependency to your project.
Event Bus
public class TimerIntentService extends IntentService {
public TimerIntentService() {
super("TimerIntentService");
}
public static void startTimer(Context context) {
Intent intent = new Intent(context, TimerIntentService.class);
context.startService(intent);
}
private CountDownTimer countdownTimer = new CountDownTimer(60000,1000) {
#Override
public void onTick(long millisUntilFinished) {
EventBus.getDefault().postSticky(new MessageEventToActivityEvent(TimerStatus.RUNNING));
}
#Override
public void onFinish() {
EventBus.getDefault().postSticky(new MessageEventToActivityEvent(TimerStatus.FINISHED));
}
};
#Override
protected void onHandleIntent(Intent intent) {
EventBus.getDefault().postSticky(new MessageEventToActivityEvent(TimerStatus.STARTED));
countdownTimer.start();
}
#Override
public void onDestroy() {
super.onDestroy();
try{
countdownTimer.cancel();
} catch (Exception e){
e.printStackTrace();
}
stopSelf();
}}
step 2:
in your first activity start intent service
TimerIntentService.startTimer(context);
step 3:
in your base activity or second activity add like the following
public class TimerBaseActivity extends AppCompatActivity {
#Subscribe(threadMode = ThreadMode.MAIN,sticky=true)
public void onMessageEvent(MessageEventToActivity event) {
if(event.timerStatus!=null){
if(event.timerStatus==TimerStatus.FINISHED){
// do your Stuff
}
if(event.timerStatus.equals(TimerStatus.RUNNING)){
Log.e("Timer State : ",""+event.timerStatus.name());
}
}
}
#Override
public void onStart() {
super.onStart();
EventBus.getDefault().register(this);
}
#Override
public void onStop() {
super.onStop();
EventBus.getDefault().unregister(this);
}}
With Koin DI & MVVM It is a very simple one.
Step 1: define your timer in your ViewModel
class TimerViewModel(application: Application) : AndroidViewModel(application) {
val isFinished = MutableLiveData<Boolean>().apply { value = false }
val timerTask: CountDownTimer = object : CountDownTimer(60000, 1000) {
override fun onFinish() {
Log.e("CountDownTimer","onFinish")
isFinished.postValue(true)
}
override fun onTick(millisUntilFinished: Long) {
Log.e("millisUntilFinished","$millisUntilFinished")
}
}}
step 2: Add your model as a singleton using Koin
val myModule = module {
single { TimerViewModel(androidApplication()) }}
Step 3: start your time in Your first activity
class MainActivity : AppCompatActivity() {
lateinit var activityMainBinding: ActivityMainBinding
val timerViewModel: TimerViewModel by inject()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
activityMainBinding = DataBindingUtil.setContentView(this, R.layout.activity_main)
activityMainBinding.viewModel = timerViewModel
activityMainBinding.lifecycleOwner = this
timerViewModel.timerTask.start()
startActivity(Intent(this, SecondActivity::class.java))
}
Step 4: Observe ViewModeldata in Second Activity
class SecondActivity : AppCompatActivity() {
val timerViewModel: TimerViewModel by inject()
lateinit var activitySecondBinding: ActivitySecondBinding
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
activitySecondBinding = DataBindingUtil.setContentView(this, R.layout.activity_second)
activitySecondBinding.viewModel = timerViewModel
activitySecondBinding.lifecycleOwner = this
timerViewModel.isFinished.observe(this, Observer {isTimerFinished->
if(isTimerFinished){
Toast.makeText(this#SecondActivity,"Timer Finished",Toast.LENGTH_LONG).show()
}
})
}
fun stopTimer(view:View){
timerViewModel.timerTask.cancel()
}
}
Note:
Problem Solved. This is one of the ways. If you don't want to use singleton object, then run your timer in IntentService. onFinish notify to the activity using EventBus. Event Bus will share your event with multiple activities.
I want to start an activity but strange enough I couldn't find a single place that tells where exactly should I do that.
Here is my code:
#Override public void onCreate() {
super.onCreate();
SoLoader.init(this, /* native exopackage */ false);
initializeFlipper(this); // Remove this line if you don't want Flipper enabled
Intent service = new Intent(getApplicationContext(), MyTaskService.class); Bundle bundle = new Bundle();
bundle.putString("foo", "bar"); service.putExtras(bundle);
getApplicationContext().startService(service);
}
You can use Android Lifecycle component to detect if app is going to background.
Please refer the below code :
import android.app.Application
import android.arch.lifecycle.ProcessLifecycleOwner
class SampleApp : Application() {
private val lifecycleListener: SampleLifecycleListener by lazy {
SampleLifecycleListener()
}
override fun onCreate() {
super.onCreate()
setupLifecycleListener()
}
private fun setupLifecycleListener() {
ProcessLifecycleOwner.get().lifecycle
.addObserver(lifecycleListener)
}
}
SampleApp is just an Android Application, declared in the manifest like:
<application
android:name=".SampleApp"/>
Code for lifecycleListner :
class SampleLifecycleListener : LifecycleObserver {
#Inject
var component: MyLifecycleInterestedComponent
#OnLifecycleEvent(Lifecycle.Event.ON_START)
fun onMoveToForeground() {
component.appReturnedFromBackground = true
}
#OnLifecycleEvent(Lifecycle.Event.ON_STOP)
fun onMoveToBackground() {
}
}
In onMoveToBackground() method you can write your code.
For more information refer to this link.
I'm a new to java and android. I was working on my own app but I'm having a problem in passing a method from Activity A to Activity B.
Here is what I did :
ActivityA has Demo() method.
public class ActivityA extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
protected void demo() {
// Do something
}
}
I created the below class to access the method of ActivityA to ActivityB:
public class External {
private ActivityA activitya;
private static External instance = null;
public External(ActivityA activitya) {
this.activitya = activitya;
}
static public External getInstance(ActivityA activitya) {
if (instance == null) {
instance = new MyntraExternal(activitya);
return instance;
} else {
return instance;
}
}
}
Now how can I proceed further? I'm having lots of problem in getting the method which is in ActivityA from ActivityB.
Please anybody help.
Edit :
ActivityB is my launcher class and I want some access from ActivityA's method in ActivityB. What to do ?
Since you are new to Android, I will tell you it's a bad practice call methods from Activity A to B or vice versa, you can pass parameters from one activity to another using intents and bundles and if you need to pass parameters from the second activity to the first you need to use the override method onActivityResults
Here are some usefull link about passing parameters from one activity to another:
https://www.dev2qa.com/passing-data-between-activities-android-tutorial/
In this link you can see a example of how things work.
Hope it helps.
--EDIT (if you need to call a function from B to A in case you want to change something in A upon creation this is the best and simplest way to do it):
In Activity B:
Intent intent = new Intent(this, ActivityA.class);
intent.putExtra("Work","doJump");
startActivity(intent);
In Activity A:
onCreate:
String extra = getIntent().getStringExtra("Work");
if(extra != null && extra.equals("doJump")){
jump();
}
make that method public and static and then access it using class name. e.g. In your 2nd activity, use ActivityB.demo()
Try using startActivityForResult
To start activity B from activity A
startActivityForResult(intent, SOME_CODE)
And to be called back on result you will need to add the following code the also in activity A
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
when(code){
SOME_CODE -> if (resultCode == Activity.RESULT_OK) doSomething()
}
}
To tell Activity A to call the method, in activity B you can say:
setResult(Activity.RESULT_OK)
finish()
After B is finished, onActivityResult in A will be executed
To go back to A without executing the "doSomething()" method,
setResult(Activity.RESULT_CANCELED)
finish()
Please try this way
public class ActivityA extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
public void demo() {
// Do something
}
}
public class ActivityB extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ActivityA activityA = new ActivityA(); // create object
activityA.demo(); //
}
}
this is My Situation:
I want to make two classes communicate with each other. So MainActivity is
making a new Object of SomeOtherstrangeClass.
public class MainActivity extends AppCompatActivity implements myEventListener {
private TextView txtHelloSet;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
txtHelloSet = (TextView) findViewById(R.id.txtHello);
SomeOtherStrangeClass someOtherStrangeClass = new SomeOtherStrangeClass();
}
#Override
public void someEvent(int e) {
Log.v("[Listener]", "SomeEvent triggered. Number: " + e);
txtHelloSet.setText("Event came in!");
}
}
#
public class SomeOtherStrangeClass {
public SomeOtherStrangeClass(){
EventThrower eventThrower = new EventThrower();
MainActivity mainActivity = new MainActivity();
eventThrower.addListener(mainActivity);
Log.v("[Listener]", "Throwing event");
eventThrower.someEvent(13);
}
}
interface myEventListener extends java.util.EventListener {
void someEvent(int e);
}
public class EventThrower {
private List<myEventListener> listeners = new ArrayList<myEventListener>();
public void addListener(myEventListener toAdd){
listeners.add(toAdd);
}
public void removeListener(myEventListener toRemove){
listeners.remove(toRemove);
}
public void someEvent(int e){
for(myEventListener el : listeners)
el.someEvent(e);
}
}
So this is how I'am doing it. But the Problem is, that when an event is thrown its like I'am still calling the Methode someEvent which is in MainActivity in SomeOtherStrangeClass. So I'am not able to modify any Objects in MainActivity. It's like they are not existing. I'am getting a NPE:
Caused by: java.lang.NullPointerException: Attempt to invoke virtual method 'void android.widget.TextView.setText(java.lang.CharSequence)' on a null object reference
Why this is so?
Is there any solution for that?
MainActivity mainActivity = new MainActivity();
You are doing something wrong here. It is not supposed to create Activity objects like this. You should rethink about your application logic.
You need to reference the instance of MainActivity that is currently in foreground in order to recognize any callbacks in it.
Try changing the constructor for SomeOtherStrangeClass like this:
public SomeOtherStrangeClass(myEventListener listener) {
EventThrower eventThrower = new EventThrower();
eventThrower.addListener(listener);
Log.v("[Listener]", "Throwing event");
eventThrower.someEvent(13);
}
Then, in onCreate() of your activity,
SomeOtherStrangeClass someOtherStrangeClass = new SomeOtherStrangeClass(this);
I have a fragment which is instantiated by an Activity. The issue that I'm having is that I have another class in the fragment which takes as a parameter an Activity context.
public class LocationQueries {
Activity context;
private static int REQUEST_CODE_RECOVER_PLAY_SERVICES = 200;
public LocationQueries(Activity context) {
this.context = context;
}
public boolean checkGooglePlayServices() {
int checkGooglePlayServices = GooglePlayServicesUtil
.isGooglePlayServicesAvailable(context);
if (checkGooglePlayServices != ConnectionResult.SUCCESS) {
/*
* google play services is missing or update is required
* return code could be
* SUCCESS,
* SERVICE_MISSING, SERVICE_VERSION_UPDATE_REQUIRED,
* SERVICE_DISABLED, SERVICE_INVALID.
*/
GooglePlayServicesUtil.getErrorDialog(checkGooglePlayServices,
context, REQUEST_CODE_RECOVER_PLAY_SERVICES).show();
return false;
}
return true;
}
}
I instantiate that class in my fragment like this
private LocationQueries locationQueries = new LocationQueries(getActivity());
but when I try to use locationQueries.checkGooglePlayServices();
I get Caused by: java.lang.NullPointerException: Attempt to invoke virtual method android.content.pm.PackageManager android.content.Context.getPackageManager() on a null object reference.
It looks like the LocationQueries(getActivity()) doesn't actually pass the activity context. How can I solve this ?
Edit: Everything is working if I do the same from an Activity -> LocationQueries(this);
It seems that you initiate LocationQueries at the wrong place. Indeed, I assume this:
private LocationQueries locationQueries = new LocationQueries(getActivity());
is called as a global variable in your Fragment class.
Instead you should keep your variable as global but set it into onCreate() or onResume(), as follows:
private class Frag extends Fragment {
private LocationQueries locationQueries;
...
#Override
public void onCreate(Bundle inState) {
locationQueries = new LocationQueries(getActivity());
}
}
Your Fragment appears not to be attached to an Activity.
I suspect you have instantiated your LocationQueries object before onAttach(Activity activity) has been called, or after onDetach() has been called on your Fragment.
In such a case, calling getActivity() will return null which is what you then pass to your LocationQueries object resulting in the NPE.