Skip to content

Commit

Permalink
Merge pull request #7 from seniorquico/sharing
Browse files Browse the repository at this point in the history
Added share image/text functionality
  • Loading branch information
seniorquico authored Mar 27, 2018
2 parents 28842f7 + 2791e75 commit 9addf8a
Show file tree
Hide file tree
Showing 6 changed files with 213 additions and 54 deletions.
2 changes: 1 addition & 1 deletion zapic/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/provider_paths" />
android:resource="@xml/zapic_provider_paths" />
</provider>
</application>
</manifest>
Original file line number Diff line number Diff line change
Expand Up @@ -270,7 +270,7 @@ private AppSource injectInitializationScript(@NonNull final AppSource appSource)
final String script = "<script>" +
"window.zapic = {" +
" environment: 'webview'," +
" version: 1," +
" version: 2," +
" androidVersion: '" + String.valueOf(Build.VERSION.SDK_INT).replace("'", "\\'") + "'," +
" onLoaded: function (action$, publishAction) {" +
" window.zapic.dispatch = function (action) {" +
Expand Down
Original file line number Diff line number Diff line change
@@ -1,24 +1,35 @@
package com.zapic.sdk.android;

import android.content.Context;
import android.content.Intent;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.net.Uri;
import android.os.Handler;
import android.os.Handler.Callback;
import android.os.Message;
import android.support.annotation.MainThread;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.annotation.WorkerThread;
import android.support.v4.content.FileProvider;
import android.util.Base64;
import android.util.Log;
import android.webkit.JavascriptInterface;
import android.webkit.MimeTypeMap;
import android.webkit.WebView;

import org.json.JSONException;
import org.json.JSONObject;

import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;

