I'm attempting to use the native Android share functionality to share a set of images I retrieved using Glide. So any app that is compatible with the images will show up in Android's share menu.
I created a method that gets invoked when share is clicked.
private void onShareClicked() {
GlideUrl glideUrl = new GlideUrl(url1, new LazyHeaders.Builder().addHeader("x-auth-token", mToken).build());
FutureTarget<File> image1 = mGlide.load(glideUrl).downloadOnly(SIZE_ORIGINAL, SIZE_ORIGINAL);
glideUrl = new GlideUrl(url2, new LazyHeaders.Builder().addHeader("x-auth-token", token).build());
FutureTarget<File> image2 = mGlide.load(glideUrl).downloadOnly(SIZE_ORIGINAL, SIZE_ORIGINAL);
new ShareImagesTask(this, "image/png").execute(image1, image2);
}
Then created ShareImagesTask which allows you to share multiple images by completing the request to get the images files via Glide:
public class ShareImagesTask extends AsyncTask<FutureTarget<File>, Void, ArrayList<Uri>> {
private final Context mContext;
private final String mMimeType;
public ShareImagesTask(Context context, String mimeType) {
this.mContext = context;
this.mMimeType = mimeType;
}
#Override
protected ArrayList<Uri> doInBackground(FutureTarget<File>... targets) {
final ArrayList<Uri> imageUris = new ArrayList<>(targets.length);
for(FutureTarget<File> target : targets) {
try {
File file = target.get();
Uri uri = FileProvider.getUriForFile(mContext, "com.myapp.fileprovider", file);
imageUris.add(uri);
} catch (Exception ex) {
Log.w("SHARE", "Sharing failed for one or more image files.", ex);
}
}
return imageUris;
}
#Override
protected void onPostExecute(ArrayList<Uri> result) {
if(!result.isEmpty()) {
Intent intent = new Intent();
intent.setAction(Intent.ACTION_SEND_MULTIPLE);
intent.putParcelableArrayListExtra(Intent.EXTRA_STREAM, result);
intent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
intent.setType(mMimeType);
mContext.startActivity(Intent.createChooser(intent, "Share Your Photo"));
}
}
}
Here is my AndroidManifest.xml FileProvider:
<provider
android:name=".provider.MyAppFileProvider"
android:authorities="com.myapp.fileprovider"
android:grantUriPermissions="true"
android:exported="false">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="#xml/filepaths" />
</provider>
And finally, filepaths.xml:
<?xml version="1.0" encoding="utf-8"?>
<paths xmlns:android="http://schemas.android.com/apk/res/android">
<cache-path name="shared_images" path="/data/data/com.myapp/cache/image_manager_disk_cache/"/>
</paths>
I am able to get to the point of calling FileProvider.getUriForProvider() and it gives the exception below. I have also tried changing the path in filepaths.xml to variations of the path mentioned in the exception but I get the same error. Any ideas?
java.lang.IllegalArgumentException: Failed to find configured root
that contains
/data/data/com.myapp/cache/image_manager_disk_cache/11fa9845150748cc77d49a75954d92f246cc7095e72e36b85d6a07dbdeb9cf46.0
Replace:
<cache-path name="shared_images" path="/data/data/com.myapp/cache/image_manager_disk_cache/"/>
with:
<cache-path name="shared_images" />
or possibly with:
<cache-path name="shared_images" path="image_manager_disk_cache/"/>
if you are trying to restrict FileProvider to a subset of getCacheDir().
Related
EDIT: Facebook issue : https://developers.facebook.com/support/bugs/2434932356771915/
I'm trying to share a bitmap to an Instagram story sticker.
But it loads Instagram, but doesn't show any sticker, no background colors, only a black story screen.
What is wrong with this code ? I check if file was in the external data cache directory and that's good.
According to Facebook's Instagram documentation :
I added read/write rights to storage and a provider to my AndroidManifest.xml
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<provider
android:authorities="my.app.id.fileprovider"
android:name="androidx.core.content.FileProvider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="#xml/file_paths" />
</provider>
I added a file_paths.xml to share my external cache folder to other apps
<?xml version="1.0" encoding="utf-8"?>
<resources>
<paths xmlns:android="http://schemas.android.com/apk/res/android">
<external-cache-path name="share" path="."/>
</paths>
</resources>
I create a bitmap from a drawable, write it to a file on the external cache dir, and apply the Intent like example in the docs
public static void sendStickerToInstagram() {
Bitmap bm = BitmapFactory.decodeResource(context.getResources(), R.drawable.sticker);
File extStorageDirectory = context.getExternalCacheDir();
File stickerFile = new File(extStorageDirectory, "sticker.png");
try {
FileOutputStream outStream = new FileOutputStream(stickerFile);
bm.compress(Bitmap.CompressFormat.PNG, 100, outStream);
outStream.flush();
outStream.close();
} catch (IOException e) {
Log.e("TEST", e.getMessage());
}
Uri stickerUri = FileProvider.getUriForFile(activity, "my.app.id.fileprovider", stickerFile);
// Uri stickerUri = Uri.fromFile(stickerFile);
String linkUrl = "https://stackoverflow.com";
Intent intent = new Intent("com.instagram.share.ADD_TO_STORY");
intent.setType("image/*");
intent.putExtra("interactive_asset_uri", stickerUri);
intent.putExtra("content_url", linkUrl);
intent.putExtra("top_background_color", "#33FF33");
intent.putExtra("bottom_background_color", "#FF00FF");
grantUriPermission("com.instagram.android", stickerUri, Intent.FLAG_GRANT_READ_URI_PERMISSION);
if (getPackageManager().resolveActivity(intent, 0) != null) {
startActivityForResult(intent, 0);
}
}
Most strange part is when I test to share my sticker as background, it works, so the file is loaded as espected, with all permissions.
Intent intent = new Intent("com.instagram.share.ADD_TO_STORY");
intent.setDataAndType(stickerUri, "image/*");
intent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
intent.putExtra("content_url", linkUrl);
if (activity.getPackageManager().resolveActivity(intent, 0) != null) {
activity.startActivityForResult(intent, 0);
}
Any idea why I can't share a sticker ?
Don't worry... It's not you. It's Instagram. :P
Their most recent release of Instagram on Android broke the
com.instagram.share.ADD_TO_STORY intent.
Keep an eye on this thread for updates.
https://developers.facebook.com/support/bugs/2434932356771915/
I added a destination file to save my images in app's created folder.
static final File imageRoot = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES), appDirectoryName);
In cropIwa's documentation, it needs destinationUri
Here is what I have done:
cropIwaView.crop(new CropIwaSaveConfig.Builder(Uri.fromFile(imageRoot.getAbsoluteFile()))
.setCompressFormat(Bitmap.CompressFormat.PNG)
.setQuality(100) //Hint for lossy compression formats
.build());
ADDED #Khaled Lela
cropIwaView.crop(new CropIwaSaveConfig.Builder(getUriFromFile(this, new File(R.xml.file_paths + ".png")))
.setCompressFormat(Bitmap.CompressFormat.PNG)
.setQuality(100) //Hint for lossy compression formats
.build());
ADDED #Khaled Lela a saveCompleteListener of cropiwa.
cropIwaView.setCropSaveCompleteListener(new CropIwaView.CropSaveCompleteListener() {
#Override
public void onCroppedRegionSaved(Uri bitmapUri) {
addPicToGallery(CropProfilePicture.this, bitmapUri);
Toast.makeText(CropProfilePicture.this, "Done", Toast.LENGTH_SHORT).show();
finish();
}
});
Create temp file where the image should save
// Create the File where the photo should go
File photoFile = null;
try {
photoFile = createImageFile(context);
} catch (IOException ex) {
// Error occurred while creating the File
Timber.e("Can't create photoFile:%s",ex.getMessage());
}
private static File createImageFile(Context ctx) throws IOException {
// Create an image file name
String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss",Locale.US).format(new Date());
String imageFileName = "JPEG_" + timeStamp + "_";
File storageDir = ctx.getExternalFilesDir(Environment.DIRECTORY_PICTURES);
return File.createTempFile(
imageFileName, /* prefix */
".png", /* suffix */
storageDir /* directory */
);
}
Generate Uri and use FileProvide when version LOLLIPOP or above
final Uri imageUri ;
if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.LOLLIPOP)
imageUri = Uri.fromFile(photoFile); // file://
else
imageUri = getUriFromFile(context,photoFile); // FileProvider
Saving image logic
cropIwaView.setCropSaveCompleteListener(bitmapUri -> {
addPicToGallery(context, bitmapUri); // sendBroadcast to gallery to scan new added images...
});
cropIwaView.crop(new CropIwaSaveConfig.Builder(imageUri)
.setCompressFormat(Bitmap.CompressFormat.PNG)
.setQuality(100) //Hint for lossy compression formats
.build());
Use FileProvider with android version LOLLIPOP and above
private static Uri getUriFromFile(Context context, File newFile) {
return FileProvider.getUriForFile(context, context.getPackageName() + ".fileprovider", newFile);
}
Under app res add file_paths.xml
<?xml version="1.0" encoding="utf-8"?>
<paths>
<external-path name="my_images" path="Android/data/com.your_package_id/files/Pictures" />
</paths>
manifest.xml
<application
...// other attributes
>
<provider
android:name="android.support.v4.content.FileProvider"
android:authorities="com.your_package_id.fileprovider"
android:readPermission="com.your_package_id.fileprovider.READ"
android:grantUriPermissions="true"
android:exported="false">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="#xml/file_paths" />
</provider>
</application>
add permission
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
Update
Update image on gallery.
private static void addPicToGallery(Context context, Uri contentUri) {
Intent mediaScanIntent = new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE);
mediaScanIntent.setData(contentUri);
context.sendBroadcast(mediaScanIntent);
}
I want my users to be able to share an image and select an app to share it to whether its something like their native messenger app, or twitter. Whenever I go to select the app I want to share the image to, I get a message saying "This media can't be loaded" or something like that. Here is the sharing code in BitmapUtils.java
static void shareImage(Context context, String imagePath) {
// Create the share intent and start the share activity
File imageFile = new File(imagePath);
Intent shareIntent = new Intent(Intent.ACTION_SEND);
shareIntent.setType("image/*");
Uri photoURI = FileProvider.getUriForFile(context, FILE_PROVIDER_AUTHORITY, imageFile);
shareIntent.putExtra(Intent.EXTRA_STREAM, photoURI);
context.startActivity(shareIntent);
}
Here is my file provider code in my Manifest file:
<provider
android:name="android.support.v4.content.FileProvider"
android:authorities="com.aaronapp.hideme.fileprovider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="#xml/file_paths" />
Here is the file_paths file which contains the file paths.
<paths xmlns:android="http://schemas.android.com/apk/res/android">
<external-cache-path name="my_cache" path="." />
<external-path name="my_images" path="Pictures/" />
</paths>
Here is the share method that is invoked inside the MainActivity class.
/**
* OnClick method for the share button, saves and shares the new bitmap.
*/
#OnClick(R.id.share_button)
public void shareMe() {
// Delete the temporary image file
BitmapUtils.deleteImageFile(this, mTempPhotoPath);
// Share the image
BitmapUtils.shareImage(this, mTempPhotoPath);
}
If you need anymore information that I forgot to show I'll be happy to supply it. I'm trying to fix this issue and get my images to share to different apps(I know Facebook has a certain way of sharing images, but I will tackle that later)
You can also replicate this issue by downloading the Hide me, Emoji App on the google play store, taking a picture and trying to share it across your apps.https://play.google.com/store/apps/details?id=com.aaronapp.hideme
This will allow you to read files from Internal or external sdcard.
Add this in manifest.xml
<!-- this is For Access External file Storage -->
<provider
android:name="android.support.v4.content.FileProvider"
android:authorities="com.demo.test.provider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="#xml/file_paths"/>
</provider>
file_paths.xml
<?xml version="1.0" encoding="utf-8"?>
<paths xmlns:android="http://schemas.android.com/apk/res/android">
<external-path
name="external_files"
path="." />
<root-path
name="external_files"
path="/storage/"/>
</paths>
Try with adding flag Intent.FLAG_GRANT_READ_URI_PERMISSION to Intent
The file you want to share with another app, you need to allow the client app to access the file. To allow access, grant permissions to the client app by adding the content URI to an Intent and then setting permission flags on the Intent.
// Grant temporary read permission to the content URI
shareIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
The permissions you grant are temporary and expire automatically when the receiving app's task stack is finished.
Please try this code, this is working in my case
File filePath = new File(FIlePath);
Intent intent = new Intent();
intent.setAction(Intent.ACTION_SEND_MULTIPLE);
ArrayList<Uri> uriArrayList = new ArrayList<>();
uriArrayList.add(getUriFromFilePath(filePath));
intent.setType("image/*");
intent.putParcelableArrayListExtra(Intent.EXTRA_STREAM, uriArrayList);
startActivity(intent);
public Uri getUriFromFilePath(Context theCtx, File theSrcPath) {
Uri requirdUri = null;
// observation
// SDKversion: 25 -- Uri.fromFile Not working, So we have to use Provider
// FileProvider.getUriForFile will not work when the file is located in external Sdcard.
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
requirdUri = FileProvider.getUriForFile(theCtx,
theCtx.getApplicationContext().getPackageName() + PROVIDER_FILE_EXTENSION,
theSrcPath);
} else {
requirdUri = Uri.fromFile(theSrcPath);
}
return requirdUri;
}
I have done following changes in your code, i had tested app after
changes and able to share image now.
Please check and let me know.
in MainActivity, shareMe method you have delete the temp file before sharing that why the error was occured.
now i have delete the Temp file after sharing.
Modified code in MainActivity.java
public static final int REQUEST_CODE_SHARE_FILE = 100;
public void shareMe()
{
// BitmapUtils.deleteImageFile(this, mTempPhotoPath); delete temp file in on activity result.
BitmapUtils.shareImage(MainActivity.this, mTempPhotoPath);
}
#Override
protected void onActivityResult(int requestCode, int resultCode, Intent data)
{
//If the image capture activity was called and was successful
if(requestCode == REQUEST_IMAGE_CAPTURE && resultCode == RESULT_OK)
{
processAndSetImage();
}
else if(requestCode == REQUEST_CODE_SHARE_FILE)
{
BitmapUtils.deleteImageFile(this, mTempPhotoPath);
}else {
BitmapUtils.deleteImageFile(this, mTempPhotoPath);
}
}
Modified code in BitmapUtils.java
static void shareImage(Activity activity, String imagePath) {
// Create the share intent and start the share activity
File imageFile = new File(imagePath);
Intent shareIntent = new Intent(Intent.ACTION_SEND);
shareIntent.setType("image/*");
Uri photoURI = FileProvider.getUriForFile(activity, FILE_PROVIDER_AUTHORITY, imageFile);
shareIntent.putExtra(Intent.EXTRA_STREAM, photoURI);
activity.startActivityForResult(shareIntent,MainActivity.REQUEST_CODE_SHARE_FILE);
}
I am testing your app on the device with Android version 6.0 and app gets crashed every time.
Inside detectFacesandOverlayEmoji method, on line number 32(SparseArray faces = detector.detect(frame);)
a) Sometimes app remains open without showing anything in logcat.
b) Sometimes app crashed with following error in logcat.
05-24 11:00:27.192 17880-17880/com.aaronapp.hideme E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.aaronapp.hideme, PID: 17880
java.lang.OutOfMemoryError: Failed to allocate a 51916812 byte allocation with 16765168 free bytes and 36MB until OOM
at com.google.android.gms.vision.Frame.zzTM(Unknown Source)
at com.google.android.gms.vision.Frame.getGrayscaleImageData(Unknown Source)
at com.google.android.gms.vision.face.FaceDetector.detect(Unknown Source)
at com.aaronapp.hideme.HideMe.detectFacesandOverlayEmoji(HideMe.java:32)
at com.aaronapp.hideme.MainActivity.processAndSetImage(MainActivity.java:153)
at com.aaronapp.hideme.MainActivity.onActivityResult(MainActivity.java:133)
at android.app.Activity.dispatchActivityResult(Activity.java:6428)
at android.app.ActivityThread.deliverResults(ActivityThread.java:3695)
at android.app.ActivityThread.handleSendResult(ActivityThread.java:3742)
at android.app.ActivityThread.-wrap16(ActivityThread.java)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1393)
at android.os.Handler.dispatchMessage(Handler.java:102)
at android.os.Looper.loop(Looper.java:148)
at android.app.ActivityThread.main(ActivityThread.java:5417)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:726)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:616)
Make use of Picasso, sharing you a working code from my project.
Put this in your App level gradle,
implementation 'com.squareup.picasso:picasso:2.71828'
Code used is given below,
static void shareImage(Context context, String imagePath) {
// Create the share intent and start the share activity
Picasso.with(getApplicationContext())
.load(imagePath)
.into(new Target() {
#Override
public void onBitmapLoaded(Bitmap bitmap, Picasso.LoadedFrom from) {
Intent i = new Intent(Intent.ACTION_SEND);
i.setType("image/*");
i.putExtra(Intent.EXTRA_STREAM, getLocalBitmapUri(bitmap));
if (!TextUtils.isEmpty(des)) {
i.putExtra(Intent.EXTRA_TEXT, des);
}
startActivity(Intent.createChooser(i, "Share Image"));
}
#Override
public void onBitmapFailed(Drawable errorDrawable) {
}
#Override
public void onPrepareLoad(Drawable placeHolderDrawable) {
}
});
}
public Uri getLocalBitmapUri(Bitmap bmp) {
Uri bmpUri = null;
try {
File file = new File(getExternalFilesDir(Environment.DIRECTORY_PICTURES),
"share_image_" + System.currentTimeMillis() + ".png");
FileOutputStream out = new FileOutputStream(file);
bmp.compress(Bitmap.CompressFormat.PNG, 90, out);
out.close();
bmpUri = Uri.fromFile(file);
} catch (IOException e) {
e.printStackTrace();
}
return bmpUri;
}
Hope it may help you.
I have an android app where I take a photo from camera and then I would like to convert it to the base64 format but I have an error after accepting the taken photo. It seems like I don't have access to the external storage and I think this leads to a null bitmap. THE PERMISSIONS IN THE ANDROID_MANIFEST HAVE BEEN DECLARED. How can I resolve the permission problem and what may lead to a null bitmap?
here is my code:
public class MainActivity extends AppCompatActivity {
private Button takePhotoButton;
private String encoded_string, image_name;
private Bitmap bitmap;
private File file;
private Uri file_uri;
private String getRealPathFromURI(Uri contentUri) {
String[] proj = { MediaStore.Images.Media.DATA };
CursorLoader loader = new CursorLoader(this, contentUri, proj, null, null, null);
Cursor cursor = loader.loadInBackground();
int column_index = cursor.getColumnIndexOrThrow(MediaStore.Images.Media.DATA);
cursor.moveToFirst();
String result = cursor.getString(column_index);
cursor.close();
return result;
}
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
takePhotoButton = (Button) findViewById(R.id.take_photo);
takePhotoButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
Intent i = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
getFileUri();
i.putExtra(MediaStore.EXTRA_OUTPUT, file_uri);
startActivityForResult(i, 10);
}
});
}
private void getFileUri() {
image_name= "test123.jpg";
file = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES) + File.separator + image_name);
file_uri = Uri.fromFile(file);
}
#Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if(requestCode == 10 && resultCode == RESULT_OK){
new Encode_image().execute();
}
}
private class Encode_image extends AsyncTask<Void, Void, Void>{
#Override
protected Void doInBackground(Void... params) {
BitmapFactory.Options options = new BitmapFactory.Options();
int scale = 8;
options.inSampleSize = scale;
options.inJustDecodeBounds = false;
bitmap = BitmapFactory.decodeFile(file_uri.getPath(), options);
ByteArrayOutputStream stream = new ByteArrayOutputStream();
bitmap.compress(Bitmap.CompressFormat.JPEG, 50, stream);
//convert stream to byte array
byte[] array = stream.toByteArray();
encoded_string = Base64.encodeToString(array, 0);
return null;
}
#Override
protected void onPostExecute(Void aVoid) {
makeRequest();
}
}
private void makeRequest() {
RequestQueue requestQueue = Volley.newRequestQueue(this);
StringRequest request = new StringRequest(Request.Method.POST, "http://192.168.1.102/AndroidFileUpload/connection.php",
new Response.Listener<String>() {
#Override
public void onResponse(String response) {
}
}, new Response.ErrorListener() {
#Override
public void onErrorResponse(VolleyError error) {
}
}) {
#Override
protected Map<String, String> getParams() throws AuthFailureError {
HashMap<String, String> map = new HashMap<>();
map.put("encoded_string", encoded_string);
map.put("image_name", image_name);
return map;
}
};
requestQueue.add(request);
}
}
I get the error in the line
bitmap.compress(Bitmap.CompressFormat.JPEG, 50, stream)
because the bitmap is null.
The crash after running the app returned this errors/messages:
E/BitmapFactory: Unable to decode stream: java.io.FileNotFoundException: /storage/emulated/0/Pictures/test123.jpg: open failed: EACCES (Permission denied)
E/AndroidRuntime: FATAL EXCEPTION: AsyncTask #1
Process: com.example.andrei.sendphotos, PID: 17972
java.lang.RuntimeException: An error occurred while executing doInBackground()
at android.os.AsyncTask$3.done(AsyncTask.java:309)
at java.util.concurrent.FutureTask.finishCompletion(FutureTask.java:354)
at java.util.concurrent.FutureTask.setException(FutureTask.java:223)
at java.util.concurrent.FutureTask.run(FutureTask.java:242)
at android.os.AsyncTask$SerialExecutor$1.run(AsyncTask.java:234)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1113)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:588)
at java.lang.Thread.run(Thread.java:818)
Caused by: java.lang.NullPointerException: Attempt to invoke virtual method 'boolean android.graphics.Bitmap.compress(android.graphics.Bitmap$CompressFormat, int, java.io.OutputStream)' on a null object reference
at com.example.andrei.sendphotos.MainActivity$Encode_image.doInBackground(MainActivity.java:115)
at com.example.andrei.sendphotos.MainActivity$Encode_image.doInBackground(MainActivity.java:92)
at android.os.AsyncTask$2.call(AsyncTask.java:295)
at java.util.concurrent.FutureTask.run(FutureTask.java:237)
at android.os.AsyncTask$SerialExecutor$1.run(AsyncTask.java:234)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1113)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:588)
at java.lang.Thread.run(Thread.java:818)
E/Surface: getSlotFromBufferLocked: unknown buffer: 0xa23ae0e0
Android Manifest xml:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.andrei.sendphotos">
<uses-permission android:name="android.permission.INTERNET" />
<uses-feature android:name="android.hardware.camera" android:required="true" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<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">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
You need to implement run-time permissions for Android 6.X. Refer the official documentation here: Requesting Runtime Permissions
There are public helper classes available like one of my own here: PermissionHelper.java
Or you could simply downgrade your target SDK version to 22 and all the dependencies too, which I wouldn't recommend.
One of the major changes in Android 6.X was the enforced run-time permission model. Google had to keep backward compatibility for the applications already in the market (which don't check and ask for permissions on run-time).
So they made it work like - if the application targets SDK version 22, all the permissions will be granted to the application on installation. But if an application targets SDK 23, it will not be granted permissions by default, rather the application should check for permissions before performing any task that needs dangerous permissions and ask the user for it if not already granted before continuing the same.
I have an Android app that writes .txt files to the downloads folder based off your inputs, a listview that displays all your downloads and lets you click to view them (I have this working), and I'm now trying to code a way to upload them to Google Drive. I have done the developer's verification process with the SHA1 key so it should be fine as far as that goes. I often see this demo app that takes pictures and uploads them being recommended and it looks like a good code to start with and modify, but when I run it on my phone, it doesn't work-it just repeatedly asks me to select my account endlessly. The java code inside the project itself is this (I'm pretty new and don't quite understand how all of it works, but this is for the google demo in the link):
public class MainActivity extends Activity implements ConnectionCallbacks,
OnConnectionFailedListener {
private static final String TAG = "drive-quickstart";
private static final int REQUEST_CODE_CAPTURE_IMAGE = 1;
private static final int REQUEST_CODE_CREATOR = 2;
private static final int REQUEST_CODE_RESOLUTION = 3;
private GoogleApiClient mGoogleApiClient;
private Bitmap mBitmapToSave;
/**
* Create a new file and save it to Drive.
*/
private void saveFileToDrive() {
// Start by creating a new contents, and setting a callback.
Log.i(TAG, "Creating new contents.");
final Bitmap image = mBitmapToSave;
Drive.DriveApi.newDriveContents(mGoogleApiClient)
.setResultCallback(new ResultCallback<DriveContentsResult>() {
#Override
public void onResult(DriveContentsResult result) {
// If the operation was not successful, we cannot do anything
// and must
// fail.
if (!result.getStatus().isSuccess()) {
Log.i(TAG, "Failed to create new contents.");
return;
}
// Otherwise, we can write our data to the new contents.
Log.i(TAG, "New contents created.");
// Get an output stream for the contents.
OutputStream outputStream = result.getDriveContents().getOutputStream();
// Write the bitmap data from it.
ByteArrayOutputStream bitmapStream = new ByteArrayOutputStream();
image.compress(Bitmap.CompressFormat.PNG, 100, bitmapStream);
try {
outputStream.write(bitmapStream.toByteArray());
} catch (IOException e1) {
Log.i(TAG, "Unable to write file contents.");
}
// Create the initial metadata - MIME type and title.
// Note that the user will be able to change the title later.
MetadataChangeSet metadataChangeSet = new MetadataChangeSet.Builder()
.setMimeType("image/jpeg").setTitle("Android Photo.png").build();
// Create an intent for the file chooser, and start it.
IntentSender intentSender = Drive.DriveApi
.newCreateFileActivityBuilder()
.setInitialMetadata(metadataChangeSet)
.setInitialDriveContents(result.getDriveContents())
.build(mGoogleApiClient);
try {
startIntentSenderForResult(
intentSender, REQUEST_CODE_CREATOR, null, 0, 0, 0);
} catch (SendIntentException e) {
Log.i(TAG, "Failed to launch file chooser.");
}
}
});
}
#Override
protected void onResume() {
super.onResume();
if (mGoogleApiClient == null) {
// Create the API client and bind it to an instance variable.
// We use this instance as the callback for connection and connection
// failures.
// Since no account name is passed, the user is prompted to choose.
mGoogleApiClient = new GoogleApiClient.Builder(this)
.addApi(Drive.API)
.addScope(Drive.SCOPE_FILE)
.addConnectionCallbacks(this)
.addOnConnectionFailedListener(this)
.build();
}
// Connect the client. Once connected, the camera is launched.
mGoogleApiClient.connect();
}
#Override
protected void onPause() {
if (mGoogleApiClient != null) {
mGoogleApiClient.disconnect();
}
super.onPause();
}
#Override
protected void onActivityResult(final int requestCode, final int resultCode, final Intent data) {
switch (requestCode) {
case REQUEST_CODE_CAPTURE_IMAGE:
// Called after a photo has been taken.
if (resultCode == Activity.RESULT_OK) {
// Store the image data as a bitmap for writing later.
mBitmapToSave = (Bitmap) data.getExtras().get("data");
}
break;
case REQUEST_CODE_CREATOR:
// Called after a file is saved to Drive.
if (resultCode == RESULT_OK) {
Log.i(TAG, "Image successfully saved.");
mBitmapToSave = null;
// Just start the camera again for another photo.
startActivityForResult(new Intent(MediaStore.ACTION_IMAGE_CAPTURE),
REQUEST_CODE_CAPTURE_IMAGE);
}
break;
}
}
#Override
public void onConnectionFailed(ConnectionResult result) {
// Called whenever the API client fails to connect.
Log.i(TAG, "GoogleApiClient connection failed: " + result.toString());
if (!result.hasResolution()) {
// show the localized error dialog.
GoogleApiAvailability.getInstance().getErrorDialog(this, result.getErrorCode(), 0).show();
return;
}
// The failure has a resolution. Resolve it.
// Called typically when the app is not yet authorized, and an
// authorization
// dialog is displayed to the user.
try {
result.startResolutionForResult(this, REQUEST_CODE_RESOLUTION);
} catch (SendIntentException e) {
Log.e(TAG, "Exception while starting resolution activity", e);
}
}
#Override
public void onConnected(Bundle connectionHint) {
Log.i(TAG, "API client connected.");
if (mBitmapToSave == null) {
// This activity has no UI of its own. Just start the camera.
startActivityForResult(new Intent(MediaStore.ACTION_IMAGE_CAPTURE),
REQUEST_CODE_CAPTURE_IMAGE);
return;
}
saveFileToDrive();
}
#Override
public void onConnectionSuspended(int cause) {
Log.i(TAG, "GoogleApiClient connection suspended");
}
}
And in the manifest:
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.google.android.gms.drive.sample.quickstart"
android:versionCode="1"
android:versionName="1.0" >
<uses-sdk
android:minSdkVersion="8"
android:targetSdkVersion="18" />
<application
android:allowBackup="true"
android:icon="#drawable/ic_launcher"
android:label="#string/app_name"
android:theme="#style/AppTheme" >
<activity
android:name="com.google.android.gms.drive.sample.quickstart.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>
<meta-data
android:name="com.google.android.gms.version"
android:value="#integer/google_play_services_version" />
</application>
</manifest>
And with that, my main two questions are:
Any idea why the quick start project from Google is behaving the way it is?
If it's not too much, but what parts of the java code do I need to modify to make it upload a .txt file after pressing a button?
You are supposed to create a project in Google Api COnsole,then enable Drive API. Then create required credentials(SHA-1, key, CLIENT-ID) for the the android app to connect with drive api.
After getting all the credentials, include them in Manifest file and String File.
Follow the instructions here, QuickStart Android Instructions