This question is related to this previously answered question Share Image on Android Application from Unity Game
My question is this:
Is there a way to share the image WITHOUT using a file? I'd like to remove the sd card read/write permission on Android. I'm not a Java programmer, so I'm having a heck of a time getting it working. None of my attempts have yielded anything.
"Programmer" answered the previous question with the below code, which works great for me. I basically want to use the variable imageBytes instead of the file. Is this even possible?
void takeScreenShotAndShare()
{
StartCoroutine(takeScreenshotAndSave());
}
private IEnumerator takeScreenshotAndSave()
{
string path = "";
yield return new WaitForEndOfFrame();
Texture2D screenImage = new Texture2D(Screen.width, Screen.height);
//Get Image from screen
screenImage.ReadPixels(new Rect(0, 0, Screen.width, Screen.height), 0, 0);
screenImage.Apply();
//Convert to png
byte[] imageBytes = screenImage.EncodeToPNG();
System.IO.Directory.CreateDirectory(Application.persistentDataPath + "/GameOverScreenShot");
path = Application.persistentDataPath + "/GameOverScreenShot" + "/DiedScreenShot.png";
System.IO.File.WriteAllBytes(path, imageBytes);
StartCoroutine(shareScreenshot(path));
}
private IEnumerator shareScreenshot(string destination)
{
string ShareSubject = "Picture Share";
string shareLink = "Test Link" + "\nhttps://stackoverflow.com/questions/36512784/share-image-on-android-application-from-unity-game";
string textToShare = "Text To share";
Debug.Log(destination);
if (!Application.isEditor)
{
AndroidJavaClass intentClass = new AndroidJavaClass("android.content.Intent");
AndroidJavaObject intentObject = new AndroidJavaObject("android.content.Intent");
intentObject.Call<AndroidJavaObject>("setAction", intentClass.GetStatic<string>("ACTION_SEND"));
AndroidJavaClass uriClass = new AndroidJavaClass("android.net.Uri");
AndroidJavaObject uriObject = uriClass.CallStatic<AndroidJavaObject>("parse", "file://" + destination);
intentObject.Call<AndroidJavaObject>("putExtra", intentClass.GetStatic<string>("EXTRA_STREAM"), uriObject);
intentObject.Call<AndroidJavaObject>("putExtra", intentClass.GetStatic<string>("EXTRA_TEXT"), textToShare + shareLink);
intentObject.Call<AndroidJavaObject>("putExtra", intentClass.GetStatic<string>("EXTRA_SUBJECT"), ShareSubject);
intentObject.Call<AndroidJavaObject>("setType", "image/png");
AndroidJavaClass unity = new AndroidJavaClass("com.unity3d.player.UnityPlayer");
AndroidJavaObject currentActivity = unity.GetStatic<AndroidJavaObject>("currentActivity");
currentActivity.Call("startActivity", intentObject);
}
yield return null;
}
Unity3d share image on android
You can directly pass the byte array, no need to convert it to file -> uri -> string
Below are java code, but you can easily translate this to JNI wrapper calls
Bundle bundle = new Bundle();
bundle.putByteArray("byteArr", imageBytes); //put byte array to bundle!!!
Intent intent = new Intent(...);
intent.putExtra(bundle );
To retrieve the image byte array in another activity, use below code:
getIntent().getByteArrayExtra("byteArr");
If you wrap each above function calls into a corresponding JNI function, it would be very tedious. You can on the other hand:
Create an android library project
Add two static java functions, one is saveByteArrayToBundle(...), and the other is getByteArrayFromBundle(...)
Compile the android library into an aar file;
then put the aar file into the Assets/ folder (actually any subfolders will do)
Then call the two static java functions as you have done in the OP
Refer to below tutorials if you need to go through step-by-step:
Step-by-Step guide for developing Android Plugin for Unity3D (I)
Step-by-Step guide for developing Android Plugin for Unity3D (II)
Related
I have an android app I've programmed where most of the action happens in a html+javascript webView, and I have a couple of scenarios where I pass data and commands back and forth between the Java side and the WebView/JS side.
I've recently programmed a way for the JS side to trigger a registerForActivityResult(new ActivityResultContracts.GetContent(), ... call to allow the user to select an image, and it works, but when I try to send the data back from Java to my javascript, it fails.
public void onActivityResult(Uri uri) {
// this means I've got the URI selected from the gallery
try {
final InputStream imageStream = getContentResolver().openInputStream(uri);
final Bitmap selectedImage = BitmapFactory.decodeStream(imageStream);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
selectedImage.compress(Bitmap.CompressFormat.JPEG, 100, baos);
byte[] b = baos.toByteArray();
String ecImage = Base64.encodeToString(b, Base64.DEFAULT);
myWebView.evaluateJavascript("onImageSelectTest('" + ecImage + "')", null);
} catch (FileNotFoundException e) {
// e.printStackTrace();
}
}
After a bit of testing, I've found that it's only when the ecImage variable contains the base64 long string that the function doesn't run -- it runs just fine if I set ecImage = "abcd"; for example.
I want the webview to have the image for display purposes and, mainly, for posting the data to a web server, and right now for me the easiest way I can think of doing that is passing the image as a base64 string, but the evaluateJavascript call just isn't working with the long strings right now. I'm open to alternative ways of passing the image data to the webview.
I have used JNA library and this small API (JnaFileChooser)
https://github.com/steos/jnafilechooser
JnaFileChooser fc = new JnaFileChooser();
fc.addFilter("All Files", "*");
fc.addFilter("Pictures", "jpg", "jpeg", "png", "gif", "bmp");
if (fc.showDialog(parent)) {
File f = fc.getSelectedFile();
// do something with f
}
But how do I use JNA to access this dialg "select folder"
The entire dialogue is controlled on the native side. The package you are are using is already accessing that dialog and that button.
Tracing through the source code of the JnaFileChooser class, this dialogue is part of the WindowsFolderBrowser class. The dialog appears using the SHBrowseForFolder() function combined with SHGetPathFromIDList, and returns the path when the Select Folder button is pressed.
final Pointer pidl = Shell32.SHBrowseForFolder(params);
if (pidl != null)
// MAX_PATH is 260 on Windows XP x32 so 4kB should
// be more than big enough
final Pointer path = new Memory(1024 * 4);
Shell32.SHGetPathFromIDListW(pidl, path);
final String filePath = path.getWideString(0);
final File file = new File(filePath);
Ole32.CoTaskMemFree(pidl);
return file;
}
The params variable passed to this function is of native type BROWSEINFO which controls the dialog box. You can see in the code how a few things have been assigned to it (abbreviated version of code):
final Shell32.BrowseInfo params = new Shell32.BrowseInfo();
params.hwndOwner = Native.getWindowPointer(parent);
params.ulFlags = Shell32.BIF_RETURNONLYFSDIRS | Shell32.BIF_USENEWUI;
params.lpszTitle = title;
If you want to change anything else about the dialog, you need to use a callback. One of the elements in BROWSEINFO is BFFCALLBACK lpfn; where you would define that function, e.g., params.lpfn = the defined callback function.
Documentation for BFFCALLBACK indicates you'll use the option to use SendMessage to change the OK button text with BFFM_SETOKTEXT.
I am building a meme generator app. So far, the app does everything I need it to do but I want to add 1 more functionality. The app saves the meme as a bitmap on my device. I want to add a functionality where, if the user wishes to share the meme on Facebook, Twitter, IG, etc, the app retrieves that same bitmap (which was just created.).
I'm not worried about the sharing function at the moment. I want to find out how I can retrieve the file to be able to share it.
This is the method where the creation and saving of the meme takes place. I will omit unnecessary code:
public void createBitmapAndSave(ImageView img) {
...
counter++;
File file;
Intent mediaScanIntent = new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE);
String path = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DCIM).getPath();
file = new File(path + "/SimpliMeme/" + timeStamp + "-" + counter + ".jpg");
file.getParentFile().mkdir();
try {
OutputStream stream = new FileOutputStream(file);
mutableBitmap.compress(Bitmap.CompressFormat.JPEG, 100, stream);
stream.flush();
stream.close();
Toast.makeText(getContext(), "Top Text: " + String.valueOf(topTextMeasurement) + " and bottom text: " + String.valueOf(bottomTextMeasurement),
Toast.LENGTH_SHORT).show();
} catch (IOException e) {
e.printStackTrace();
}
Uri contentUri = Uri.fromFile(file);
mediaScanIntent.setData(contentUri);
Objects.requireNonNull(getContext()).sendBroadcast(mediaScanIntent);
}
and I've created an empty method that needs to be called when the user hits the "Share" button:
public void shareMeme(){}
Like you've mentioned earlier, you gotta divide this big method into 2 small ones.
As far as your initial question goes: There is no point in going back to your device just to find and get that same photo. Divide the method into 2 methods (createBitmap() & saveBitmap(), for example) and if the user just wants to share it without saving it to the device, the app will just call the createBitmap() method to only create the bitmap and then you'll use that in your upcoming sharing functionality.
I want to make an application where users can select 3 gallery images. After click next button a video will be created with these 3 photos and user can save this video to sd card.
Try using animation set in android that can help you achieve what you are after it is called FrameAnimation, here is an example on how to use it:
FrameAnimation Example
or checkout below code snippet if it helps :
` final Handler handler = new Handler();
handler.postDelayed(new Runnable() {
public void run() {
int randomNum = random.nextInt(6);
dice.setImageResource(images[randomNum]);
handler.postDelayed(this, 500);
}
}, 500);`
You can use jcodec SequenceEncoder to convert sequence of images to MP4 file.
Sample code :
import org.jcodec.api.awt.SequenceEncoder;
...
SequenceEncoder enc = new SequenceEncoder(new File("filename"));
// GOP size will be supported in 0.2
// enc.getEncoder().setKeyInterval(25);
for(...) {
BufferedImage image = ... // Obtain an image to encode
enc.encodeImage(image);
}
enc.finish();
It's a java library so it's easy to import it into Android project, you don't have to use NDK unlike ffmpeg.
Refer http://jcodec.org/ for sample code & downloads.
According to Abhishek V
Look here to see more
I have a little bit strange question: this time everything works, but I can't understand why.
AFAIK it's possible to mount more than one sd card. Everything will be mounted to /mnt directory. (is it true?)
On my device there is only one sd card which mounted to /mnt/sdcard. And in my application I open files from it. I'm using next code:
private void open() {
// get file extension
String extension = "";
int dotIndex = downloadedFile.lastIndexOf('.');
if (dotIndex != -1) {
extension = downloadedFile.substring(dotIndex + 1, downloadedFile.length());
}
// create an intent
Intent intent = new Intent(android.content.Intent.ACTION_VIEW);
Uri data = Uri.fromFile(new File(downloadedFile));
String type = MimeTypeMap.getSingleton().getMimeTypeFromExtension(extension);
if (type == null || type.length() == 0) {
// if there is no acceptable mime type
type = "application/octet-stream";
}
intent.setDataAndType(data, type);
// get the list of the activities which can open the file
List resolvers = context.getPackageManager().queryIntentActivities(intent, PackageManager.MATCH_DEFAULT_ONLY);
if (resolvers.isEmpty()) {
(new AlertDialog.Builder(context)
.setMessage(R.string.AttachmentUnknownFileType)
.setNeutralButton(R.string.NeutralButtonText, null)
.create()).show();
} else {
context.startActivity(intent);
}
}
Actually downloadedFile variable has value like file:///sdcard/mydir/myfile.txt. But the code works. Why? How Android understand what /sdcard/... is the same as /mnt/sdcard/...?
And main question: what happened if sd card will be mounted to other dir (for exmaple, /mnt/another-sd/ or even /media/sd)? What if more than one sd cards will be mounted: how android understand what card to use?
Thank you for any help! Have a good day!
It's simple android configueres the mounting over a settings file on phone boot so if there are mor sdcards Android will simply prefer one of them to set as
/sdcard/
so when the mount settings change your code is simply useless you can only hope the settings being untouched .
Every company that procuces Android smartphones use the "sdcard" path even custom roms like use it