final class WebViewJavascriptInterface implements Callback {
Expand Down Expand Up @@ -57,6 +68,11 @@ final class WebViewJavascriptInterface implements Callback {
*/
private static final int SHOW_BANNER = 1006;

/**
* Identifies the "SHOW_SHARE_MENU" action type.
*/
private static final int SHOW_SHARE_MENU = 1007;

/**
* The tag used to identify log messages.
*/
Expand Down Expand Up @@ -145,11 +161,52 @@ public void dispatch(@Nullable final String message) {
case "SHOW_BANNER":
this.onShowBannerDispatched(action);
break;
case "SHOW_SHARE_MENU":
this.onShowShareMenuDispatched(action);
break;
default:
break;
}
}

@Override
public boolean handleMessage(@Nullable final Message msg) {
if (msg == null) {
return false;
}

@SuppressWarnings("unchecked") final Map<String, Object> args = (Map<String, Object>) msg.obj;
switch (msg.what) {
case APP_LOADED:
this.onAppLoadedHandled();
break;
case APP_STARTED:
this.onAppStartedHandled();
break;
case CLOSE_PAGE_REQUESTED:
this.onClosePageRequestedHandled();
break;
case LOGIN:
this.onLoginHandled();
break;
case LOGOUT:
this.onLogoutHandled();
break;
case PAGE_READY:
this.onPageReadyHandled();
break;
case SHOW_BANNER:
this.onShowBannerHandled(args);
break;
case SHOW_SHARE_MENU:
this.onShowShareMenuHandled(args);
default:
break;
}

return true;
}

@WorkerThread
private void onAppLoadedDispatched() {
this.mHandler.obtainMessage(APP_LOADED).sendToTarget();
Expand Down Expand Up @@ -272,8 +329,8 @@ private void onShowBannerDispatched(@NonNull final JSONObject action) {
icon = null;
} else {
try {
byte[] imageBytes = Base64.decode(encodedIcon, Base64.DEFAULT);
icon = BitmapFactory.decodeByteArray(imageBytes, 0, imageBytes.length);
byte[] iconBytes = Base64.decode(encodedIcon, Base64.DEFAULT);
icon = BitmapFactory.decodeByteArray(iconBytes, 0, iconBytes.length);
} catch (IllegalArgumentException e) {
if (BuildConfig.DEBUG) {
Log.e(TAG, "Failed to parse icon", e);
Expand All @@ -300,39 +357,132 @@ private void onShowBannerHandled(Map<String, Object> args) {
NotificationUtilities.showBanner(this.mContext, (String) args.get("title"), (String) args.get("subtitle"), (Bitmap) args.get("icon"));
}

@Override
public boolean handleMessage(@Nullable final Message msg) {
if (msg == null) {
return false;
@WorkerThread
private void onShowShareMenuDispatched(@NonNull final JSONObject action) {
Map<String, Object> args = new HashMap<>();
String text;
String url;
String encodedImage;
try {
final JSONObject payload = action.getJSONObject("payload");
text = payload.optString("text");
url = payload.optString("url");
encodedImage = payload.optString("image");
} catch (JSONException e) {
// TODO: Send an error to the JavaScript application.
return;
}

@SuppressWarnings("unchecked") final Map<String, Object> args = (Map<String, Object>) msg.obj;
switch (msg.what) {
case APP_LOADED:
this.onAppLoadedHandled();
break;
case APP_STARTED:
this.onAppStartedHandled();
break;
case CLOSE_PAGE_REQUESTED:
this.onClosePageRequestedHandled();
break;
case LOGIN:
this.onLoginHandled();
break;
case LOGOUT:
this.onLogoutHandled();
break;
case PAGE_READY:
this.onPageReadyHandled();
break;
case SHOW_BANNER:
this.onShowBannerHandled(args);
break;
default:
break;
if (!text.equals("")) {
args.put("text", text);
}

return true;
if (!url.equals("")) {
args.put("url", url);
}

if (!encodedImage.equals("")) {
byte[] imageBytes = Base64.decode(encodedImage, Base64.DEFAULT);
String imageMimeType;
try {
// Get the mime type without allocating memory for the pixels.
final BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeByteArray(imageBytes, 0, imageBytes.length, options);
imageMimeType = options.outMimeType;
} catch (IllegalArgumentException e) {
if (BuildConfig.DEBUG) {
Log.e(TAG, "Failed to parse image", e);
}

imageMimeType = null;
}

File imageFile;
if (imageMimeType != null) {
final File filesDir = this.mContext.getFilesDir();
if (filesDir == null) {
imageFile = null;
} else {
final File zapicDir = new File(filesDir.getAbsolutePath() + File.separator + "Zapic");
if (!zapicDir.isDirectory() && !zapicDir.mkdirs()) {
imageFile = null;
} else {
String imageFileExtension = MimeTypeMap.getSingleton().getExtensionFromMimeType(imageMimeType);
if (imageFileExtension == null) {
imageFileExtension = "file";
}

imageFile = new File(zapicDir.getAbsolutePath() + File.separator + "IMG_" + new SimpleDateFormat("yyyyMMdd_HHmmss", Locale.US).format(new Date()) + "." + imageFileExtension);
}
}
} else {
imageFile = null;
}

Uri imageUri;
if (imageFile != null) {
try {
BufferedOutputStream outputStream = new BufferedOutputStream(new FileOutputStream(imageFile));
outputStream.write(imageBytes);
outputStream.flush();
outputStream.close();

final String packageName = this.mContext.getPackageName();
imageUri = FileProvider.getUriForFile(this.mContext, packageName + ".zapic", imageFile);
} catch (IllegalArgumentException | IOException e) {
imageUri = null;
}
} else {
imageUri = null;
}

if (imageUri != null) {
args.put("imageMimeType", imageMimeType);
args.put("imageUri", imageUri);
}
}

if (args.size() > 0) {
this.mHandler.obtainMessage(SHOW_SHARE_MENU, args).sendToTarget();
}
}

@MainThread
private void onShowShareMenuHandled(Map<String, Object> args) {
final WebViewManager webViewManager = WebViewManager.getInstance();
final ZapicActivity activity = webViewManager.getActivity();
if (activity == null) {
return;
}

final String text = args.containsKey("text") ? (String) args.get("text") : null;
final String url = args.containsKey("url") ? (String) args.get("url") : null;
final String message = text != null && url != null
? text + " " + url
: text != null ? text : url;

final String imageMimeType = args.containsKey("imageMimeType") ? (String) args.get("imageMimeType") : null;
final Uri imageUri = args.containsKey("imageUri") ? (Uri) args.get("imageUri") : null;

Intent intent;
if (imageUri != null && imageMimeType != null) {
intent = new Intent(Intent.ACTION_SEND);
intent.setType(imageMimeType);
intent.putExtra(Intent.EXTRA_STREAM, imageUri);
if (message != null) {
intent.putExtra(Intent.EXTRA_TEXT, message);
}
} else if (message != null) {
intent = new Intent(Intent.ACTION_SEND);
intent.setType("text/plain");
intent.putExtra(Intent.EXTRA_TEXT, message);
} else {
intent = null;
}

if (intent != null) {
activity.startActivity(Intent.createChooser(intent, "Share"));
}
}
}
37 changes: 23 additions & 14 deletions zapic/src/main/java/com/zapic/sdk/android/ZapicActivity.java
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.os.Environment;
import android.provider.MediaStore;
import android.provider.Settings;
import android.support.annotation.CheckResult;
Expand Down Expand Up @@ -471,24 +470,34 @@ public void onClick(@NonNull final DialogInterface dialog, final int which) {
}

private void startImageCameraActivity() {
final File filesDir = this.getApplicationContext().getExternalFilesDir(Environment.DIRECTORY_PICTURES);
final File filesDir = this.getApplicationContext().getFilesDir();
if (filesDir == null) {
assert this.mWebViewManager != null : "mWebViewManager is null";
this.mWebViewManager.cancelImageUpload();

Toast.makeText(this.getApplicationContext(), R.string.zapic_activity_folder_error, Toast.LENGTH_SHORT).show();
} else {
this.mImageCameraFile = new File(filesDir.getAbsolutePath() + File.separator + "IMG_" + new SimpleDateFormat("yyyyMMdd_HHmmss", Locale.US).format(new Date()) + ".jpg");
final Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
intent.putExtra(MediaStore.EXTRA_OUTPUT, FileProvider.getUriForFile(this.getApplicationContext(), this.getApplicationContext().getPackageName() + ".zapic", this.mImageCameraFile));
try {
this.startActivityForResult(intent, IMAGE_CAMERA_REQUEST);
} catch (ActivityNotFoundException e) {
assert this.mWebViewManager != null : "mWebViewManager is null";
this.mWebViewManager.cancelImageUpload();

Toast.makeText(this.getApplicationContext(), R.string.zapic_activity_camera_error, Toast.LENGTH_SHORT).show();
}
return;
}

final File zapicDir = new File(filesDir.getAbsolutePath() + File.separator + "Zapic");
if (!zapicDir.isDirectory() && !zapicDir.mkdirs()) {
assert this.mWebViewManager != null : "mWebViewManager is null";
this.mWebViewManager.cancelImageUpload();

Toast.makeText(this.getApplicationContext(), R.string.zapic_activity_folder_error, Toast.LENGTH_SHORT).show();
return;
}

this.mImageCameraFile = new File(zapicDir.getAbsolutePath() + File.separator + "IMG_" + new SimpleDateFormat("yyyyMMdd_HHmmss", Locale.US).format(new Date()) + ".jpg");
final Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
intent.putExtra(MediaStore.EXTRA_OUTPUT, FileProvider.getUriForFile(this.getApplicationContext(), this.getApplicationContext().getPackageName() + ".zapic", this.mImageCameraFile));
try {
this.startActivityForResult(intent, IMAGE_CAMERA_REQUEST);
} catch (ActivityNotFoundException e) {
assert this.mWebViewManager != null : "mWebViewManager is null";
this.mWebViewManager.cancelImageUpload();

Toast.makeText(this.getApplicationContext(), R.string.zapic_activity_camera_error, Toast.LENGTH_SHORT).show();
}
}

Expand Down
6 changes: 0 additions & 6 deletions zapic/src/main/res/xml/provider_paths.xml

This file was deleted.

6 changes: 6 additions & 0 deletions zapic/src/main/res/xml/zapic_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>
<files-path
name="zapic_files"
path="Zapic/" />
</paths>

0 comments on commit 9addf8a

Please sign in to comment.