Two apps communicating using Intent, passing data - java

I'm trying to solve an issue passing data between different apps using Intent.
The scenario is like this:
Main (App 1): User clicks Register
Main App launches Register activity (form) in Register (App 2)
User enters first name, last name etc, clicks Send Back
Register app returning values to Main app
Main app displays user's data
Note that Register activity is not the main activity in Register activity.
I would like to solve this without an additional class and without Broadcasting.
My code for Main App, user clicks Register method:
/* User clicks Register */
public void clickRegister(View view) {
Intent intent = new Intent(Intent.ACTION_SEND);
// Verify it resolves
PackageManager packageManager = getPackageManager();
List<ResolveInfo> activities = packageManager.queryIntentActivities(intent, 0);
boolean isIntentSafe = activities.size() > 0;
// Start an activity if it's safe
if (isIntentSafe) {
startActivity(intent);
}
}
Manifest file for Register activity in Register App:
<activity android:name="com.example.register.Register">
<intent-filter>
<action android:name="android.intent.action.SEND"/>
<category android:name="android.intent.category.DEFAULT"/>
<data android:mimeType="text/plain"/>
</intent-filter>
</activity>
Now, this is the code in Register activity, of click Send Back method:
/* User clicks Send Back in register activity */
public void clickSendBack(View view) {
// Create an Intent for Register class
// Intent myIntent = new Intent(this, MainActivity.class);
Intent myIntent = new Intent(Intent.ACTION_SEND);
final EditText firstNameInput = (EditText) findViewById(R.id.firstNameEditText);
final EditText secondNameInput = (EditText) findViewById(R.id.secondNameEditText);
String firstName = firstNameInput.getText().toString();
String secondName = secondNameInput.getText().toString();
myIntent.setAction(Intent.ACTION_SEND);
myIntent.putExtra("firstName",firstName);
myIntent.putExtra("secondName",secondName);
myIntent.setType("text/plain");
// Starts activity
startActivity(myIntent);
finish();
}
And here I'm stuck.
Would love to hear any clarification for this topic, and a example for a solution would also be great.
Thanks!

