Java error api level method - java

I get an error
java.lang.NoSuchMethodError: android.app.Activity.isDestroyed
It has something to do with only running on devices which are api 17 or higher, is there anyway around this?
private WeakReference<Activity> mActivityRef;
#Override
public void onFinish() {
Activity activity = mActivityRef.get();
// if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
//do your thing!
if (activity != null && !activity.isFinishing() && !activity.isDestroyed()) {
activity.startActivity(new Intent(activity, ListOfAlarms.class));
activity.finish();
}
mStarted = false;
// }
// Intent goBack = new Intent(CountDownAct.this, ListOfAlarms.class);
// startActivity(goBack);
// finish();
}

This API was added only in API Level 17 Check this!
To avoid the crash, you can verify with the following code
if(Build.VERSION.SDK_INT >= VERSION_CODES.JELLY_BEAN_MR1){
//do your thing!
}
To get it working on lower API levels, you can create your own BaseActivity.java and add code like this
public class BaseActivity extends Activity {
private boolean mDestroyed;
public boolean isDestroyed(){
return mDestroyed;
}
#Override
protected void onDestroy() {
mDestroyed = true;
super.onDestroy();
}
}
Now, make all your activities extend this BaseActivity as follows
public class MainActivity extends BaseActivity {
...
}
Hope this helps! Please accept this answer if it worked for you :)
EDIT (After OP added code snippets)
First create a BaseActivity.java file as I have shown above.
Then, make all your activities extend BaseActivity instead of Activity.
Now, change
private WeakReference<Activity> mActivityRef;
to
private WeakReference<BaseActivity> mActivityRef;
And change
Activity activity = mActivityRef.get();
to
BaseActivity activity = mActivityRef.get();

Related

How to send event from MainActivity onCreate to React Native?

I'm working on an alarm clock and I can't figure out how to sendEvent to React Native from MainActivity. This is what I managed to do so far:
#Override
protected void onCreate(Bundle savedInstanceState) {
mInitialProps = new Bundle();
final Bundle bundle = mActivity.getIntent().getExtras();
ReactInstanceManager mReactInstanceManager = getReactNativeHost().getReactInstanceManager();
ReactApplicationContext context = (ReactApplicationContext) mReactInstanceManager.getCurrentReactContext();
if (context == null) {
mReactInstanceManager.addReactInstanceEventListener(new ReactInstanceManager.ReactInstanceEventListener() {
public void onReactContextInitialized(ReactContext context) {
if (bundle != null && bundle.containsKey("sendAlarm")) {
if (bundle.getString("sendAlarm").equals("sendAlarmOn")) {
LauncherModule.startAlarm(mActivity); // works
LauncherModule.sendAlarmEvent(); // doesn't work. Should run after alarm manager starts app which previously had been killed
}
}
}
});
} else {
if (bundle != null && bundle.containsKey("sendAlarm")) {
if (bundle.getString("sendAlarm").equals("sendAlarmOn")) {
LauncherModule.startAlarm(mActivity); // works
LauncherModule.sendAlarmEvent(); // works and sends event only when app was left open
}
}
}
super.onCreate(savedInstanceState);
}
The code works only If app is left open and alarm manager restarts app itself. If I close the app and alarm manager starts it then it seems that only startAlarm function (it has sound effect) is beeing triggered..
No matter what I do whether I put sendEvent function inside Mainactivity or elsewhere (e.g. external module) it simply won't send event if I close the app. I also tried getReactInstanceManager().getCurrentReactContext() combined with while from this question Send data from Android activity to React Native to no avail.
Also tried to create bolean beeing set to true onCreate and then send event onStart or onRestart. Also to no avail.
Any suggestions?
EDIT: Here is how sendEvent function looks like:
public final void sendEvent(String eventName, boolean isAlarmOn) {
getReactInstanceManager().getCurrentReactContext()
.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class)
.emit(eventName, isAlarmOn);
}
SOLUTION
Well I think that the answer is not to use sendEvent method onCreate because (I might be wrong) listener seems to be initialized after the event had been sent. So nothing is going to listen to this event.
It seems to work pretty well inside onStart, onRestart, onPause though.
What can we do? React Native provides ReactActivityDelegate with initial props. And it does the job!
ReactActivityDelegate in MainActivity should look as below:
public class ActivityDelegate extends ReactActivityDelegate {
private Bundle mInitialProps = null;
private final #Nullable Activity mActivity;
public ActivityDelegate(Activity activity, String mainComponentName) {
super(activity, mainComponentName);
this.mActivity = activity;
}
#Override
protected void onCreate(Bundle savedInstanceState) {
mInitialProps = new Bundle();
final Bundle bundle = mActivity.getIntent().getExtras();
if (bundle != null && bundle.containsKey("sendAlarm")) {
if (bundle.getString("sendAlarm").equals("sendAlarmOn")) {
mInitialProps.putBoolean("alarmOn", true);
}
}
super.onCreate(savedInstanceState);
}
#Override
protected Bundle getLaunchOptions() {
return mInitialProps;
}
};
#Override
protected ReactActivityDelegate createReactActivityDelegate() {
return new ActivityDelegate(this, getMainComponentName());
}
Then in your main app component (usually index.android.js) call your propTypes and use them to run your code:
static propTypes = {
alarmOn: PropTypes.boolean
}
componentDidMount() {
if (this.props.alarmOn === true) {
// your code
}
}
Voila!
You can find full example here: https://github.com/vasyl91/react-native-android-alarms

Android - Close first activity

I have created an android application which has two activities, one of them is MainActivity and the other one is Article activity
In this application you can open unlimited number of activities. So it's like an article app, inside an article there are also other articles so you can follow unlimited number of articles. Every article opens a new Article activity.
Now what I want to do is this: When the 10th Article is opened I want to close the first Article activity (not MainActivity).
I have also read this question, but it works only if you have different activities (i.e. ActivityA, ActivityB, ActivityC...).
Is there any way I can do this?
When opening 10th article, do this:
Intent intent = new Intent(this,Your_Article.class);
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
startActivity(intent);
or if you only want to close a particular activity, get the instance of that activity and close it using finish().
this is a hack only to get it working but you may want to try fragment from your description;
Create a new class called ActivityHandler.java
public class ActivityHandler {
private static ActivityHandler uniqueInstance;
private static List<Activity> mListActivity;
private static int SIZE_LIMIT = 10;
public static ActivityHandler getInstance() {
if (uniqueInstance == null) {
synchronized (ActivityHandler.class) {
if (uniqueInstance == null) {
uniqueInstance = new ActivityHandler();
}
}
}
return uniqueInstance;
}
private Activityhandler() {
if (mListActivity == null ) {
mListActivity = new ArrayList();
}
}
public static void add (Activity activity){
mListActivity.add(activity);
if (mListActivity.size() > 10){
Activity firstActivity = mListActivity.remove(0);
firstActivity.finish();
}
}
}
In your ArticleActivity's OnCreateMethod:
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.article_activity);
ActivityHandler.getInstance().add(this);
}

Android-Libgdx, Calling Another Activity after the Game starts on Button click

I faced a major problem when I need to call another activity when the button is clicked after the Game is started. The Game is called via initiate(game, ) method from AndroidApplication interface.
In normal Activity, I can easily call the another Activity but it seems to be difficult to call another Activity from Libgdx class that implements AndroidApplication.
Could anyone suggest a proper method to call the Activity from Libgdx class that implements AndroidApplication interface?
I tried to do this for a week but it seems that my method is totally wrong..
Thanks in advance.
Define a callback interface in you LibGdx class, and use it to notify your AndroidLauncher to start the new activity.
For example in your LibGdx game class:
// Your Game class in the core package
public class MyGame extends Game {
// Define an interface for your various callbacks to the android launcher
public interface MyGameCallback {
public void onStartActivityA();
public void onStartActivityB();
public void onStartSomeActivity(int someParameter, String someOtherParameter);
}
// Local variable to hold the callback implementation
private MyGameCallback myGameCallback;
// ** Additional **
// Setter for the callback
public void setMyGameCallback(MyGameCallback callback) {
myGameCallback = callback;
}
#Override
public void create () {
...
}
...
private void someMethod() {
...
// check the calling class has actually implemented MyGameCallback
if (myGameCallback != null) {
// initiate which ever callback method you need.
if (someCondition) {
myGameCallback.onStartActivityA();
} else if (someOtherCondition) {
myGameCallback.onStartActivityB();
} else {
myGameCallback.onStartSomeActivity(someInteger, someString);
}
} else {
Log.e("MyGame", "To use this class you must implement MyGameCallback!")
}
}
}
Then ensure your AndroidLauncher implements the required interface:
// Your AndroidLauncher
public class AndroidLauncher extends AndroidApplication implements MyGame.MyGameCallback {
#Override
protected void onCreate (Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
AndroidApplicationConfiguration config = new AndroidApplicationConfiguration();
// create an instance of MyGame, and set the callback
MyGame myGame = new MyGame;
// Since AndroidLauncher implements MyGame.MyGameCallback, we can just pass 'this' to the callback setter.
myGame.setMyGameCallback(this);
initialize(myGame, config);
}
#Override
public void onStartActivityA() {
Intent intent = new Intent(this, ActivityA.class);
startActivity(intent);
}
#Override
public void onStartActivityB(){
Intent intent = new Intent(this, ActivityB.class);
startActivity(intent);
}
#Override
public void onStartSomeActivity(int someParameter, String someOtherParameter){
Intent intent = new Intent(this, ActivityA.class);
// do whatever you want with the supplied parameters.
if (someParameter == 42) {
intent.putExtra(MY_EXTRA, someOtherParameter);
}
startActivity(intent);
}
}

