Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: share image directly via share menu item #35

Closed
wants to merge 3 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 0 additions & 17 deletions app/app.iml
Original file line number Diff line number Diff line change
Expand Up @@ -63,13 +63,6 @@
<sourceFolder url="file://$MODULE_DIR$/src/main/java" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/src/main/rs" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/src/main/shaders" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/src/test/res" type="java-test-resource" />
<sourceFolder url="file://$MODULE_DIR$/src/test/resources" type="java-test-resource" />
<sourceFolder url="file://$MODULE_DIR$/src/test/assets" type="java-test-resource" />
<sourceFolder url="file://$MODULE_DIR$/src/test/aidl" isTestSource="true" />
<sourceFolder url="file://$MODULE_DIR$/src/test/java" isTestSource="true" />
<sourceFolder url="file://$MODULE_DIR$/src/test/rs" isTestSource="true" />
<sourceFolder url="file://$MODULE_DIR$/src/test/shaders" isTestSource="true" />
<sourceFolder url="file://$MODULE_DIR$/src/androidTest/res" type="java-test-resource" />
<sourceFolder url="file://$MODULE_DIR$/src/androidTest/resources" type="java-test-resource" />
<sourceFolder url="file://$MODULE_DIR$/src/androidTest/assets" type="java-test-resource" />
Expand All @@ -80,24 +73,14 @@
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/assets" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/blame" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/classes" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/dependency-cache" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/incremental" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/incremental-classes" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/incremental-runtime-classes" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/incremental-safeguard" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/incremental-verifier" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/instant-run-resources" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/instant-run-support" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/jniLibs" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/manifests" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/pre-dexed" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/proguard-rules" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/reload-dex" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/res" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/restart-dex" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/rs" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/shaders" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/sourceFolderJavaResources" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/symbols" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/transforms" />
<excludeFolder url="file://$MODULE_DIR$/build/outputs" />
Expand Down
17 changes: 17 additions & 0 deletions app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,23 @@
<meta-data android:name="android.content.SyncAdapter"
android:resource="@xml/syncadapter" />
</service>

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could this have a comment saying why it's here, please. For instance, saying that it's here for this particular feature, and what would happen if it wasn't here.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done!

<!--Generates a FileProvider class for the app with the appropriate authority.
This allows the system to securely share files by creating content URI for files rather than file URI,
thus granting temporary file access permissions to sharing apps.
This is required for devices with Android M or above, while it wasn't necessary before. Without a file provider class,
the OS will deny other apps the permission to access GalaxyZoo's files, and sharing locally stored images will be impossible-->
<provider
android:name="android.support.v4.content.FileProvider"
android:authorities="@string/authority_fileprovider"
android:exported="false"
android:grantUriPermissions="true">
<!--This meta data and the associated XML file specify that the files will be written to the root folder
of the app's external storage area-->
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/provider_paths"/>
</provider>
</application>

</manifest>
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
import android.database.Cursor;
import android.net.Uri;
import android.os.Bundle;
import android.support.annotation.Nullable;
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I doubt that this change needs to be in the commit.

import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentTransaction;
Expand Down
86 changes: 85 additions & 1 deletion app/src/main/java/com/murrayc/galaxyzoo/app/SubjectFragment.java
Original file line number Diff line number Diff line change
Expand Up @@ -19,15 +19,23 @@

package com.murrayc.galaxyzoo.app;

import android.*;
import android.Manifest;
import android.app.Activity;
import android.app.DownloadManager;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.database.Cursor;
import android.graphics.Bitmap;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.Bundle;
import android.os.Environment;
import android.support.v4.app.ActivityCompat;
import android.support.v4.app.LoaderManager;
import android.support.v4.content.CursorLoader;
import android.support.v4.content.FileProvider;
import android.support.v4.content.Loader;
import android.support.v4.view.MenuItemCompat;
import android.support.v7.widget.ShareActionProvider;
Expand All @@ -45,6 +53,11 @@
import com.squareup.picasso.Callback;
import com.squareup.picasso.Picasso;

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;