In the first app, add an intent-filter to receive data back from the second app, which is your Register application.
Now, do the same with your Register application, we need to do this so that we can invoke it from our first app.
The intent-filter is there to make sure that we can send data back. According to https://developer.android.com/guide/components/intents-filters:
To advertise which implicit intents your app can receive, declare one or more intent filters for each of your app components with an element in your manifest file.
From the first app, create an Intent that will bring you to the second app. If you don't want to open up the Android share sheet then I suggest you use a PackageManager that gets all the activities that can receive your data and then find in the list your second app and open it with setComponent() with your intent. (Check my code below)
On to our second app, do the same as you did in the first app but now you can add your extras or data like first name and second name.
Back to our first app, write code that will receive the incoming intent from our second app and there, done!
Refer to:
https://developer.android.com/training/sharing
for more information about sending/receiving data with intents.
Here's a sample code:
First Application's Main Activity
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// Receive your data here from your Register app
Intent receivedIntent = getIntent();
String action = receivedIntent.getAction();
String type = receivedIntent.getType();
if (Intent.ACTION_SEND.equals(action) && type != null) {
if ("text/plain".equals(type)) {
handleReceivedData(receivedIntent);
}
}
}
private void handleReceivedData(Intent intent) {
String firstName = intent.getStringExtra("first_name");
String secondName = intent.getStringExtra("second_name");
if (firstName == null || secondName == null) {
Toast.makeText(this, "Cannot received data!", Toast.LENGTH_SHORT).show();
return;
}
// Do here what you want with firstName and secondName
// ...
Toast.makeText(this, "First name: " + firstName +
" Second name: " + secondName, Toast.LENGTH_SHORT).show();
}
public void open(View view) {
Intent sendIntent = new Intent();
sendIntent.setAction(Intent.ACTION_SEND);
sendIntent.putExtra(Intent.EXTRA_TEXT,
"Here you can put a message for your 'register' application");
sendIntent.setType("text/plain");
PackageManager packageManager = getPackageManager();
List<ResolveInfo> activities = packageManager.queryIntentActivities(sendIntent,
PackageManager.MATCH_DEFAULT_ONLY);
////////////////// Get the other application package name //////////////////
// This is so the user cannot choose other apps to send your data
// In order words, this will send the data back to the other
// application without opening the Android Share sheet
ActivityInfo activityInfo = null;
for (ResolveInfo activity: activities) {
// Specify here the package name of your register application
if (activity.activityInfo.packageName.equals("com.example.registerapp")) {
activityInfo = activity.activityInfo;
break;
}
}
// If the other application is not found then activityInfo will be null
// So make sure you add the correct intent-filter there!
if (activityInfo != null) {
// This will open up your register application
ComponentName name = new ComponentName(activityInfo.applicationInfo.packageName,
activityInfo.name);
sendIntent.setComponent(name);
startActivity(sendIntent);
}
else {
Toast.makeText(this,
"Receiver app doesn't exist or not installed on this device!",
Toast.LENGTH_SHORT).show();
}
}
First Application's Manifest
<activity android:name=".MainActivity">
<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.SEND" />
<category android:name="android.intent.category.DEFAULT" />
<data android:mimeType="text/plain" />
</intent-filter>
</activity>
Second Application's Receiver Activity (In this case, your Register app)
Note: as you want, this is NOT the main activity
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_receiver);
// This is NOT the main activity!
}
public void send(View view) {
// I just want to note that I am calling the receiving application: "other application"
EditText firstNameEditText = findViewById(R.id.firstNameEditText);
EditText secondNameEditText = findViewById(R.id.secondNameEditText);
String firstName = firstNameEditText.getText().toString().trim();
String secondName = secondNameEditText.getText().toString().trim();
// Check if any of the inputs are empty
if (firstName.isEmpty() || secondName.isEmpty()) {
Toast.makeText(this, "Text boxes cannot be empty!", Toast.LENGTH_SHORT).show();
return;
}
// Send data back to the other application
Intent sendBackIntent = new Intent();
sendBackIntent.setAction(Intent.ACTION_SEND);
sendBackIntent.putExtra("first_name", firstName);
sendBackIntent.putExtra("second_name", secondName);
sendBackIntent.setType("text/plain");
// Get all the available applications that can receive your data
// (in this case, first name and second name)
PackageManager packageManager = getPackageManager();
List<ResolveInfo> activities = packageManager.queryIntentActivities(sendBackIntent,
PackageManager.MATCH_DEFAULT_ONLY);
////////////////// Get the other application package name //////////////////
ActivityInfo activityInfo = null;
for (ResolveInfo activity: activities) {
// Specify here the package name of the other application
if (activity.activityInfo.packageName.equals("com.example.mainapp")) {
activityInfo = activity.activityInfo;
break;
}
}
if (activityInfo != null) {
// Same as before, this will open up the other application
ComponentName name = new ComponentName(activityInfo.applicationInfo.packageName,
activityInfo.name);
sendBackIntent.setComponent(name);
startActivity(sendBackIntent);
}
else {
Toast.makeText(this,
"Receiver app doesn't exist or not installed on this device!",
Toast.LENGTH_SHORT).show();
}
}
Second Application's Manifest
<activity android:name=".ReceiverActivity">
<intent-filter>
<action android:name="android.intent.action.SEND" />
<category android:name="android.intent.category.DEFAULT" />
<data android:mimeType="text/plain" />
</intent-filter>
</activity>
Happy coding!
Edit: I added explanation to my answer.

