My soundboard app can't share .mp3 files - java

I'm new to Java and Android programming and I'm working on a soundboard app.
By the moment the app plays a sound when you click on a button, but I also want to add a sharing functionality for every sound (using OnLongClick), but I just can't make it work.
I've been looking for a solution through the Internet for the past two days but there's no way I can solve it.
I'm using shareIntent, and when the user long clicks the button it will display a list of apps you can share the sound (dropbox, bluetooth, whatsapp...).
If I select dropbox, it just uploads a file with no extension, and sharing with Whatsapp won't work and will display this error: "Failed to send, please try again".
My code looks like this:
final int[] buttonIds = { R.id.sound01, R.id.sound02, R.id.sound03,
R.id.sound04, R.id.sound05, R.id.sound06,
R.id.sound07, R.id.sound08, R.id.sound09,
R.id.sound10, R.id.sound11, R.id.sound12 };
final int[] soundIds = { R.raw.sound01, R.raw.sound02, R.raw.sound03,
R.raw.sound04, R.raw.sound05, R.raw.sound06,
R.raw.sound07, R.raw.sound08, R.raw.sound09,
R.raw.sound10, R.raw.sound11, R.raw.sound12 };
View.OnLongClickListener listener2 = new View.OnLongClickListener()
{
#Override
public boolean onLongClick(View v)
{
for(int i = 0; i < buttonIds.length; i++)
{
if(v.getId() == buttonIds[i])
{
selectedSoundId = soundIds[i];
// Can't share audio. It shares it with no format, so whatsapp won't accept it
Intent shareIntent = new Intent();
shareIntent.setAction(Intent.ACTION_SEND);
shareIntent.setType("audio/mp3");
shareIntent.putExtra(Intent.EXTRA_STREAM, Uri.parse("android.resource://com.guillefix.zombie_soundboard/" + selectedSoundId));
startActivity(Intent.createChooser(shareIntent, "Send to:"));
break;
}
}
return false;
}
};

Declare Intent Filter in Manifest
<intent-filter>
<action android:name="android.intent.action.SEND" />
<category android:name="android.intent.category.DEFAULT" />
<data android:mimeType="audio/*" />
</intent-filter>

Related

Firebase dynamic link to open specific activity with data

I'm facing some problems with implementing Firebase Dynamic links in my app.
My app contains a gallery of books and under each book, there is a share button. When a user clicks that button it creates a message that he can send other uses saying something like: "Hey you might like this book, check it here!"
Once the user clicks the image, I had like it to open the app at that activity (called BookDetailActivity). If the user has the app, I had like it to open BookDetailActivity with the specific intent data that is relevant to that book (say bookid = abcde) and if the user doesn't have the app, to send him to download the app.
In order to do so, I added to my manifest the following code under BookDetailActivity:
<activity android:name=".BookDetailActivity">
<intent-filter android:label="SB">
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data
android:host="https://x.y.link"
android:pathPrefix="/SB"
android:scheme="https" />
</intent-filter>
</activity>
Then, inside the BookDetailActivity, I added the following (used it just to check if the app opens at that activity and to see the link I get).
FirebaseDynamicLinks.getInstance()
.getDynamicLink( getIntent() )
.addOnSuccessListener( this, new OnSuccessListener<PendingDynamicLinkData>() {
#Override
public void onSuccess(PendingDynamicLinkData pendingDynamicLinkData) {
Uri deepLink = null;
if (pendingDynamicLinkData != null) {
deepLink = pendingDynamicLinkData.getLink();
Log.d( "TEST1", deepLink + "" );
}
}
} )
.addOnFailureListener( this, new OnFailureListener() {
#Override
public void onFailure(#NonNull Exception e) {
Log.d( "TEST2", "HERE" );
}
} );
Now, the button that sends that invitation message is:
ib_Share.setOnClickListener( v -> {
Intent shareIntent = new Intent();
shareIntent.setAction( Intent.ACTION_SEND );
String suggest = "Hey you might like this book, check it here! - https://x.y.link/SB?bookid=abcde";
shareIntent.putExtra( Intent.EXTRA_TEXT, Html.fromHtml( suggest ) );
shareIntent.setType( "text/html" );
startActivity( shareIntent );
} );
What I get is that I successfully send the message to another user, the user clicks on that link and the app opens but at the MAIN ACTIVITY instead of at BookDetail activity.
Is there a reason it won't take the user to that correct activity?
The way it looks in my Firebase Console is( the domain (x.y.link is just an example):
Also, how users that have a previous version of my app without the FirebaseDynamicLinks part in their BookDetailActivity code will manage to get these invitations? It will open them to download the app or just do nothing?
Thank you very much
I guess, whenever dynamic links are clicked, it opens your MainActivity. I tried to arrive at the solution as below.
In your MainActivity, check if there is any new intent created because of the dynamic link using onNewIntent() method
#Override
protected void onNewIntent ( Intent newIntent ) {
super.onNewIntent ( newIntent );
setIntent ( newIntent );
checkDynamicLinkIntent ();
}
private void checkDynamicLinkIntent () {
if (!Objects.equals ( null, getIntent ().getData () )) {
Uri dynamicLink = Uri.parse ( getIntent ().getData ().toString () );
parameterValue = dynamicLink.getQueryParameter ( parameterName );
//get any data from your URI if needed
Intent intent = new Intent ( MainActivity.this, BookDetailActivity.class );
intent.putExtra ( "bookDetail", parameterValue );
startActivity ( intent );
}
}
Hope the above code snippet helps you! :)

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.

Two apps communicating using Intent, passing data

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.

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);
}

Categories