Demo of MediaPlayer.setDataSource()? - java

I am learning about mediaplayer in android.
I wanted some simple and easy to understand code example of MediaPlayer.setDataSource().

Well, for more deep understanding of MediaPlayer that's better to read official documentation https://developer.android.com/reference/android/media/MediaPlayer#setDataSource(android.content.res.AssetFileDescriptor). But for basic comprehension here is the code example.
MediaPlayer mp = new MediaPlayer();
// Here you may set which stream to use either MEDIA or ALARM etc.
mp.setAudioStreamType(AudioManager.STREAM_ALARM);
try {
if (isAnyActiveSongExist){
// Here you may set dataSource as path of the file
mp.setDataSource(firstPrioritySongEntityPath);
}
else{
// Here you may set dataSource using Uri
mp.setDataSource(context, Settings.System.DEFAULT_RINGTONE_URI);
}
mp.prepare();
} catch (IOException e) {
e.printStackTrace();
}
mp.start();
setDataSource() defines which file should be used by your MediaPlayer for playing.

First of all, code never been simple and easy until you'll not go through it. Check this link click here, I think you'll have your answer from here
About setDataSource(String) call
After seeing your comment, it looks like you exactly want setDataSource(string) to be used for your purpose. I don't understand why. But, what I assume is, for some reason you are trying to avoid using "context". If that is not the case then the above two solutions should work perfectly for you or if you are trying to avoid context, I'm afraid that is not possible with the function with signature setDataSource(String) call. The reason is as below,
MediaPlayer setDataSource() function has these below options out of which you are only interested in setDataSource(String),
public void setDataSource(String path)
throws IOException, IllegalArgumentException, SecurityException, IllegalStateException {
setDataSource(path, null, null);
}
and if you check setDataSource(String path, String[] keys, String[] values) code, you will see the below condition filtering the path based on its scheme, particularly if it is "file" scheme it calls setDataSource(FileDescriptor) or if scheme is non "file", it calls native JNI media function.
{
final Uri uri = Uri.parse(path);
final String scheme = uri.getScheme();
if ("file".equals(scheme)) {
path = uri.getPath();
} else if (scheme != null) {
// handle non-file sources
nativeSetDataSource(
MediaHTTPService.createHttpServiceBinderIfNecessary(path),
path,
keys,
values);
return;
}
final File file = new File(path);
if (file.exists()) {
FileInputStream is = new FileInputStream(file);
FileDescriptor fd = is.getFD();
setDataSource(fd);
is.close();
} else {
throw new IOException("setDataSource failed.");
}
}
In the above code, your resource file URI scheme will not be null (android.resource://) and setDataSource(String) will try to use native JNI function nativeSetDataSource() thinking that your path is http/https/rtsp and obviously that call will fail as well without throwing any exception. Thats why your call to setDataSource(String) escapes without an exception and gets to prepare() call with the following exception.
Prepare failed.: status=0x1
So setDataSource(String) override cannot handle your resource file. You need to choose another override for that.
On the other side, check setDataSource(Context context, Uri uri, Map headers) which is used by setDataSource(Context context, Uri uri), it uses AssetFileDescriptor, ContentResolver from your context and openAssetFileDescriptor to open the URI which gets success as openAssetFileDescriptor() can open your resource file and finally the resultant fd is used to call setDataSource(FileDescriptor) override.
AssetFileDescriptor fd = null;
try {
ContentResolver resolver = context.getContentResolver();
fd = resolver.openAssetFileDescriptor(uri, "r");
// :
// :
// :
if (fd.getDeclaredLength() < 0) {
setDataSource(fd.getFileDescriptor());
} else {
setDataSource(fd.getFileDescriptor(), fd.getStartOffset(), fd.getDeclaredLength());
}
To conclude, you cannot use setDataSource(String) override as is to use your resource mp3 file. Instead, if you want use string to play your resource file you can use either MediaPlayer.create() static function with getIdentifier() as given above or setDataSource(context,uri) as given in Update#1.
Refer to the complete source code for more understanding here: Android MediaPlayer

Related

How to add an image to MimeMessageHelper as inline image from a byte array?

I want to add an inline image to the email, so I am using spring boot and their JavaMailSender mechanism.
Now it results in a NPE when I try to add an image via the addInline method
byte[] attachmentData = aMailConfig.getAttachments().get(messageAttach).getBinaryData();
LOG.debug("size of attachment is {0}", attachmentData.length);
Resource res = new InputStreamResource(new ByteArrayInputStream(attachmentData));
msg.addInline(messageAttach, res);
Here the stacktrace
java.lang.NullPointerException
at javax.activation.MimetypesFileTypeMap.getContentType(MimetypesFileTypeMap.java:299)
at org.springframework.mail.javamail.ConfigurableMimeFileTypeMap.getContentType(ConfigurableMimeFileTypeMap.java:184)
at org.springframework.mail.javamail.MimeMessageHelper.addInline(MimeMessageHelper.java:941)
The problem is, that there is call to the resource.getFilename() in the addInline method.
As I am generating the Resource with InputStreamResource, there is no filename set. Is there a way so that I can set this filename in my case or is it necessary to create a tempfile and generate the resource object by using this file?
Here the addLine mehod of the org.springframework.mail.javamail.MimeMessageHelper
public void addInline(String contentId, Resource resource) throws MessagingException {
Assert.notNull(resource, "Resource must not be null");
String contentType = getFileTypeMap().getContentType(resource.getFilename());
addInline(contentId, resource, contentType);
}
Straight forward:
Resource res = new InputStreamResource(new ByteArrayInputStream(attachmentData)) {
#Override
public String getFilename() {
return "dummy-filename.io"; // or something more complex/dynamic (but "file name like"!)
}
};
...(anonymous) override the "malicious method", with something that suits us better.
But BEST, we would know the MimeType of attachment (not guessing it from a fictive file name), use the "more appropriate not malicious method" (overload), and just set it like:
msg.addInline(
"contentId",
new InputStreamResource(new ByteArrayInputStream(attachmentData)),
"image/jpeg" // knowing it or guessing it.
);
javadoc.
So VERY BEST would be, when:
aMailConfig.getAttachments().get(messageAttach)
besides getBinaryData():byte[] also had a (correct) getContentType():String (..or even get[Original]FileName():String ;).

Android, Glide Image Loading Library: NullPointerException, ProviderInfo

Just getting into the Glide image loading library for Android. Currently working with some code from here:
https://github.com/bumptech/glide/issues/459
My full project is here is you want to look at it:
https://github.com/mhurwicz/glide02
I'm getting the following exception when I run the app in the emulator in Android Studio:
java.lang.NullPointerException: Attempt to invoke virtual method 'android.content.res.XmlResourceParser android.content.pm.ProviderInfo.loadXmlMetaData(android.content.pm.PackageManager, java.lang.String)' on a null object reference
This is the key statement in MainActivity:
new ShareTask(this).execute("http://thelink");
(thelink is actually goo.gl/gEgYUd -- couldn't leave that in above because stackoverflow doesn't allow URL shorteners. )
Here is my code for the ShareTask class
class ShareTask extends AsyncTask<String, Void, File> {
private final Context context;
public ShareTask(Context context) {
this.context = context;
}
#Override protected File doInBackground(String... params) {
String url = params[0]; // should be easy to extend to share multiple images at once
try {
return Glide
.with(context)
.load(url)
.downloadOnly(Target.SIZE_ORIGINAL, Target.SIZE_ORIGINAL)
.get() // needs to be called on background thread
;
} catch (Exception ex) {
Log.w("SHARE", "Sharing " + url + " failed", ex);
return null;
}
}
#Override protected void onPostExecute(File result) {
if (result == null) { return; }
Uri uri = FileProvider.getUriForFile(context, context.getPackageName(), result);
share(uri); // startActivity probably needs UI thread
}
private void share(Uri result) {
Intent intent = new Intent(Intent.ACTION_SEND);
intent.setType("image/jpeg");
intent.putExtra(Intent.EXTRA_SUBJECT, "Shared image");
intent.putExtra(Intent.EXTRA_TEXT, "Look what I found!");
intent.putExtra(Intent.EXTRA_STREAM, result);
context.startActivity(Intent.createChooser(intent, "Share image"));
}
}
Using debug, it seems I may be running into trouble at the get() statement. For one thing, the width and the height are very large negative numbers. (See the code highlighted in green below.) Then the get() statement returns null. (See the code highlighted in red below.)
Thanks in advance for any help you can provide!
The NPE is coming from FileProvider.getUriForFile because you're passing in the wrong authority. You declared android:authorities="com.example.fileprovider" in the manifest, but you're using the package name at the call. This fails to resolve the info in FileProvider.parsePathStrategy. Match those two strings up and you'll be good to go.
The easiest fix is to use android:authorities="${applicationId}", this leads to 0 hardcoded strings, so you can keep using context.getPackageName().
Regarding your concerns during debug:
Target.SIZE_ORIGINAL is declared to be MIN_VALUE, hence the large number
it's not returning null, IDEA is just confused about where it is in the method, that return null; shouldn't be executed if it fails in the FileProvider code.
doGet(null): null is the timeout here, it's guarded properly in code
I've run the app and weirdly I got a log line saying
W/SHARE: Sharing http://... failed
but not a stack trace, which is weird, because ex cannot be null in a catch!