Your RegisterActivity should look like so
RegisterActivity is parsing the user data to the MainActivity
/* User clicks Send Back in register activity */
public void clickSendBack(View view) {
// Create an Intent for Register class
final EditText firstNameInput =
findViewById(R.id.firstNameEditText);
final EditText secondNameInput =
findViewById(R.id.secondNameEditText);
String firstName = firstNameInput.getText().toString();
String secondName = secondNameInput.getText().toString();
Intent i = new Intent(RegisterActiivty.this, MainActivity.class);
i.putExtra("firstName", firstName);
i.putExtra("secondName", secondName);
startActivity(i);
}
#################################################################
Your MainActivity should look like so.
The MainActivity is responsible for receiving the data
This line of codes should be called in your onCreate method of MainActivity
String firstName = getIntent().getStringExtra("firstName");
String secondName = getIntent().getStringExtra("firstName")
// if you have a TextView on MainActivity you could display the data you have
//gotten from the RegisterActivity like so
Textview firstName = findViewById(R.id.firstName);
firstName.setText(firstName);
Textview secondName = findViewById(R.id.secondName);
secondName.setText(secondName);
/* User clicks Register */
public void clickRegister(View view) {
startActivity(Intent(this, RegisterActivity.class))
}
##########################################################
Your manifest should look like this
<activity android:name="com.example.register.Register"/>
I am trying to wrap my head around what you meant by Main App and Register.
It sound as though they are two different apps or probably two different
project modules. But if what you are trying to say is MainActivity and
RegisterActivity then the above solution should be able to fix your problem.
What you did wrong:
That intent you were trying to parse in the manifest was not needed. If I understood properly what you are trying to achieve. The same goes for the MainActivity and RegisterActivity. You were using Implicit Intent instead of explicit Intent. And when calling findViewById this extra (EditText) was not needed because it is redundant. For more on the intent you can check this
However, this guide should be useful to anyone seeking to parse data from one activity to another.

Related

How do I come back to the same activity after launching a new app without creating a new activity

I am trying to make payment using ATH Movil. So it needs to open the ATH Movil app and get the response from it either completed or cancel. After launching the ATH Movil app and getting the response, it is creating a new activity every time instead of coming back to the same activity which launched the app.
private static void execute(Context context, String json, long timeout) {
PackageInfo athmInfo;
int athmVersionCode = 0;
String athmBundleId = COM_EVERTEC_ATHMOVIL_ANDROID + buildType;
Intent intent = context.getPackageManager().getLaunchIntentForPackage(athmBundleId);
try {
athmInfo = context.getPackageManager().getPackageInfo(athmBundleId, 0);
athmVersionCode = athmInfo.versionCode;
} catch (PackageManager.NameNotFoundException e) {
logForDebug(e.getMessage());
}
if (intent == null || athmVersionCode <= ConstantUtil.ATH_MOVIL_REQUIRED_VERSION_CODE) {
intent = new Intent(Intent.ACTION_VIEW);
intent.setData(Uri.parse(ConstantUtil.ATH_MOVIL_MARKET_URL));
}
intent.putExtra((ConstantUtil.BUNDLE), context.getPackageName());
intent.putExtra(ConstantUtil.JSON_DATA_KEY, json);
intent.putExtra(ConstantUtil.PAYMENT_DURATION_TIME_KEY, timeout);
intent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
// intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
// intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
context.startActivity(intent);
}
here is the manifest file:
<activity
android:name=".Activity1"
android:label=""
android:screenOrientation="portrait">
<intent-filter>
<action android:name="....ATHMSDK" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</activity>
Also Activity1 implements PaymentResponseListener and onResume of Activity1 I am calling
PaymentResponse.validatePaymentResponse(getIntent(), Activity1.this); // this function is from their sdk and is read only.
Now when Activity1 launches the ATH Movil app and get the response. It creating again creating Activity1 instead of coming back to the existing one. Can somebody help me? What is wrong with my code?
Add this to your Activity tag in AndroidManifesto:
android:launchMode="singleTop"
This assumes that you want to navigate back to the same Task that started the ATH Movil and the last item on the back stack was indeed Activity1. As long as these conditions hold true, then android OS will return you to the same instance of Activity1.
For more information on this, you can also refer to this.

Broadcast receiver. I want to move to another activity through a broadcast receiver after the charger is disconnected