/**
* A fragment representing a single subject.
* This fragment is either contained in a {@link ListActivity}
Expand Down Expand Up @@ -406,6 +419,9 @@ public void onPause() {
}

private void updateShareActionIntent() {
/**
* Initialization and setup of the share intent is done here so that less work is left after the AsyncTask's execution
*/
if (mShareActionProvider == null) {
Log.error("updateShareActionIntent(): mShareActionProvider is null.");
return;
Expand All @@ -427,7 +443,75 @@ private void updateShareActionIntent() {
//shareIntent.putExtra(Intent.EXTRA_STREAM, mUriImageStandard);
//shareIntent.setType("image/*");

/**
* if the image URI is not null, a GetImageBitmapAsyncTask is executed for it, and the returned URI is set as
* a stream extra on the intent that will be shared, along with an explicit permission for recieving contexts to
* read the content URI, enabling them to access the generated image.
*/
if (mUriStandardRemote!=null) {
GetImageBitmapAsyncTask getImageBitmapAsyncTask = new GetImageBitmapAsyncTask(){
@Override
protected void onPostExecute(Uri uri) {
shareIntent.setType("image/*");
shareIntent.putExtra(Intent.EXTRA_STREAM, uri);
shareIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
mShareActionProvider.setShareIntent(shareIntent);
}
};
getImageBitmapAsyncTask.execute(mUriStandardRemote);
}

}

/**
*These constants and the verifyStoragePermissions method will gain explicit permission from users to read and write
* files on their devices. This will allow us to save an image, that can then be shared to other apps.
*/
private static final int REQUEST_EXTERNAL_STORAGE = 1;
private static String[] PERMISSIONS_STORAGE = {
Manifest.permission.WRITE_EXTERNAL_STORAGE,
Manifest.permission.READ_EXTERNAL_STORAGE
};

public static void verifyStoragePermissions(Activity activity) {
int permission = ActivityCompat.checkSelfPermission(activity, Manifest.permission.WRITE_EXTERNAL_STORAGE);

if (permission != PackageManager.PERMISSION_GRANTED) {
ActivityCompat.requestPermissions(
activity,
PERMISSIONS_STORAGE,
REQUEST_EXTERNAL_STORAGE
);
}
}

mShareActionProvider.setShareIntent(shareIntent);
/**
* This AsyncTask, when executed, takes a String that represents a URI, creates the appropriate Bitmap, saves it to
* the external storage area of the app as a jpg, and returns the content Uri of that file as generated by the FileProvider class.
* Keeping a single file name ensures that future tasks will write over images previously generated by this task instead
* of creating new ones.
*/
private class GetImageBitmapAsyncTask extends AsyncTask<String, Integer, Uri> {
@Override
protected Uri doInBackground(String... params) {
try {
Bitmap image = Picasso.with(getContext()).load(params[0]).get();
ByteArrayOutputStream bytes = new ByteArrayOutputStream();
image.compress(Bitmap.CompressFormat.JPEG, 100, bytes);

String filename = "galaxy_zoo_image.jpg";

String pathname = Environment.getExternalStorageDirectory() + File.separator + filename;
File f = new File(pathname);
f.createNewFile();
FileOutputStream fo = new FileOutputStream(f);
fo.write(bytes.toByteArray());
return FileProvider.getUriForFile(getActivity(), getString(R.string.authority_fileprovider), f);
} catch (IOException e) {
verifyStoragePermissions(getActivity());
e.printStackTrace();
}
return null;
}
}
}
3 changes: 3 additions & 0 deletions app/src/main/res/values/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -186,6 +186,9 @@
//when specifying a transition when starting an activity that contains this SubjectFragment.
<string name="transition_subject_image" translatable="false">transition_subject_image</string>

<!--share image strings-->
<string name="authority_fileprovider">com.murrayc.galaxyzoo.app.fileprovider</string>

<!-- The possible numbers of items that may be downloaded in advance before they are needed. -->
<string-array name="pref_cache_size_entries">
<item>5 subjects</item>
Expand Down
6 changes: 6 additions & 0 deletions app/src/main/res/xml/provider_paths.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<paths xmlns:android="http://schemas.android.com/apk/res/android">
<!--The FileProvider class will draw from the root directory of the app's external storage area (specified by path="."),
and the generated content URI will indicate that this directory is called "external_files" for security reasons.-->
<external-path name="external_files" path="."/>
</paths>