How to match catalog.xml entities with database?

I have to validate some xml files with .xsd files, which are listed in catalog.xml, but they are in database. So i need resolver, which will match systemId from catalog.xml with .xsd file stored as blob in database.
I found that XMLInputSource resolveEntity(XMLResourceIdentifier resourceIdentifier) method doing this, but I can't find how parser uses this method, so I'm not sure how to override it to do it propertly. I thought that it returns XMLInputStram which contains .xsd file in Stream, but it's not true because of "leaving resolution of the entity and opening of the input stream up to the caller", according to XMLInputSource documentation.
So my question is - how to map entities from catalog.xml with .xsd files stored in database?
I really hope that I explained problem clearly, but I know that my english is really poor - so feel free to ask for more details or better explaation.
Greetings,
Rzysia
Here's the resolver I wrote for the maven-jaxb2-plugin. This resolver resolves system ids to resources in Maven artifacts. This is somewhat similar task to yours.
Your task is, basically, to implement the resolveEntity method.
Normally it is practical to extend an existing CatalogResolver.
Then you can override the getResolvedEntity method.
Typically you first call the super method to resolve systemId/publicId.
Then you try to do you custom resolution.
systemId is normally the resource location URL (or logical URI).
publicId is often the namespace URI.
Here's a simple code snippet from another resolver which resolves classpath:com/acme/foo/schema.xsd in the classpath:
public static final String URI_SCHEME_CLASSPATH = "classpath";
#Override
public String getResolvedEntity(String publicId, String systemId) {
final String result = super.getResolvedEntity(publicId, systemId);
if (result == null) {
return null;
}
try {
final URI uri = new URI(result);
if (URI_SCHEME_CLASSPATH.equals(uri.getScheme())) {
final String schemeSpecificPart = uri.getSchemeSpecificPart();
final URL resource = Thread.currentThread()
.getContextClassLoader()
.getResource(schemeSpecificPart);
if (resource == null) {
return null;
} else {
return resource.toString();
}
} else {
return result;
}
} catch (URISyntaxException urisex) {
return result;
}
}
In your scenario, I'd do the following:
Define the URI schema like database:schema:table:value:id:schema.xsd.
Write a catalog resolver which is capable of resolving such URIs.
Define a catalog file which rewrites namespace URIs or absolute schema location URLs to your database:... URIs.
In simple notation this would be something like:
REWRITE_SYSTEM "http://example.com/schemas" "database:schemas:content:schema_id:example/schemas"
So the "base" catalog resolver would first resolve http://example.com/schemas/schema.xsd into database:schemas:content:schema_id:example/schemas/schema.xsd.
Then your code resolves database:schemas:content:schema_id:example/schemas/schema.xsd into a database resource.
Ok, i found solution - as I thought, method XMLInputSource resolveEntity(XMLResourceIdentifier resourceIdentifier) shoud return XMLInputSource with setted my own InputStream containing speciefied xsd schema.
My version of this overrided class:
public XMLInputSource resolveEntity(XMLResourceIdentifier resourceIdentifier)
throws XNIException, IOException {
String resolvedId = resolveIdentifier(resourceIdentifier);
if (resolvedId != null) {
XMLInputSource xmlis = new XMLInputSource(resourceIdentifier.getPublicId(),
resolvedId,
resourceIdentifier.getBaseSystemId());
try {
InputStream is = getXSDFromDb(resourceIdentifier.getLiteralSystemId());
xmlis.setByteStream(is);
} catch (SQLException ex) {
ex.printStackTrace();
}
return xmlis;
}
return null;

Java MemoryClassLoader (IllegalArgumentException)

I have a memory class loader (here) that I am using in a custom Minecraft launcher.
Memory Class Loader
Whenever I load up Minecraft (a Java LWJGL game), I am getting the following error:
27 achievements
182 recipes
Setting user
LWJGL Version: 2.4.2
java.lang.IllegalArgumentException: input == null!
at javax.imageio.ImageIO.read(Unknown Source)
at lc.<init>(SourceFile:21)
at gi.<init>(SourceFile:10)
at net.minecraft.client.Minecraft.a(SourceFile:254)
at net.minecraft.client.Minecraft.run(SourceFile:657)
at java.lang.Thread.run(Unknown Source)
I am creating the class loader like this:
Base.cLoader = new CLoader(
GameUpdater.classLoader,
new JarInputStream(new ByteArrayInputStream(jarFileBytes)));
As you can see, it manages to load up the first part then suddenly after LWJGL Version it crashes with "input == null".
Edit - Here is the new getResource method.
The error is on "URL()", as shown.
Code:
public URL getResource(final String name) {
URL url = new URL() { public InputStream openStream() {
return new ByteArrayInputStream((byte[])others.get(name));
}};
return url;
}
A wild guess... it could be this: Warning: URLs for this are not yet implemented! You cannot call getResource() or getResources()!
So your code expects to retrieve an image from the JAR using the unimplemented method. An equivalent of this is probably being executed:
ImageIO.read(memClassLoader.getResource(someString));
Except that, as we have seen, the Error thrown from getResource is getting ignored and null being used as the value. ImageIO.read goes like this:
public static BufferedImage read(URL input) throws IOException {
if (input == null) {
throw new IllegalArgumentException("input == null!");
}
InputStream istream = null;
try {
istream = input.openStream();
} catch (IOException e) {
throw new IIOException("Can't get input stream from URL!", e);
}
}
Sounds familiar? So, this is roughly what you need to implement:
public URL getResource(final String name) {
try {
return new URL("", "", 0, "",
new URLStreamHandler() { public URLConnection openConnection(URL url) {
return new URLConnection(url) {
public void connect() {}
public InputStream getInputStream() {
// return the appropriate InputStream, based on the name arg
}
};
}});
} catch (MalformedURLException e) { throw new RuntimeException(e); }
}
The MemoryClassLoader is pretty much broken. It does not implement getResource() (as stated in the comment in the source), and also it does not define Packages for the classes it loads (this may or may not break an application).
Most likely that ClassLoader was quickly hacked for testing purposes, leaving the more complicated methods out.
Implementing your own URL protocol to handle getResource() is not too difficult, in getResource() you return an URL that uses a custom protocol name (e.g. "myclassloader://resourcename"), and also a custom implementation of URLStreamHandler that handles that protocol.
That may not cover all the loopholes that might cause trouble in locating a resource, if the code loaded through the ClassLoader uses URL.toString() and converts it back it will still break.
Implementing a fully working ClassLoader that does not simple delegation to existing ClassLoaders, is not as simple as most examples make it look.

Trigger MediaScanner without an entire class [duplicate]

I got this class:
import android.content.Context;
import android.media.MediaScannerConnection;
import android.net.Uri;
import android.util.Log;
public class MediaScannerWrapper implements
MediaScannerConnection.MediaScannerConnectionClient {
private MediaScannerConnection mConnection;
private String mPath;
private String mMimeType;
// filePath - where to scan;
// mime type of media to scan i.e. "image/jpeg".
// use "*/*" for any media
public MediaScannerWrapper(Context ctx, String filePath, String mime){
mPath = "/sdcard/DCIM/Camera";
mMimeType = "jpg";
mConnection = new MediaScannerConnection(ctx, this);
}
// do the scanning
public void scan() {
mConnection.connect();
}
// start the scan when scanner is ready
public void onMediaScannerConnected() {
mConnection.scanFile(mPath, mMimeType);
Log.w("MediaScannerWrapper", "media file scanned: " + mPath);
}
public void onScanCompleted(String path, Uri uri) {
// when scan is completes, update media file tags
}
}
How to use it in the other class?
I don't know how to properly use classes, I tried but nothing is working.
I do something wrong, but I don't know what, can someone help me with this.
The Story
Before Android 4.4, we could just send a broadcast to trigger the media scanner on any particular file, or folder or even on the root of the storage. But from 4.4 KitKat, this have been fixed by the Android Developers.
Why do I say fixed? The reason is simple. Sending a broadcast using MEDIA_MOUNTED on the root directory is very expensive. Running the Media Scanner is an expensive operation and the situation gets even worse when the user has got a lot of files in the storage and deep folder structures.
Before Android 4.4
Keep it straight and simple. If you are targeting your app before Android 4.4. But keep in mind not to use it on the root directory unless absolutely necessary.
sendBroadcast(new Intent(Intent.ACTION_MEDIA_MOUNTED, Uri.parse("file://" + Environment.getExternalStorageDirectory())));
From Android 4.4
There are two ways for you.
i) The first one is very similar to the previous example, but may not work efficiently and is not recommended too.
sendBroadcast(new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE, Uri.parse("file://" + Environment.getExternalStorageDirectory())));
ii) Now, let us move on to the most recommended and efficient solution to this problem.
Add the file paths of the files which have been updated, like this, in a String type ArrayList
ArrayList<String> toBeScanned = new ArrayList<String>();
toBeScanned.add(item.getFilePath());
Now you need to run scanFile() static method of the MediaScannerConnection class and pass the String array containing the list of all the files which have been updated and needs to be media scanned.
You can also put a listener to respond when the scanning has been finished for individual files.
String[] toBeScannedStr = new String[toBeScanned.size()];
toBeScannedStr = toBeScanned.toArray(toBeScannedStr);
MediaScannerConnection.scanFile(getActivity(), toBeScannedStr, null, new OnScanCompletedListener() {
#Override
public void onScanCompleted(String path, Uri uri) {
System.out.println("SCAN COMPLETED: " + path);
}
});
Hey I found out how to do it with a very simple code.
Just call this line of code:
sendBroadcast(new Intent(Intent.ACTION_MEDIA_MOUNTED, Uri.parse("file://" + Environment.getExternalStorageDirectory())));
This should trigger mediascanner.
In Android, there is a content database which is used by the media scanner to keep track of all the media content present on the device.
When Android boots up, the mediascanner service is launched and runs through the entire external storage to find if there is any new media content if it finds one then,
It adds an entry of that media content into the content database
Each entry in the content database contains metadata of the media content like Name, date, file size, type of file, etc..
So when you make a modification to a media content, you will need to update the content database also.
If the content database is not update then other applications also will not be able to access that particular media content.
Running the media scanner just updates the content database
Instead of running the media scanner, you can update the content database yourself and it should resolve the problem.
Here is an explanation on how to insert, delete, update using the content resolver. (Search for the section "Inserting, Updating, and Deleting Data")
Edit:
There is a sample code in this answer. Check for the answer by Janusz.
File file = new File(absolutePath);
Uri uri = Uri.fromFile(file);
Intent intent = new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE, uri);
sendBroadcast(intent);
private void galleryAddPic() {
Intent mediaScanIntent = new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE);
File f = new File(mCurrentPhotoPath);
Uri contentUri = Uri.fromFile(f);
mediaScanIntent.setData(contentUri);
this.sendBroadcast(mediaScanIntent);
}
Reference: http://developer.android.com/training/camera/photobasics.html#TaskGallery
The Add the Photo to a Gallery Section
As #Aritra Roy's answer, i decide to make an experiment about this issue.
What i got here are:
Intent.ACTION_MEDIA_MOUNTED and Intent.ACTION_MEDIA_SCANNER_SCAN_FILE
can accept individual file path, so sendBroadcast(new
Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE, Uri.parse(filePath)));
or sendBroadcast(new Intent(Intent.ACTION_MEDIA_MOUNTED,
Uri.parse(filePath))); will be valid.
If you use individual file path with Intent.ACTION_MEDIA_MOUNTED on Kitkat or above, your application will still crash
If you use Intent.ACTION_MEDIA_SCANNER_SCAN_FILE or MediaScannerConnection on device lower than Kitkat, your application will not force close, but the method will just simply not working as you want.
From that experiment, i think the best method to handle is
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
MediaScannerConnection.scanFile(context, new String[]{imagePath}, null, new MediaScannerConnection.OnScanCompletedListener() {
public void onScanCompleted(String path, Uri uri) {
//something that you want to do
}
});
} else {
context.sendBroadcast(new Intent(Intent.ACTION_MEDIA_MOUNTED,
Uri.parse("file://" + imagePath)));
}
Let me know if i missed something

Categories