I'm making a university project where I have to make a charger alarm. I select a ringtone, enter a pin, then plug in a charger. When I plug out, it moves to another activity where the phone starts ringing, the screen is locked and can only proceed if provided a correct pin code.
I'm stuck at the point where I have to move from one activity to another when the charger disconnects.
My code is not working. This is the receiver in the manifest file:
<receiver android:name=".MainActivity$PowerConnectionReceiver">
<intent-filter>
<action android:name="android.intent.action.ACTION_POWER_DISCONNECTED" />
</intent-filter>
</receiver>
This is the broadcast receiver I made in the MainActivity:
public class PowerConnectionReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
// TODO: This method is called when the BroadcastReceiver is receiving
// an Intent broadcast.
Toast.makeText(context, "The device is not charging", Toast.LENGTH_SHORT).show();
Intent myintent = new Intent(MainActivity.this,setter.class);
startActivity(myintent);
}
}
Please see:
You cannot receive this through components declared in manifests, only
by explicitly registering for it with...
https://developer.android.com/reference/android/content/Intent.html#ACTION_BATTERY_CHANGED
And did you set the corresponding permission?
<uses-permission android:name="android.permission.BATTERY_STATS"/>
Your intent has not correct parameters.
Intent myintent = new Intent(MainActivity.this,setter.class);
Change should be:
Intent myintent = new Intent(context, MainActivity.class)
Make sure your <receiver></receiver> tags are out of the activity tag in Manifest. Place them in application level.

Read the value and Share it via different apps in android