How to prevent a Fragment from being added when an Activity is closing

I am creating an Activity which communicates with a Service to download some data from internet via POST method. To do this, I use Messenger. Here is my code to make it clearer for you:
My onCreated() method:
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_comments);
CommentsHandler commentsHandler = new CommentsHandler(this, savedInstanceState);
Messenger messenger = new Messenger(commentsHandler);
Intent serviceIntent = new Intent(this, WindowService.class);
serviceIntent.putExtra("messenger", messenger);
serviceIntent.putExtra("state", 888);
serviceIntent.putExtra("number", getIntent().getStringExtra("number"));
startService(serviceIntent);
}
The code in my Service's thread to post the result data to the Activity via the Messenger object:
/** ... **/
Messenger messenger = intent.getParcelableExtra("messenger");
/** ... **/
Message resultMsg = this.obtainMessage();
resultMsg.obj = jParser.getArrayList(); //This is an ArrayList of my downloaded data.
messenger.send(resultMsg);
The code in the Activity to handle the Message from the Service:
public static class CommentsHandler extends Handler {
Bundle mSavedInstanceState;
ActionBarActivity activity;
public CommentsHandler(Activity a, Bundle savedInstanceState) {
activity = (ActionBarActivity) a;
mSavedInstanceState = savedInstanceState;
}
#Override
public void handleMessage(Message msg) {
comments = (ArrayList<HashMap<String, String>>) msg.obj;
if (mSavedInstanceState == null && msg.arg1 != 793) {
activity.getSupportFragmentManager().beginTransaction()
.add(R.id.container, new CommentsFragment()).commit();
} else if (msg.arg1 == 793) { //793 is my preferred code to determine
//if the internet connection could not be
//established when the Service was trying
//to download the data.
activity.finish();
}
}
}
The problem is: if I open the Activity and close it before the data is downloaded, this code .add(R.id.container, new CommentsFragment()).commit(); gives me the error Can not perform this action after onSaveInstanceState, because this code only gets executed after the data in my Service is processed and sent via the Messenger object, but at that time the Activity is already closed by the user so the Fragment cannot be added. How to solve this issue? How to check if the Activity is not closed/being closed before adding the Fragment? Or, better, how to stop the thread in which that code is running on Activity's onDestroy() method so it doesn't get executed if the Activity is closed? Thanks in advance!
In your activity, you should create a boolean to check if the activity is visible or not:
public ActionBarActivity extends Activity {
private boolean isActivityVisible = false;
#Override
protected void onResume(){
isActivityVisible = true;
}
#Override
protected void onPause(){
isActivityVisible = false;
}
public boolean isVisible(){
return this.isActivityVisible;
}
}
And then you modify your Handler class definition:
public static class CommentsHandler extends Handler {
Bundle mSavedInstanceState;
ActionBarActivity activity;
public CommentsHandler(Activity a, Bundle savedInstanceState) {
activity = (ActionBarActivity) a;
mSavedInstanceState = savedInstanceState;
}
#Override
public void handleMessage(Message msg) {
// here you check if your activity is no longer visible and then break up
if(activity == null || !activity.isVisible())
return;
comments = (ArrayList<HashMap<String, String>>) msg.obj;
if (mSavedInstanceState == null && msg.arg1 != 793) {
activity.getSupportFragmentManager().beginTransaction()
.add(R.id.container, new CommentsFragment()).commit();
} else if (msg.arg1 == 793) { //793 is my preferred code to determine
//if the internet connection could not be
//established when the Service was trying
//to download the data.
activity.finish();
}
}
}
The smallest change would be to have a boolean field in the Activity, setting it to true in onResume() and to false in onPause(), and check its value in handleMessage() (i.e. ignore the message if the flag is currently false).
Another option, instead of using Messenger and handleMessage(), do this with a BroadcastReceiver. Register the receiver in onResume() and unregister it in onPause(). That way the broadcast from the service will be simply ignored.
Both solutions are basically the same, anyway, but broadcasts are somewhat "higher level".
This assumes that you're not interested in the Service's result if the activity is paused. If you are (for example, if you switch out of the application and back in, and you need to display the update) then you should put the received data in a field and process it on the following onResume().
Your way of doing this is different than how I would handle it but using what you have I would make these adjustments:
public static class CommentsHandler extends Handler {
Bundle mSavedInstanceState;
ActionBarActivity activity;
public CommentsHandler(Activity a, Bundle savedInstanceState) {
activity = (ActionBarActivity) a;
mSavedInstanceState = savedInstanceState;
}
public void setActivity(Activity a){
activity = a;
}
#Override
public void handleMessage(Message msg) {
if(activity == null){
return;
}
comments = (ArrayList<HashMap<String, String>>) msg.obj;
if (mSavedInstanceState == null && msg.arg1 != 793) {
activity.getSupportFragmentManager().beginTransaction()
.add(R.id.container, new CommentsFragment()).commit();
} else if (msg.arg1 == 793) { //793 is my preferred code to determine
//if the internet connection could not be
//established when the Service was trying
//to download the data.
activity.finish();
}
}
}
Then I would use your activities onPause()/onResume() methods to like this:
#Override
protected void onPause() {
super.onPause();
commentsHandler.setActivity(null);
}
#Override
protected void onResume() {
super.onResume();
commentsHandler.setActivity(this);
}

