I am working on unity android project.
I have called the android side methods from unity like this
AndroidJavaObject aObj = new AndroidJavaObject("com.mypackage.UnityBridge",param1,param2);
aObj.Call("callme");
And on Android side
public class UnityBridge{
public UnityBridge(final String param1, final int param2) {
activity = UnityPlayer.currentActivity;
this.param1= param1;
this.param2= param2;
activity.runOnUiThread(new Runnable() {
#Override
public void run() {
// INITIALIZATION OF ANDROID CLASS CONSTRUCTOR HERE
}
});
}
public void callme() {
activity.runOnUiThread(new Runnable() {
#Override
public void run() {
if(obj!= null)
{
// ANDROID METHOD CALL HERE
}
}
});
}
This much is working perfectly.
If I want to call Activity specific methods like onPause(), onResume(), so there is a method in unity to do so.
void OnApplicationPause(bool pauseStatus) {
// HERE I CAN CALL activity specific **onPause()** and **onResume()** based on pauseStatus
}
Is there anything in unity from which I can give call to onNewIntent(Intent i) of Android. If not then how is it possible to call onNewIntent()
Please help to resolve this.
I wrote a post how this problem can be solved without overriding Unity activity (its about onActivityResult but the principle is the same): https://medium.com/#tarasleskiv/unity-android-plugins-and-onactivityresult-callback-abef4b6bbc87#.6d7sl8z81
Basically you create a custom empty activity and start it just to receive these callbacks and finish it immediately after that.
Check also this post about how to create activity for onNewIntent callback: https://blog.getsocial.im/android-unity-app-and-the-intent-issue/
Here is the example code of the intermediate activity yo have to create:
public class MyDeepLinkingActivity extends Activity
{
private static String TAG = "GetSocial GetSocialDeepLinkingActivity";
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
requestWindowFeature(Window.FEATURE_NO_TITLE);
Log.v(TAG, "Forward the intent");
// Do with intent what you need to
Log.v(TAG, "Returning to main activity");
//start main activity
Intent newIntent = new Intent(this, getMainActivityClass());
this.startActivity(newIntent);
finish();
}
private Class<?> getMainActivityClass() {
String packageName = this.getPackageName();
Intent launchIntent = this.getPackageManager().getLaunchIntentForPackage(packageName);
try {
return Class.forName(launchIntent.getComponent().getClassName());
} catch (Exception e) {
Log.e(TAG, "Unable to find Main Activity Class");
return null;
}
}
}
Looks like there is no built it way to get onNewIntent callback in Unity. However i can suggest to export google android project from unity and override this method in main unity activity
#Override
protected void onNewIntent(Intent intent) {
super.onNewIntent(intent);
//some code
}
Or you can create android plugin with your own activity which extends unity activity, but keep in mind that some plugins could stop working if you do this.
Related
I would like to inject an activity before the main activiy shows up. I added the following code in OnCreate method in the application class
[Application]
public class XYZ: Application
{
protected override void OnCreate(Bundle bundle)
{
base.OnCreate(bundle);
Intent intent = new Intent(this, typeof(SplashActivity));
intent.AddFlags(ActivityFlags.NewTask);
StartActivity(intent);
// rest code.. but I dont want it is excuded until my splashactivity
closed
}
}
I cannot find startActivityForResult method in the application class so after StartActivity, the code still is executed that I do not want.
How can make it stop there and wait until splashscreen is closed.
I am not inderested in doing Splasscreen as a mainlauncher. I just want to inject it with another reasons and there is no another way for me.
Note: its for xamarin but java code is also fine.
We can do it for example by interface:
// our app class
public class MyApplication extends Application implements IComplete {
#Override
public void onCreate() {
super.onCreate();
Intent intent = new Intent(this, Main2Activity.class);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(intent);
// rest code.. but I dont want it is excuded until my splashactivity
}
#Override
public void notify(boolean result) {
// do the rest code part here ...
}
}
// the interface
public interface IComplete {
void notify(boolean result);
}
// and the interface calling code snipped in the SplashActivity
#Override
public void onBackPressed() {
super.onBackPressed();
((MyApplication)getApplication()).notify(true);
}
hope helps you,
Good luck )
Problem: I need to run some code at every start before my app is ready to be used.
At first, I tried doing it in a dedicated activity.
AndroidManifest.xml
<activity android:name=".MainActivity" />
<activity android:name=".StarterActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
AppLoader.java
public class AppLoader {
private static Object someInstance;
public static void load(Runnable onCompleteCallback) {
try {
someInstance = new Object();
//potentially long operation to initialize the app
Thread.sleep(5000);
onCompleteCallback.run();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
public static void checkInitialized() {
if (someInstance == null) {
throw new RuntimeException("Not initialized");
}
}
}
StarterActivity.java
public class StarterActivity extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
AppLoader.load(() -> {
MainActivity.start(this);
finish();
});
}
}
MainActivity.java
public class MainActivity extends AppCompatActivity {
public static void start(Context context) {
Intent starter = new Intent(context, MainActivity.class);
context.startActivity(starter);
}
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
AppLoader.checkInitialized();
}
}
This works fine if the app is cold started via the launcher icon but crashes in all other cases. Simple way to reproduce the issue:
Go to developer settings on your device and set "Background process limit" to "No background process"
Open the app
Open some other app
Open the app again. Result: it crashes.
Here's an article describing a similar problem: Android process death — and the (big) implications for your app
Possible solutions:
Lazy loading/reactive approach. I try to use it as much as possible but there is always some code I need to run in a blocking way before user can interact with the app so this is not enough.
Putting all of that code in App.onCreate(). This would probably work for small apps but I've seen large apps that take 5-10 seconds to initialize, and I doubt they use onCreate() for that. Possible downsides: ANR and/or excessive startup time in Android Vitals?
Checking if the app is initialized in a BaseActivity, but that would require either blocking onCreate or managing lifecycle callbacks manually which doesn't sound like a good idea.
So, what's the proper way to run some code every time the app is launched?
Note: Normally StarterActivity would be a splash screen, AppLoader would be injected, etc, but I left that out for simplicity.
AndroidManifest.xml
<application
android:name=".AppLoader"
AppLoader.java
public class AppLoader extends Application {
private static Object someInstance;
#Override
public void onCreate() {
super.onCreate();
// DO YOUR STUFF
}
}
Update
- Use Handler with splash screen.
public class StarterActivity extends AppCompatActivity {
private Handler handler;
private Runnable myStuffRunnable;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
handler = new Handler();
myStuffRunnable = new Runnable(){
public void run(){
// DO MY STUFF
MainActivity.start(this);
}
};
}
#Override
protected void onPause() {
handler.removeCallbacks(myStuffRunnable);
super.onPause();
}
#Override
protected void onResume() {
super.onResume();
handler.post(myStuffRunnable);
}
#Override
protected void onDestroy() {
handler.removeCallbacks(myStuffRunnable);
super.onDestroy();
}
}
Your app is throwing the RuntimeException you set in AppLoader.checkInitialized() method, because your someInstance object is losing it's state when the app goes to background and gets killed by the system ('cause you have set your device to hold zero background threads). So, when you try to reopen the app, the system launches MainActivity directly (and not StarterActivity) because it is trying to restore it's previous state. But variables are not restored, not even static variables.
So, if you need the Object someInstance on your MainActivity, you should integrate it's instantiation into MainActivitie's lifecycle, overriding methods like onSavedInstanceState, onRestoreInstanceState, etc, to properly handle and reaload this object if your app gets killed by the system.
Take a look on this https://developer.android.com/guide/components/activities/activity-lifecycle
If anyone's interested, I ended up just redirecting the user to StarterActivity if needed to make sure the necessary code is executed at every start.
public abstract class BaseActivity extends AppCompatActivity {
private boolean isCreated;
#Override
protected final void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (!appLoader.isLoaded()) {
StarterActivity.start(this);
finish();
return;
}
onCreateActivity(savedInstanceState);
isCreated = true;
}
protected void onCreateActivity(#Nullable Bundle savedInstanceState) {
}
#Override
protected final void onDestroy() {
super.onDestroy();
if (isCreated) {
onDestroyActivity();
}
}
protected void onDestroyActivity() {
}
}
All activities extend BaseActivity (except StarterActivity) and override onCreateActivity/onDestroyActivity instead of onCreate/onDestroy.
I would like to get voice recognition using just one method.
In order to do that i've created 3 classes
the main class
public class Index extends Activity {
private Button boton;
private EditText texto;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_index);
boton = (Button)findViewById(R.id.boton);
texto = (EditText) findViewById(R.id.texto);
boton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View arg0) {
texto.setText(IVRecognition.getInstancia().getComando(Index.this));
}
});
}
}
the intermediate
public class IVRecognition {
//*******************singleton********************
private static IVRecognition instancia;
private IVRecognition (){}
public static IVRecognition getInstancia(){
if (instancia==null) instancia = new IVRecognition();
return instancia;
}
//************************************************
public static String resultado = null;
public String getComando(Context content){
Intent intent = new Intent(content, VRecognition.class);
content.startActivity(intent);
//pause here untill VRecognition.onActivityResult is executed
return resultado;
}
}
and the recognition one
public class VRecognition extends Activity {
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
startRecognition();
}
public void startRecognition (){
Intent intent = new Intent(RecognizerIntent.ACTION_RECOGNIZE_SPEECH);
intent.putExtra(RecognizerIntent.EXTRA_LANGUAGE_MODEL,RecognizerIntent.LANGUAGE_MODEL_FREE_FORM);
intent.putExtra(RecognizerIntent.EXTRA_CALLING_PACKAGE,this.getPackageName());
startActivityForResult(intent, 1 /*VOICE_RECOGNITION_REQUEST_CODE*/);
}
#Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if(requestCode == 1 /*VOICE_RECOGNITION_REQUEST_CODE*/ && resultCode == RESULT_OK){
ArrayList<String> result = data.getStringArrayListExtra(RecognizerIntent.EXTRA_RESULTS);
IVRecognition.getInstancia().resultado = result.get(0);
}
this.finish();
}
}
The problem is that when I call VRecognition activity using content.startActivity(intent); the execution of the aplication keeps on going, so the variable called resultado has null value until onActivityResult is executed, which results in a null return value.
Hope you can help me. Cheers
Ian's answer is good. But from your comment, I'd recommend using an IntentService and the BroadcastManager. That way you don't need the intermediate activity. You call the startService(intent) from any activity that wants the VR result (and implements BroadcastReceiver). Then the IntentService calls startActivityForResult(intent,1) and Broadcasts the result.
More info:
http://developer.android.com/training/run-background-service/index.html
It sounds like you want to pause execution until voice recognition is complete. You may want to rethink this; you're calling getComando() from your UI thread, so your application UI will be frozen until recognition is complete. In the (probably quite likely) event that recognition takes more than five seconds, the system will pop up an Application Not Responding dialog. (Also, since you're implementing getComando() by starting another activity within your process, blocking the UI thread in getComando() would prevent recognition from ever running.)
The right way to do this is to use a completion callback. For instance, you could create an IVRecognitionListener interface:
public interface IVRecognitionListener {
public void onRecognitionComplete(String resultado);
}
and pass an instance of that to getComando(). Then instead of just setting IVRecognition.resultado in onActivityResult(), you could call onRecognitionComplete() to notify the caller of the result.
I have a main activity that hold the tabs and each tab start a new activity. May I know how can I change the tab title from the new activity? Thanks.
Although CommonsWare has pointed out that having Activities as Tab content is deprecated, if you still want to do it then one possibility is to use a nested BroadcastReceiver and have the content Activity send a broadcast Intent. I'm not sure if it will work but I would try something like the following...
public class MainActivity extends Activity {
bool tabMonitorIsRegistered = false;
TabMonitor tabMonitor = null;
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Other code
tabMonitor = new TabMonitor();
}
#Override
protected void onResume() {
super.onResume();
if (!tabMonitorIsRegistered) {
registerReceiver(tabMonitor, new IntentFilter(Intent.com.mydomain.myapp.ACTION_TAB_CHANGE));
tabMonitorIsRegistered = true;
}
}
#Override
protected void onPause() {
super.onPause();
if (tabMonitorIsRegistered) {
unregisterReceiver(tabMonitor);
tabMonitorIsRegistered = false;
}
}
// Nested BroadcastReceiver
private class TabMonitor extends BroadcastReceiver {
#Override
public void onReceive(Context arg0, Intent arg1) {
// Process the Intent here to change the tab title
}
}
}
At this point it occurs to me that each 'content' Activity will need to tell the MainActivity (via the Intent it sends) 'who' it is. To do this, I would use an Intent extra when adding the tab content Activities identifying each as 'tab1', tab2' etc. When the 'content' Activities start, e.g., in onCreate(), they can store this string and use it in the Intent they send as the broadcast to the MainActivity.
I've got one question regarding the intent action ACTION_CALL.
What is the correct way of getting back to the own application/activity after the user ends the call?
Intent intent = new Intent(Intent.ACTION_CALL);
intent.setData(Uri.parse("tel:" +m1));
startActivity(intent);
I have made the call using the above code. Please provide me with a solution to call my own activity after the call action.
unfortunately some phones have settings to force going into for example the Call Log after a call...
However, you can run a loop after your startActivity to check TelephonyManager.getCallState, and when it's again TelephonyManager.CALL_STATE_IDLE, you can restart your own Activity
be sure to add some sleep to the loop
AFter the endofcall.......it just had to come back to the activity..!! you can handle that one onRestart();
I run with the same problem, and ended up solving it like this:
Make a Callback interface with single (or multiple if you want)
methods
Implement that interface in your activity
Make a reference to that interface inside PhoneStateListener class
Call a method within that interface when the call ended
public class CallTracker extends PhoneStateListener implements Runnable {
private Context mActivity;
private Callback mCallback;
public interface Callback {
void onCallEnded();
}
public CallTracker(Activity activity) {
super();
mActivity = activity;
if (mActivity instanceof Callback) {
mCallback = (Callback) mActivity;
}
}
#Override public void onCallStateChanged(int state, String incomingNumber) {
if (state == TelephonyManager.CALL_STATE_IDLE) {
mCallback.onCallEnded();
}
}
public class CallerActivity extends AppCompatActivity implements
CallTracker.Callback {
#Override public void onCallEnded() {
Toast.makeText(this, "Call ended!", Toast.LENGTH_SHORT).show();
}
}