i have a problem with sharing data through Sharing Intent. Here's is code of sharing the data via different app.
btnshare.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Intent sharingIntent = new Intent(android.content.Intent.ACTION_SEND);
sharingIntent.setType("text/plain");
String shareBody = "The Langitude: "+longi+"The Latitude:"+lati;
sharingIntent.putExtra(android.content.Intent.EXTRA_SUBJECT,value);
sharingIntent.putExtra(android.content.Intent.EXTRA_TEXT, shareBody);
startActivity(Intent.createChooser(sharingIntent, "Share via"));
}
});
it just a button which is work for sharing . but now the prolem is i want share some value in that case (lati, longi which i've got from textview. how can i pass the value to the message body of the share action.
for information here's the origin of '[lati' and 'longi']value they belong from different method but they are in same class.
private void getLocation() {
locationListener = new LocationListener() {
#Override
public void onLocationChanged(Location location) {
Log.d("Location:", location.toString());
double lati =location.getLatitude();
double longii=location.getLongitude();
double alt=location.getAltitude();
txt_lati.setText("" +lati);
txt_longi.setText(""+longii);
return;
}
here's the method where lati and longi belongs. now my question is how can i share the latitude(lati) and longitude(longi) values via sharing prompt? i mean how can i share that specific two data? fyi if i'm going to share this way the value isn't comming .something like v7.appcompact that kind of text filled up in message body.
you need to make your first app activity intent as action intent
like
<activity
android:name=".FeedbackActivity" >
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
<action android:name="com.example.foo.bar.YOUR_ACTION" />
</intent-filter>
</activity>
and you can call it from another application
String CUSTOM_ACTION = "com.example.foo.bar.YOUR_ACTION";
Intent i = new Intent();
i.setAction(CUSTOM_ACTION);
startActivity(i);

How to customize share menu in android?

I need to share image and add "Save image" item in android share menu, I saw something like this in 9gag app, they have "Save" item in share menu, and share menu seems to be a bottom sheet. But how it can be achieved?
What I've done:
I added empty activity with intent filter in manifest which launches service and this service downloads image
<activity
android:name=".model.services.ShareActivity"
android:icon="#drawable/download_icon"
android:label="Save">
<intent-filter
android:label="Save"
android:icon="#drawable/download_icon">
<action android:name="android.intent.action.SEND" />
<category android:name="android.intent.category.DEFAULT" />
<data android:mimeType="image/*"/>
</intent-filter>
</activity>
and now I have this icon in share menu and it works, but this icon also appear in share menu of other apps, I need to show it only in my app, how I can make it private or something?
Ok, I found a solution. First - we need activity which will handle image saving intent, this activity can launch service or something. I make it like this:
public class ShareActivity extends Activity {
#Override
protected void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Bundle extras = getIntent().getExtras();
String url = extras.getString("url");
String name = extras.getString("name");
String description = extras.getString("description");
SaveImageService.downloadFile(url, name, description);
finish();
}
}
Where SaveImageService has static method which handle image saving to SD card
Second, we need to add some text in manifest:
<activity
android:name=".model.services.ShareActivity"
android:icon="#drawable/download_icon"
android:label="Save">
<intent-filter
android:label="Save"
android:icon="#drawable/download_icon">
<action android:name="com.my_app.random_text.SAVE_IMAGE" />
<category android:name="android.intent.category.DEFAULT" />
<data android:mimeType="image/*"/>
</intent-filter>
</activity>
Here intent filter has custom action (this is important) this custom action is just some string, not app package or something (I'm using package name just because I like it).
Next, we need to add this activity to share menu list:
this will get bitmap Uri for sharing in other apps from ImageView
// Returns the URI path to the Bitmap displayed in specified ImageView
static public Uri getLocalBitmapUri(ImageView imageView) {
// Extract Bitmap from ImageView drawable
Drawable drawable = imageView.getDrawable();
Bitmap bmp = null;
if (drawable instanceof BitmapDrawable) {
bmp = ((BitmapDrawable) imageView.getDrawable()).getBitmap();
} else {
return null;
}
// Store image to default external storage directory
Uri bmpUri = null;
try {
File file = new File(Environment.getExternalStoragePublicDirectory(
Environment.DIRECTORY_DOWNLOADS), "share_image_" + System.currentTimeMillis() + ".png");
file.getParentFile().mkdirs();
FileOutputStream out = new FileOutputStream(file);
bmp.compress(Bitmap.CompressFormat.JPEG, 80, out);
out.close();
bmpUri = Uri.fromFile(file);
} catch (IOException e) {
e.printStackTrace();
}
return bmpUri;
}
And this will collect all apps that can share image plus our save image intent
public void shareExcludingApp(Context ctx, PhotoView snapImage) {
// Get access to the URI for the bitmap
Uri bmpUri = ShareTool.getLocalBitmapUri(snapImage);
if (bmpUri == null) return;
List<Intent> targetedShareIntents = new ArrayList<>();
//get all apps which can handle such intent
List<ResolveInfo> resInfo = ctx.getPackageManager().queryIntentActivities(createShareIntent(bmpUri), 0);
if (!resInfo.isEmpty()) {
for (ResolveInfo info : resInfo) {
Intent targetedShare = createShareIntent(bmpUri);
//add all apps excluding android system and ourselves
if (!info.activityInfo.packageName.equals(getContext().getPackageName())
&& !info.activityInfo.packageName.contains("com.android")) {
targetedShare.setPackage(info.activityInfo.packageName);
targetedShare.setClassName(
info.activityInfo.packageName,
info.activityInfo.name);
targetedShareIntents.add(targetedShare);
}
}
}
//our local save feature will appear in share menu, intent action SAVE_IMAGE in manifest
Intent targetedShare = new Intent("com.my_app.random_text.SAVE_IMAGE"); //this is that string from manifest!
targetedShare.putExtra(Intent.EXTRA_STREAM, bmpUri);
targetedShare.setType("image/*");
targetedShare.setPackage(getContext().getPackageName());
targetedShare.putExtra("url", iSnapViewPresenter.getSnapUrlForSave());
targetedShare.putExtra("name", iSnapViewPresenter.getSnapNameForSave());
targetedShare.putExtra("description", iSnapViewPresenter.getSnapDescriptionForSave());
targetedShareIntents.add(targetedShare);
//collect all this intents in one list
Intent chooserIntent = Intent.createChooser(targetedShareIntents.remove(0),
"Share Image");
chooserIntent.putExtra(Intent.EXTRA_INITIAL_INTENTS,
targetedShareIntents.toArray(new Parcelable[targetedShareIntents.size()]));
ctx.startActivity(chooserIntent);
}

Speed up the startup of BroadcastReceiver at reboot

I've an app where the user can call custom lock screen to lock his/her mobile, that is an activity called LockScreen.class.
One this screen is loaded, i.e. the deviced is locked, a SharedPreferences called IsLocked is assigned to be true.
once the user do what he need with tthe lock screen this islocked became false, and the mobile is back to normal.
Every hing is working fine as expected.
The problem is, if for some reason the mobile had been rebooted while the lock screen is active, it is not running back upon a reboot.
So, I created a BootReciever as below, this works fine BUT after having the reboot process completed, and the user can do many things before it is loaded, my question is how can I make it loaded faster? so that the mobile screen is locked again with the custom activity before giving the chance for the user to do anything with the mobile?
public class BootReciever extends BroadcastReceiver
{
SharedPreferences mPrefs;
final String IsLockedPref = "IsLocked";
#Override
public void onReceive(Context context, Intent intent) {
mPrefs = PreferenceManager.getDefaultSharedPreferences(context);
Boolean islocked = mPrefs.getBoolean(IsLockedPref, false);
Intent i;
if (islocked)
i = new Intent(context, LockScreen.class);
else
i = new Intent(context, MainActivity.class);
i.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
context.startActivity(i);
}
}
part of the manifiest file is:
<receiver android:name=".BootReciever"
android:enabled="true"
android:exported="true">
<intent-filter >
<action android:name="android.intent.action.BOOT_COMPLETED"/>
<action android:name="android.intent.action.QUICKBOOT_POWERON" />
</intent-filter>
</receiver>
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
UPDATE
I tried to get use of this by granting Device Admin by adding the below, but nothing improved:
In the main Activity:
private static final int ADMIN_INTENT = 15;
private static final String description = "Some Description About Your Admin";
private DevicePolicyManager mDevicePolicyManager;
private ComponentName mComponentName;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mDevicePolicyManager = (DevicePolicyManager)getSystemService(
this.DEVICE_POLICY_SERVICE);
mComponentName = new ComponentName(this, AdminReceiver.class);
Intent intent = new Intent(DevicePolicyManager.ACTION_ADD_DEVICE_ADMIN);
intent.putExtra(DevicePolicyManager.EXTRA_DEVICE_ADMIN, mComponentName);
intent.putExtra(DevicePolicyManager.EXTRA_ADD_EXPLANATION,description);
startActivityForResult(intent, ADMIN_INTENT);
.
.
}
#Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode == ADMIN_INTENT) {
if (resultCode == RESULT_OK) {
Toast.makeText(getApplicationContext(), "Registered As Admin", Toast.LENGTH_SHORT).show();
}else{
Toast.makeText(getApplicationContext(), "Failed to register as Admin", Toast.LENGTH_SHORT).show();
}
}
}
and created empty receiver to extend the DeviceAdminReceiver as:
public class AdminReceiver extends DeviceAdminReceiver {
}
and added the below to the manifiest:
<receiver
android:name="AdminReceiver"
android:permission="android.permission.BIND_DEVICE_ADMIN">
<meta-data
android:name="android.app.device_admin"
android:resource="#xml/admin"/>
<intent-filter>
<action android:name="android.app.action.DEVICE_ADMIN_ENABLED" />
</intent-filter>
</receiver>
One thing you can do from your side is to set a priority to your intent-filter. From documentation
It controls the order in which broadcast receivers are executed to receive broadcast messages. Those with higher priority values are called before those with lower values.
<intent-filter
android:priority="100">
...
</intent-filter>
The value must be an integer, such as "100". Higher numbers have a higher priority. The default value is 0. The value must be greater than -1000 and less than 1000.

Categories