Mock a method to get code coverage in Android

I am writing an Android OpenGL ES 2.0 application in a test driven fashion. Here goes my code.
public class MainActivity extends Activity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
boolean checkCompatability = checkForDeviceCompatablity();
if(checkCompatability)
{
Toast.makeText(this, "Your device is opengl compatible", Toast.LENGTH_LONG).show();
}
else
{
Toast.makeText(this, "Your device does not support OpenGL", Toast.LENGTH_LONG).show();
}
}
public boolean checkForDeviceCompatablity() {
ActivityManager manager = (ActivityManager) getSystemService(Context.ACTIVITY_SERVICE);
ConfigurationInfo info = manager.getDeviceConfigurationInfo();
boolean result = info.reqGlEsVersion >= 0x20000
|| (Build.VERSION.SDK_INT
>= Build.VERSION_CODES.ICE_CREAM_SANDWICH_MR1);
return result;
}
}
And this is my test
public class MainActivityTest extends ActivityInstrumentationTestCase2<MainActivity> {
private MainActivity mActivity;
public MainActivityTest(Class<MainActivity> activityClass) {
super(activityClass);
}
public MainActivityTest(){
super(MainActivity.class);
}
//The below method would always return me a true as I test it in a real device.
public void testIntegrationCheckForConfiguration() {
mActivity = getActivity();
mActivity.startActivity(mActivity.getIntent());
boolean resultConfiguration = mActivity.checkForDeviceCompatablity();
assertNotNull(resultConfiguration);
}
//I need to mock the above method into something like the below method so that I can get coverage for the else block.
public void testCheckForConfigurationWhenDeviceIsOfInCorrectConfiguration(){
mActivity = new MainActivity(){
#Override
public boolean checkForDeviceCompatablity() {
return false;
}
};
mActivity.startActivity(mActivity.getIntent());
boolean compatability = mActivity.checkForDeviceCompatablity();
assertTrue(compatability);
}
}
I know I'm doing it incorrectly. But I need a way to mock the checkForDeviceCompatability to make it return false. When I run the test I get a NullPointerException at the startActivity call. How do I mock the function checkForDeviceCompatability and get the code cover the else part in MainActivity class?
Also is this possible with any kind of mocking framework?
Not that I have much experience with this sort of thing but you could try Mockito.
Noticed it in the examples when integrating Dagger dependency injection to my app.

Categories

Resources