so Im running a java plugin that extends UnityPlayerActivity. Im successfully overwriting the onCreate function. Only problem is when I try to get the intent data coming in, its null. The data Im looking for is the url that Triggered the Intent.
package com.company.androidlink;
import java.net.URL;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
import android.util.Log;
import com.unity3d.player.*;
public class Main extends UnityPlayerActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Intent intent = getIntent();
uri = intent.getData();
url = new URL(uri.getScheme(), uri.getHost(), uri.getPath());
}
}
Manifest
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.company.androidlink"
android:versionCode="1"
android:versionName="1.0" >
<uses-sdk
android:minSdkVersion="9"
android:targetSdkVersion="21" />
<application
android:allowBackup="true"
android:icon="#drawable/ic_launcher"
android:label="#string/app_name"
android:theme="#style/AppTheme" >
<activity
android:name=".Main"
android:label="#string/app_name" >
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
I developed a plugin some months ago to send a text to a Unity3D app. The method, which is in the root activity (equivalent to your "Main") is the following:
public static String getExtraText() {
String extraText = "";
// Store extra parameter for later.
Intent intent = UnityPlayer.currentActivity.getIntent();
if (intent != null) {
String action = intent.getAction();
String type = intent.getType();
if (action.equals(Intent.ACTION_VIEW) && type != null) {
if (type.equals("text/plain")) {
extraText = intent.getStringExtra(Intent.EXTRA_TEXT);
DebugBridge.log_d("Extra Text: " + extraText);
} else {
DebugBridge.toast("Unknown MIME type");
}
}
}
return extraText;
}
I did not get the text at startup, just invoke the "getExtraText" from Unity app when needed (at Start usually).
This is the way I send data from another android native test app to unity:
boolean sendMessageToApp(String message, String appName) {
ComponentName name = findNativeApp(appName);
if (name != null) {
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setComponent(name);
intent.addCategory(Intent.CATEGORY_LAUNCHER);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
intent.setType("text/plain");
intent.putExtra(Intent.EXTRA_TEXT, message);
startActivity(intent);
return true;
}
return false;
}
Related
I need help or guidance in resolving the issue that I have regarding React Native interacting with other native applications via intents. I know that React Native supports deeplinking out of the box but does not cater for intents, which means that one needs to create an android native module (https://reactnative.dev/docs/native-modules-android). I created a native module that calls 3rd party applications via intents and passing data using startActivityForResults and created onActivityResult that is supposed to handle data returned from 3rd party applications after closing. However, the onActivityResult executes prematurely before external applications open.
Starting the activity using startActivityForResults:
#ReactMethod
public void launchApp(String stringArgument, ReadableMap args, Promise promise) throws JSONException{
try {
final JSONObject options = convertMapToJson(args);
Bundle extras = new Bundle();;
int LAUNCH_REQUEST = 0;
if (options.has("extras")) {
extras = createExtras(options.getJSONArray("extras"));
Log.d(TAG,"Extras found");
Log.d(TAG, options.getString("extras"));
} else {
extras = new Bundle();
Log.d(TAG,"No extras");
}
if (options.has("launchRequestCode")) {
LAUNCH_REQUEST = options.getInt("launchRequestCode");
}
Intent packageIntent = this.reactContext.getPackageManager().getLaunchIntentForPackage(stringArgument);
if(packageIntent != null){
packageIntent.putExtras(extras);
//callback.invoke("Starting activity for: "+stringArgument);
Activity activity = getReactApplicationContext().getCurrentActivity();
//this.reactContext.startActivityForResult(packageIntent, LAUNCH_REQUEST, extras);
activity.startActivityForResult(packageIntent, LAUNCH_REQUEST);
return;
//mPromise.put(LAUNCH_REQUEST, promise);
}
else{
Log.d(TAG, stringArgument+" package not found");
//callback.invoke("Package not found: "+stringArgument);
}
} catch (JSONException e) {
//TODO: handle exception
Log.d(TAG, e.toString());
}
// TODO: Implement some actually useful functionality
}
Expecting the data back using onActivityResults
ActivityEventListener mActivityEventListener = new ActivityEventListener(){
#Override
public void onActivityResult(Activity activity, int requestCode, int resultCode, Intent data) {
//super.onActivityResult(requestCode, resCode, data);
Log.d(TAG,"On activity result");
if (mPromise != null && resultCode == activity.RESULT_OK) {
WritableMap result = new WritableNativeMap();
result.putInt("resultCode", resultCode);
result.putMap("data", Arguments.makeNativeMap(data.getExtras()));
mPromise.resolve(result);
}
else{
Log.d(TAG,"Promise and intent data are empty");
//mPromise.reject("Unable to get promise or intent data is empty");
}
if(resultCode == activity.RESULT_CANCELED ){
Log.d(TAG,"Result cancelled or no result or crashed with code");
}
}
#Override
public void onNewIntent(Intent intent){
Log.d(TAG,"New Intent");
}
};
Android Manifest file:
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.navapp">
<uses-permission android:name="android.permission.INTERNET" />
<application
android:name=".MainApplication"
android:label="#string/app_name"
android:icon="#mipmap/ic_launcher"
android:roundIcon="#mipmap/ic_launcher_round"
android:allowBackup="false"
android:theme="#style/AppTheme">
<activity
android:name=".MainActivity"
android:label="#string/app_name"
android:configChanges="keyboard|keyboardHidden|orientation|screenSize|uiMode"
android:launchMode="singleTask"
android:windowSoftInputMode="adjustResize">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity android:name="com.facebook.react.devsupport.DevSettingsActivity" />
</application>
</manifest>
By using Android Studio Logcat to debug, I found out that the onActivityResults executes immediately before the external app opens up.
Logcat screenshot
Thanks to this explanation here, I was able to drill down to what was causing the issue. In my React Native application, I used the package name of the external application instead of the action name to open it.
Manifest File of the external app:
<application
android:allowBackup="true"
android:icon="#mipmap/ic_launcher"
android:label="#string/app_name"
android:roundIcon="#mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="#style/AppTheme">
<activity
android:name=".MainActivity"
android:label="#string/app_name"
android:launchMode="singleTask"
android:theme="#style/AppTheme.NoActionBar">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
<intent-filter>
<action android:name="com.bld.pushnotification.Main"/>
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</activity>
</application>
I also modified the my code for launching the app:
#ReactMethod
public void launchApp(String stringArgument, ReadableMap args, Promise promise) throws JSONException{
try {
final JSONObject options = convertMapToJson(args);
Bundle extras = new Bundle();;
int LAUNCH_REQUEST = 0;
if (options.has("extras")) {
extras = createExtras(options.getJSONArray("extras"));
Log.d(TAG,"Extras found");
Log.d(TAG, options.getString("extras"));
} else {
extras = new Bundle();
Log.d(TAG,"No extras");
}
if (options.has("launchRequestCode")) {
LAUNCH_REQUEST = options.getInt("launchRequestCode");
}
Intent packageIntent = new Intent(stringArgument);
Activity activity = getReactApplicationContext().getCurrentActivity();
activity.startActivityForResult(packageIntent, LAUNCH_REQUEST);
} catch (JSONException e) {
//TODO: handle exception
Log.d(TAG, e.toString());
}
}
With my basic knowledge on Android, I'm trying to play with the Google voice APIs and followed the example here and to write an app, which would allow me to call a hardcoded number. Unfortunately i'm getting an error saying < identifier > expected, so i'm unable to check if my app even works. Can someone see what is missing here and also if i'm even going in the right direction with my thinking and code.
Java file:
public class MyVoiceActivity extends Activity {
class Confirm extends VoiceInteractor.ConfirmationRequest {
public Confirm(String ttsPrompt, String visualPrompt) {
VoiceInteractor.Prompt prompt = new VoiceInteractor.Prompt(
new String[] {ttsPrompt}, visualPrompt);
super(prompt, null);
}
#Override public void onConfirmationResult(boolean confirmed, Bundle null) {
if (confirmed) {
call();
}
finish();
}
};
#Override public void onResume() {
if (isVoiceInteractionRoot()) {
call();
}
Intent intent = new Intent(this, MyVoiceActivity.class);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(intent);
finish();
}
private void call () {
try {
final Intent callIntent = new Intent(Intent.ACTION_CALL);
callIntent.setData(Uri.parse("tel:12345678"));
startActivity(callIntent);
} catch (ActivityNotFoundException activityException) {
}
}
}
AndroidManifest file:
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.ccvoice.bt.examplevoice">
<application
android:allowBackup="true"
android:icon="#mipmap/ic_launcher"
android:label="#string/app_name"
android:supportsRtl="true"
android:theme="#style/AppTheme">
<activity
android:name=".MyVoiceActivity" >
<intent-filter>
<action android:name="android.intent.action.CALL" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.VOICE" />
</intent-filter>
</activity>
</application>
</manifest>
The < identifier > required error i'm getting is in this line of the code:
public void onConfirmationResult(boolean confirmed, Bundle null) {
Thank you in advance.
null is a reserved word and cannot be used as an identifier (eg. the name of a parameter). Use a different name for your Bundle parameter to that method.
public void onConfirmationResult(boolean confirmed, Bundle bundle) {
I want to send a simple message and i have <uses-permission android:name="android.permission.SEND_SMS"/> in my manifest, but I always get: java.lang.SecurityException: Sending SMS message: uid 10064 does not have android.permission.SEND_SMS.
I checked this answer, but it still doesn't work.
this is my manifest:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.roa.sendsms" >
<uses-permission android:name="android.permission.SEND_SMS"/>
<application
android:allowBackup="true"
android:icon="#mipmap/ic_launcher"
android:label="#string/app_name"
android:supportsRtl="true"
android:theme="#style/AppTheme" >
<activity
android:name=".MainActivity"
android:label="#string/app_name"
android:theme="#style/AppTheme.NoActionBar" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
and this is my code:
package com.roa.sendsms;
import android.os.Bundle;
import android.support.design.widget.FloatingActionButton;
import android.support.design.widget.Snackbar;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.Toolbar;
import android.telephony.SmsManager;
import android.view.View;
import android.view.Menu;
import android.view.MenuItem;
import android.widget.Button;
public class MainActivity extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
Button sendButton = (Button)findViewById(R.id.sendButton);
sendButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
sendSms(phoneNumber, "you get message from roish app!!");
}
});
}
private void sendSms(String phoneNumber, String message){
SmsManager sms = SmsManager.getDefault();
sms.sendTextMessage(phoneNumber, null, message, null, null);
}
}
thank to all.
Try to use something like this. Not tested, but should work fine. Drop a note when you see some problem here.
This is just to show how permission granting in Android M works. Please extend it by functionalities mentioned on Android tutorial site about permissions.
You will need add ActivityCompat.shouldShowRequestPermissionRationale check to match best practices. I can extend this answer but I think it's not necessary. Just make sure you are granting permissions in runtime (or use targetSdkVersion lower then 23 - this is however not recommended)
private static final int PERMISSION_SEND_SMS = 123;
private void requestSmsPermission() {
// check permission is given
if (ContextCompat.checkSelfPermission(thisActivity, Manifest.permission.READ_CONTACTS) != PackageManager.PERMISSION_GRANTED) {
// request permission (see result in onRequestPermissionsResult() method)
ActivityCompat.requestPermissions(thisActivity,
new String[]{Manifest.permission.SEND_SMS},
PERMISSION_SEND_SMS);
} else {
// permission already granted run sms send
sendSms(phone, message);
}
}
#Override
public void onRequestPermissionsResult(int requestCode, String permissions[], int[] grantResults) {
switch (requestCode) {
case MY_PERMISSIONS_REQUEST_READ_CONTACTS: {
if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
// permission was granted
sendSms(phone, message);
} else {
// permission denied
}
return;
}
}
}
private void sendSms(String phoneNumber, String message){
SmsManager sms = SmsManager.getDefault();
sms.sendTextMessage(phoneNumber, null, message, null, null);
}
All you need to do is,
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.roa.sendsms"
android:versionCode="1"
android:versionName="1.0">
<uses-permission android:name="android.permission.SEND_SMS"/>
<uses-sdk
android:minSdkVersion="8"
android:targetSdkVersion="19"/>
<application
android:allowBackup="true"
android:icon="#drawable/ic_launcher"
android:label="#string/app_name"
android:theme="#style/AppTheme">
<activity
android:name="com.example.homesafe.MainActivity"
android:label="#string/app_name">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
Try to run into the mobile instead checking it to the emulator. Just because emulator does not have the sim card as our mobiles have, so.
i had same problem you need to get runtime permission on setting>app>"your app" >enable sms permission
If you message is too long using smsManager.sendTextMessage(.. will fail quietly.
You need to use smsManager.sendMultipartTextMessage(...
SmsManager smsManager = SmsManager.getDefault();
ArrayList<String> messageList = smsManager.divideMessage(message.toString());
smsManager.sendMultipartTextMessage(phoneNumber, null, messageList, null, null);
You may try this way:
private void sendSms(String phoneNumber, String message) {
Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse( "sms:" + phoneNumber ) );
intent.putExtra( "sms_body", message );
startActivity(intent);
}
I tried this in Huawei Android 8, besides adding permission READ_PHONE_STATE in manifest, the phone needs to allow to "access phone ID" to the app. Then it works.
Trying to create an app that will "clean" a url wrapped by facebook.com/l.php. code compiles fine, and I can even call it from the facebook app, but I get a black screen, and the new url i've parsed never seems to get handled.
Not new to Java, but new to droid..... :-o
anyways, manifest as:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="fusco.leetum.fblinkcleaner"
android:versionCode="1"
android:versionName="1.0" >
<uses-sdk
android:minSdkVersion="8"
android:targetSdkVersion="15" />
<application
android:allowBackup="true"
android:icon="#drawable/ic_launcher"
android:label="#string/app_name"
android:theme="#style/AppTheme" >
<activity
android:name="fusco.leetum.fblinkcleaner.Cleaner"
android:label="#string/app_name" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<data android:scheme="http" android:host="facebook.com" />
<data android:scheme="http" android:host="m.facebook.com" />
<data android:scheme="http" android:host="www.facebook.com" />
</intent-filter>
</activity>
</application>
</manifest>
and the activity java ( finish() is there since it should do nothing if the user starts it, but since the parser code is in the onCreate() method, it needs to start every time):
package fusco.leetum.fblinkcleaner;
import android.net.Uri;
import android.os.Bundle;
import android.app.Activity;
import android.content.Intent;
import android.view.Menu;
public class Cleaner extends Activity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//setContentView(R.layout.activity_cleaner);
final Intent intent = getIntent();
if (intent.getAction().equals(Intent.ACTION_VIEW)) {
final String baseurl = intent.getDataString();
String newurl = baseurl;
if (baseurl.indexOf("?") != -1) {
String[] parseurl = baseurl.substring(baseurl.indexOf("?")+3).split("%2F");
newurl = "http:/";
for (int i=2; i < parseurl.length; i++) {
newurl = "/"+parseurl[i];
}
}
final Intent viewnewpage = new Intent(Intent.ACTION_VIEW, Uri.parse(newurl));
startActivity(viewnewpage);
}
this.finish();
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.activity_cleaner, menu);
return true;
}
}
Silly me! Instead of creating my own parser, I just had to use the Uri class stuff. What a learning curve. Android is a little different than normal desktop programming..... I guess you can't just bulldoze your way thru stuff. Oh well, manifest is the same, but here's the 'better' code. Prolly not the BEST, but still, hope this helps someone else :-D
This goes in the onCreate(...) method, replacing everything BUT the super call:
Intent intent = this.getIntent(); // get intent that started activity
if (intent.getAction().equals(Intent.ACTION_VIEW)) { // if called by view action
Uri raw = intent.getData(); // get raw uri
Uri cleanedUri = raw; // assign to allow 'pass thru' of not a wrapper
String wrappedUri = raw.getQueryParameter("u"); // get the url defined in l.php?u=
if (wrappedUri != null) { // if there was a u= parameter
cleanedUri = Uri.parse(Uri.decode(wrappedUri)); // decode %stuff and parse to the proper uri
}
startActivity(new Intent(Intent.ACTION_VIEW, cleanedUri)); // broadcast new intent with new URI
}
this.finish(); // clear out and close this activity
I am having difficulty getting a BroadcastReceiver to process my IntentService response. The service processes multiple different actions and returns an action type. The receiver will never seem to pick it up, however. The intent does get called as I can debug and set a breakpoint in the IntentService and see the action get processed successfully. I just never see the textbox updated with the appropriate data or see the BroadcastReceiver evein being called.
IntentService
protected void onHandleIntent(Intent intent) {
String action = intent.getAction();
// Data the service was called with.
Bundle incomingData = intent.getExtras();
String key = incomingData.getString(KEY_APPKEY);
String secret = incomingData.getString(KEY_SECRET);
String collection = incomingData.getString(KEY_COLLECTION);
CheckinManager cm = new CheckinManager(this.getApplicationContext(),key,secret,collection);
Intent broadcastIntent = new Intent();
broadcastIntent.addCategory(Intent.CATEGORY_DEFAULT);
if (action == ACTION_GET_POI) {
Double lat = incomingData.getDouble(KEY_LATITUDE);
Double lon = incomingData.getDouble(KEY_LONGITUDE);
ArrayList<POI> nearbyPOIs = new ArrayList<POI>();
//broadcastIntent.setAction(ACTION_GET_POI_PROCESSED);
broadcastIntent.setAction("com.msalinger.checkinmanager.CheckinService.getPOIProcessed");
try {
nearbyPOIs = cm.getPOI(lat, lon);
broadcastIntent.putExtra(OUT_KEY_RESULT, true);
broadcastIntent.putExtra(OUT_KEY_ERROR, "");
broadcastIntent.putParcelableArrayListExtra(OUT_KEY_POILIST, nearbyPOIs);
} catch (JSONException ex) {
Log.d(TAG,ex.getMessage() + "\n" + ex.getStackTrace());
broadcastIntent.putExtra(OUT_KEY_RESULT, false);
broadcastIntent.putExtra(OUT_KEY_ERROR, ex.getMessage());
}
}
else if (action == ACTION_CHECK_IN) {
// Do something
}
else if (action == ACTION_GET_CHECKINS) {
// Do Something
}
else if (action == ACTION_FIND_NEARBY_POIS_WITH_CHECKINS) {
// Do Something
}
sendBroadcast(broadcastIntent);
}
Broadcast Receiver as sub-class of Main Activity
public class CheckinReceiver extends BroadcastReceiver {
private final static String INTENT_BASE_URI = "com.msalinger.checkinmanager.CheckinService";
private final static String ACTION_GET_POI_PROCESSED = ".getPOIProcessed";
private final static String ACTION_CHECK_IN_PROCESSED = ".checkInProcessed";
private final static String ACTION_GET_CHECKINS_PROCESSED = ".getCheckinsProcessed";
private final static String ACTION_FIND_NEARBY_POIS_WITH_CHECKINS_PROCESSED = ".findNearbyPOIsWithCheckinsProcessed";
#Override
public void onReceive(Context context, Intent intent) {
if (intent.getAction().equals("com.msalinger.checkinmanager.CheckinService.getPOIProcessed")) {
tv = (TextView)findViewById(R.id.textBox1);
Bundle incomingData = intent.getExtras();
String st = "";
if (incomingData.getBoolean("result")) {
ArrayList<POI> poiList = incomingData.getParcelableArrayList("poList");
st = printPOI(poiList);
}
else {
st = incomingData.getString("error");
}
}
else if (intent.getAction().equals(INTENT_BASE_URI + ACTION_CHECK_IN_PROCESSED)) {
}
else if (intent.getAction().equals(INTENT_BASE_URI + ACTION_GET_CHECKINS_PROCESSED)) {
}
else if (intent.getAction().equals(INTENT_BASE_URI + ACTION_FIND_NEARBY_POIS_WITH_CHECKINS_PROCESSED)) {
}
}
}
Manifest
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.msalinger.checkinmanagerdemo"
android:versionCode="1"
android:versionName="1.0" >
<uses-sdk
android:minSdkVersion="10"
android:targetSdkVersion="15" />
<uses-permission android:name="android.permission.INTERNET"/>
<application
android:icon="#drawable/ic_launcher"
android:label="#string/app_name"
android:theme="#style/AppTheme" >
<activity
android:name=".MainActivity"
android:label="#string/title_activity_main" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<service
android:enabled="true"
android:name="com.msalinger.checkinmanager.CheckinService" />
<receiver
android:name=".CheckinReceiver">
<intent-filter>
<action android:name="com.msalinger.checkinmanager.CheckinService.getPOIProcessed" />
</intent-filter>
<intent-filter>
<action android:name="com.msalinger.checkinmanager.CheckinService.checkInProcessed" />
</intent-filter>
<intent-filter>
<action android:name="com.msalinger.checkinmanager.CheckinService.getCheckinsProcessed" />
</intent-filter>
<intent-filter>
<action android:name="com.msalinger.checkinmanager.CheckinService.findNearbyPOIsWithCheckinsProcessed" />
</intent-filter>
</receiver>
</application>
</manifest>
What am I doing wrong? Note that the IntentService exists as part of an Android class library with a different package than the Main activity.
Because the receiver exists to update data in the activity, it should be registered when the activity resume and unregistered when the activity pause. And it should not be in the manifest (see the doc for this).
If it's not the case, it shouldn't be a subclass of your activity.
In all cases, I think your Receiver is not called because it has not the right name in the manifest. It may be something like this : .MainActivity$CheckinReceiver.