refactor
parent
c9a9e4379b
commit
37415f7dd5
|
@ -1,12 +1,6 @@
|
||||||
package gallery.memories;
|
package gallery.memories;
|
||||||
|
|
||||||
import android.content.ContentUris;
|
|
||||||
import android.database.Cursor;
|
|
||||||
import android.graphics.Bitmap;
|
|
||||||
import android.net.Uri;
|
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.provider.MediaStore;
|
|
||||||
import android.util.Log;
|
|
||||||
import android.webkit.JavascriptInterface;
|
import android.webkit.JavascriptInterface;
|
||||||
import android.webkit.WebResourceRequest;
|
import android.webkit.WebResourceRequest;
|
||||||
import android.webkit.WebSettings;
|
import android.webkit.WebSettings;
|
||||||
|
@ -15,22 +9,18 @@ import android.webkit.WebViewClient;
|
||||||
|
|
||||||
import androidx.appcompat.app.AppCompatActivity;
|
import androidx.appcompat.app.AppCompatActivity;
|
||||||
|
|
||||||
import org.json.JSONArray;
|
|
||||||
import org.json.JSONException;
|
|
||||||
import org.json.JSONObject;
|
|
||||||
|
|
||||||
import java.io.ByteArrayOutputStream;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Base64;
|
|
||||||
|
|
||||||
import gallery.memories.databinding.ActivityMainBinding;
|
import gallery.memories.databinding.ActivityMainBinding;
|
||||||
|
import gallery.memories.service.ImageService;
|
||||||
|
import gallery.memories.service.JsService;
|
||||||
|
import gallery.memories.service.TimelineQuery;
|
||||||
|
|
||||||
public class MainActivity extends AppCompatActivity {
|
public class MainActivity extends AppCompatActivity {
|
||||||
|
|
||||||
private ActivityMainBinding binding;
|
|
||||||
|
|
||||||
public static final String TAG = "memories-native";
|
public static final String TAG = "memories-native";
|
||||||
|
protected ActivityMainBinding binding;
|
||||||
|
|
||||||
|
protected JsService mJsService;
|
||||||
|
protected ImageService mImageService;
|
||||||
|
protected TimelineQuery mQuery;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onCreate(Bundle savedInstanceState) {
|
protected void onCreate(Bundle savedInstanceState) {
|
||||||
|
@ -39,16 +29,18 @@ public class MainActivity extends AppCompatActivity {
|
||||||
binding = ActivityMainBinding.inflate(getLayoutInflater());
|
binding = ActivityMainBinding.inflate(getLayoutInflater());
|
||||||
setContentView(binding.getRoot());
|
setContentView(binding.getRoot());
|
||||||
|
|
||||||
|
mJsService = new JsService(this, binding.webview);
|
||||||
|
mImageService = new ImageService(this);
|
||||||
|
mQuery = new TimelineQuery(this);
|
||||||
|
|
||||||
initWebview();
|
initWebview();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void initWebview() {
|
protected void initWebview() {
|
||||||
binding.webview.setWebViewClient(new WebViewClient() {
|
binding.webview.setWebViewClient(new WebViewClient() {
|
||||||
public boolean shouldOverrideUrlLoading(WebView view, WebResourceRequest request) {
|
public boolean shouldOverrideUrlLoading(WebView view, WebResourceRequest request) {
|
||||||
// do your handling codes here, which url is the requested url
|
|
||||||
// probably you need to open that url rather than redirect:
|
|
||||||
view.loadUrl(request.getUrl().toString());
|
view.loadUrl(request.getUrl().toString());
|
||||||
return false; // then it is not handled by default action
|
return false;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -71,132 +63,11 @@ public class MainActivity extends AppCompatActivity {
|
||||||
|
|
||||||
@JavascriptInterface
|
@JavascriptInterface
|
||||||
public void getLocalByDayId(final String call, final long dayId) {
|
public void getLocalByDayId(final String call, final long dayId) {
|
||||||
Log.d(TAG, "getLocalByDayId: " + dayId);
|
mJsService.runAsync(call, () -> mQuery.getByDayId(dayId).toString().getBytes());
|
||||||
|
|
||||||
new Thread(() -> {
|
|
||||||
Uri collection = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
|
|
||||||
|
|
||||||
String[] projection = new String[] {
|
|
||||||
MediaStore.Images.Media._ID,
|
|
||||||
MediaStore.Images.Media.DISPLAY_NAME,
|
|
||||||
MediaStore.Images.Media.MIME_TYPE,
|
|
||||||
MediaStore.Images.Media.DATE_TAKEN,
|
|
||||||
MediaStore.Images.Media.HEIGHT,
|
|
||||||
MediaStore.Images.Media.WIDTH,
|
|
||||||
MediaStore.Images.Media.SIZE,
|
|
||||||
};
|
|
||||||
String selection = MediaStore.Images.Media.DATE_TAKEN + " >= ? AND "
|
|
||||||
+ MediaStore.Images.Media.DATE_TAKEN + " <= ?";
|
|
||||||
String[] selectionArgs = new String[] {
|
|
||||||
Long.toString(dayId * 86400000L),
|
|
||||||
Long.toString(((dayId+1) * 86400000L)),
|
|
||||||
};
|
|
||||||
|
|
||||||
String sortOrder = MediaStore.Images.Media.DISPLAY_NAME + " ASC";
|
|
||||||
|
|
||||||
ArrayList<JSONObject> files = new ArrayList<>();
|
|
||||||
|
|
||||||
try (Cursor cursor = getContentResolver().query(
|
|
||||||
collection,
|
|
||||||
projection,
|
|
||||||
selection,
|
|
||||||
selectionArgs,
|
|
||||||
sortOrder
|
|
||||||
)) {
|
|
||||||
int idColumn = cursor.getColumnIndexOrThrow(MediaStore.Images.Media._ID);
|
|
||||||
int nameColumn = cursor.getColumnIndexOrThrow(MediaStore.Images.Media.DISPLAY_NAME);
|
|
||||||
int mimeColumn = cursor.getColumnIndexOrThrow(MediaStore.Images.Media.MIME_TYPE);
|
|
||||||
int dateColumn = cursor.getColumnIndexOrThrow(MediaStore.Images.Media.DATE_TAKEN);
|
|
||||||
int heightColumn = cursor.getColumnIndexOrThrow(MediaStore.Images.Media.HEIGHT);
|
|
||||||
int widthColumn = cursor.getColumnIndexOrThrow(MediaStore.Images.Media.WIDTH);
|
|
||||||
int sizeColumn = cursor.getColumnIndexOrThrow(MediaStore.Images.Media.SIZE);
|
|
||||||
|
|
||||||
while (cursor.moveToNext()) {
|
|
||||||
long id = cursor.getLong(idColumn);
|
|
||||||
String name = cursor.getString(nameColumn);
|
|
||||||
String mime = cursor.getString(mimeColumn);
|
|
||||||
long dateTaken = cursor.getLong(dateColumn);
|
|
||||||
long height = cursor.getLong(heightColumn);
|
|
||||||
long width = cursor.getLong(widthColumn);
|
|
||||||
long size = cursor.getLong(sizeColumn);
|
|
||||||
|
|
||||||
Uri contentUri = ContentUris.withAppendedId(
|
|
||||||
MediaStore.Images.Media.EXTERNAL_CONTENT_URI, id);
|
|
||||||
|
|
||||||
try {
|
|
||||||
JSONObject file = new JSONObject()
|
|
||||||
.put("fileid", id)
|
|
||||||
.put("basename", name)
|
|
||||||
.put("mimetype", mime)
|
|
||||||
.put("dayid", (dateTaken / 86400000))
|
|
||||||
.put("h", height)
|
|
||||||
.put("w", width)
|
|
||||||
.put("size", size);
|
|
||||||
files.add(file);
|
|
||||||
} catch (JSONException e) {
|
|
||||||
Log.e(TAG, "JSON error");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
this.jsResolve(call, new JSONArray(files).toString());
|
|
||||||
}
|
|
||||||
}).start();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@JavascriptInterface
|
@JavascriptInterface
|
||||||
public void getJpeg(String call, String uri) {
|
public void getJpeg(final String call, final String uri) {
|
||||||
Log.d(TAG, "getPreviewById: " + uri);
|
mJsService.runAsync(call, () -> mImageService.getFromURI(uri));
|
||||||
|
|
||||||
// URI looks like nativex://<type>/<id>
|
|
||||||
String[] parts = uri.split("/");
|
|
||||||
if (parts.length != 4) {
|
|
||||||
this.jsReject(call, "Invalid URI");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
final String type = parts[2];
|
|
||||||
final long id = Long.parseLong(parts[3]);
|
|
||||||
|
|
||||||
new Thread(() -> {
|
|
||||||
Bitmap bitmap = null;
|
|
||||||
|
|
||||||
if (type.equals("preview")) {
|
|
||||||
bitmap = MediaStore.Images.Thumbnails.getThumbnail(
|
|
||||||
getContentResolver(), id, MediaStore.Images.Thumbnails.MINI_KIND, null);
|
|
||||||
} else if (type.equals("full")) {
|
|
||||||
try {
|
|
||||||
bitmap = MediaStore.Images.Media.getBitmap(
|
|
||||||
getContentResolver(), ContentUris.withAppendedId(
|
|
||||||
MediaStore.Images.Media.EXTERNAL_CONTENT_URI, id));
|
|
||||||
} catch (IOException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
this.jsReject(call, "Invalid type");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (bitmap == null) {
|
|
||||||
this.jsReject(call, "Thumbnail not found");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
ByteArrayOutputStream stream = new ByteArrayOutputStream();
|
|
||||||
bitmap.compress(Bitmap.CompressFormat.JPEG, 90, stream);
|
|
||||||
jsResolve(call, stream.toByteArray());
|
|
||||||
}).start();
|
|
||||||
}
|
|
||||||
|
|
||||||
protected void jsResolve(String call, byte[] ret) {
|
|
||||||
final String b64 = Base64.getEncoder().encodeToString(ret);
|
|
||||||
runOnUiThread(() -> binding.webview.loadUrl("javascript:(function() { window.nativexr('" + call + "', '" + b64 + "'); })();void(0);"));
|
|
||||||
}
|
|
||||||
|
|
||||||
protected void jsResolve(String call, String ret) {
|
|
||||||
jsResolve(call, ret.getBytes());
|
|
||||||
}
|
|
||||||
|
|
||||||
protected void jsReject(String call, String ret) {
|
|
||||||
final String b64 = Base64.getEncoder().encodeToString(ret.getBytes());
|
|
||||||
runOnUiThread(() -> binding.webview.loadUrl("javascript:(function() { window.nativexr('" + call + "', undefined, '" + b64 + "'); })();void(0);"));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -0,0 +1,53 @@
|
||||||
|
package gallery.memories.service;
|
||||||
|
|
||||||
|
import android.content.ContentUris;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.graphics.Bitmap;
|
||||||
|
import android.provider.MediaStore;
|
||||||
|
|
||||||
|
import java.io.ByteArrayOutputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
public class ImageService {
|
||||||
|
Context mCtx;
|
||||||
|
|
||||||
|
public ImageService(Context context) {
|
||||||
|
mCtx = context;
|
||||||
|
}
|
||||||
|
|
||||||
|
public byte[] getFromURI(String uri) throws Exception {
|
||||||
|
// URI looks like nativex://<type>/<id>
|
||||||
|
String[] parts = uri.split("/");
|
||||||
|
if (parts.length != 4) {
|
||||||
|
throw new Exception("Invalid URI path");
|
||||||
|
}
|
||||||
|
|
||||||
|
final String type = parts[2];
|
||||||
|
final long id = Long.parseLong(parts[3]);
|
||||||
|
|
||||||
|
Bitmap bitmap = null;
|
||||||
|
|
||||||
|
if (type.equals("preview")) {
|
||||||
|
bitmap = MediaStore.Images.Thumbnails.getThumbnail(
|
||||||
|
mCtx.getContentResolver(), id, MediaStore.Images.Thumbnails.MINI_KIND, null);
|
||||||
|
} else if (type.equals("full")) {
|
||||||
|
try {
|
||||||
|
bitmap = MediaStore.Images.Media.getBitmap(
|
||||||
|
mCtx.getContentResolver(), ContentUris.withAppendedId(
|
||||||
|
MediaStore.Images.Media.EXTERNAL_CONTENT_URI, id));
|
||||||
|
} catch (IOException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
throw new Exception("Invalid request type");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (bitmap == null) {
|
||||||
|
throw new Exception("Thumbnail not found");
|
||||||
|
}
|
||||||
|
|
||||||
|
ByteArrayOutputStream stream = new ByteArrayOutputStream();
|
||||||
|
bitmap.compress(Bitmap.CompressFormat.JPEG, 90, stream);
|
||||||
|
return stream.toByteArray();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,42 @@
|
||||||
|
package gallery.memories.service;
|
||||||
|
|
||||||
|
import android.app.Activity;
|
||||||
|
import android.webkit.WebView;
|
||||||
|
|
||||||
|
import java.util.Base64;
|
||||||
|
|
||||||
|
public class JsService {
|
||||||
|
protected Activity mActivity;
|
||||||
|
protected WebView mWebView;
|
||||||
|
|
||||||
|
public JsService(Activity activity, WebView webView) {
|
||||||
|
mActivity = activity;
|
||||||
|
mWebView = webView;
|
||||||
|
}
|
||||||
|
|
||||||
|
public interface AsyncFunction {
|
||||||
|
byte[] run() throws Exception;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void runAsync(final String call, final AsyncFunction callable) {
|
||||||
|
new Thread(() -> {
|
||||||
|
try {
|
||||||
|
jsResolve(call, callable.run());
|
||||||
|
} catch (Exception e) {
|
||||||
|
jsReject(call, e);
|
||||||
|
}
|
||||||
|
}).start();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void jsResolve(String call, byte[] ret) {
|
||||||
|
final String b64 = Base64.getEncoder().encodeToString(ret);
|
||||||
|
mActivity.runOnUiThread(() -> mWebView.evaluateJavascript("window.nativexr('" + call + "', '" + b64 + "');", null));
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void jsReject(String call, Exception e) {
|
||||||
|
String message = e.getMessage();
|
||||||
|
message = message == null ? "Unknown error occured" : message;
|
||||||
|
final String b64 = Base64.getEncoder().encodeToString(message.getBytes());
|
||||||
|
mActivity.runOnUiThread(() -> mWebView.evaluateJavascript("window.nativexr('" + call + "', undefined, '" + b64 + "');", null));
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,98 @@
|
||||||
|
package gallery.memories.service;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.database.Cursor;
|
||||||
|
import android.net.Uri;
|
||||||
|
import android.provider.MediaStore;
|
||||||
|
import android.util.Log;
|
||||||
|
|
||||||
|
import org.json.JSONArray;
|
||||||
|
import org.json.JSONException;
|
||||||
|
import org.json.JSONObject;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.TimeZone;
|
||||||
|
|
||||||
|
public class TimelineQuery {
|
||||||
|
final static String TAG = "QueryService";
|
||||||
|
Context mCtx;
|
||||||
|
|
||||||
|
public TimelineQuery(Context context) {
|
||||||
|
mCtx = context;
|
||||||
|
}
|
||||||
|
|
||||||
|
public JSONArray getByDayId(final long dayId) {
|
||||||
|
Uri collection = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
|
||||||
|
|
||||||
|
// Offset of current timezone from UTC
|
||||||
|
long utcOffset = TimeZone.getDefault().getOffset(System.currentTimeMillis());
|
||||||
|
|
||||||
|
// Same fields as server response
|
||||||
|
String[] projection = new String[] {
|
||||||
|
MediaStore.Images.Media._ID,
|
||||||
|
MediaStore.Images.Media.DISPLAY_NAME,
|
||||||
|
MediaStore.Images.Media.MIME_TYPE,
|
||||||
|
MediaStore.Images.Media.DATE_TAKEN,
|
||||||
|
MediaStore.Images.Media.HEIGHT,
|
||||||
|
MediaStore.Images.Media.WIDTH,
|
||||||
|
MediaStore.Images.Media.SIZE,
|
||||||
|
};
|
||||||
|
|
||||||
|
// Filter for given day
|
||||||
|
String selection = MediaStore.Images.Media.DATE_TAKEN + " >= ? AND "
|
||||||
|
+ MediaStore.Images.Media.DATE_TAKEN + " <= ?";
|
||||||
|
String[] selectionArgs = new String[] {
|
||||||
|
Long.toString(dayId * 86400000L - utcOffset),
|
||||||
|
Long.toString(((dayId+1) * 86400000L - utcOffset)),
|
||||||
|
};
|
||||||
|
|
||||||
|
// Sort by name? TODO: fix this
|
||||||
|
String sortOrder = MediaStore.Images.Media.DISPLAY_NAME + " ASC";
|
||||||
|
|
||||||
|
// Make list of files
|
||||||
|
ArrayList<JSONObject> files = new ArrayList<>();
|
||||||
|
|
||||||
|
try (Cursor cursor = mCtx.getContentResolver().query(
|
||||||
|
collection,
|
||||||
|
projection,
|
||||||
|
selection,
|
||||||
|
selectionArgs,
|
||||||
|
sortOrder
|
||||||
|
)) {
|
||||||
|
int idColumn = cursor.getColumnIndexOrThrow(MediaStore.Images.Media._ID);
|
||||||
|
int nameColumn = cursor.getColumnIndexOrThrow(MediaStore.Images.Media.DISPLAY_NAME);
|
||||||
|
int mimeColumn = cursor.getColumnIndexOrThrow(MediaStore.Images.Media.MIME_TYPE);
|
||||||
|
int dateColumn = cursor.getColumnIndexOrThrow(MediaStore.Images.Media.DATE_TAKEN);
|
||||||
|
int heightColumn = cursor.getColumnIndexOrThrow(MediaStore.Images.Media.HEIGHT);
|
||||||
|
int widthColumn = cursor.getColumnIndexOrThrow(MediaStore.Images.Media.WIDTH);
|
||||||
|
int sizeColumn = cursor.getColumnIndexOrThrow(MediaStore.Images.Media.SIZE);
|
||||||
|
|
||||||
|
while (cursor.moveToNext()) {
|
||||||
|
long id = cursor.getLong(idColumn);
|
||||||
|
String name = cursor.getString(nameColumn);
|
||||||
|
String mime = cursor.getString(mimeColumn);
|
||||||
|
long dateTaken = cursor.getLong(dateColumn);
|
||||||
|
long height = cursor.getLong(heightColumn);
|
||||||
|
long width = cursor.getLong(widthColumn);
|
||||||
|
long size = cursor.getLong(sizeColumn);
|
||||||
|
|
||||||
|
try {
|
||||||
|
JSONObject file = new JSONObject()
|
||||||
|
.put("fileid", id)
|
||||||
|
.put("basename", name)
|
||||||
|
.put("mimetype", mime)
|
||||||
|
.put("dayid", (dateTaken / 86400000))
|
||||||
|
.put("h", height)
|
||||||
|
.put("w", width)
|
||||||
|
.put("size", size);
|
||||||
|
files.add(file);
|
||||||
|
} catch (JSONException e) {
|
||||||
|
Log.e(TAG, "JSON error");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return JSON string of files
|
||||||
|
return new JSONArray(files);
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue