diff --git a/app/src/main/java/gallery/memories/MainActivity.java b/app/src/main/java/gallery/memories/MainActivity.java index c71e4433..f76476c9 100644 --- a/app/src/main/java/gallery/memories/MainActivity.java +++ b/app/src/main/java/gallery/memories/MainActivity.java @@ -39,7 +39,7 @@ public class MainActivity extends AppCompatActivity { @Override public WebResourceResponse shouldInterceptRequest(WebView view, WebResourceRequest request) { if (request.getUrl().getHost().equals("127.0.0.1")) { - return mNativeX.handleRequest(request.getUrl().getPath()); + return mNativeX.handleRequest(request); } return null; } diff --git a/app/src/main/java/gallery/memories/NativeX.java b/app/src/main/java/gallery/memories/NativeX.java index 58ff41cb..2677f118 100644 --- a/app/src/main/java/gallery/memories/NativeX.java +++ b/app/src/main/java/gallery/memories/NativeX.java @@ -9,6 +9,7 @@ import android.util.Log; import android.view.View; import android.view.Window; import android.webkit.JavascriptInterface; +import android.webkit.WebResourceRequest; import android.webkit.WebResourceResponse; import android.webkit.WebView; @@ -33,47 +34,27 @@ public class NativeX { mQuery = new TimelineQuery(activity); } - public WebResourceResponse handleRequest(final String path) { - byte[] bytes = null; - String mimeType = "application/json"; + public WebResourceResponse handleRequest(final WebResourceRequest request) { + final String path = request.getUrl().getPath(); + WebResourceResponse response; try { - // Match the path using regex - String[] parts = path.split("/"); - - if (path.matches("^/image/preview/\\d+$")) { - // Preview Image - bytes = mImageService.getPreview(Long.parseLong(parts[3])); - mimeType = "image/jpeg"; - } else if (path.matches("^/image/full/\\d+$")) { - // Full sized image - bytes = mImageService.getFull(Long.parseLong(parts[3])); - mimeType = "image/jpeg"; - } else if (path.matches("^/api/days$")) { - // Days list - bytes = mQuery.getDays().toString().getBytes(); - } else if (path.matches("/api/days/\\d+$")) { - // Single day photos - bytes = mQuery.getByDayId(Long.parseLong(parts[3])).toString().getBytes(); + if (request.getMethod().equals("GET")) { + response = routerGet(path); + } else if (request.getMethod().equals("OPTIONS")) { + response = new WebResourceResponse("text/plain", "UTF-8", new ByteArrayInputStream("".getBytes())); } else { - Log.e(TAG, "handleRequest: Unknown path: " + path); + throw new Exception("Method Not Allowed"); } } catch (Exception e) { Log.e(TAG, "handleRequest: ", e); - } - - // Construct the response - WebResourceResponse response; - if (bytes != null) { - response = new WebResourceResponse(mimeType, "UTF-8", new ByteArrayInputStream(bytes)); - } else { - response = new WebResourceResponse(mimeType, "UTF-8", new ByteArrayInputStream("{}".getBytes())); - response.setStatusCodeAndReasonPhrase(500, "Internal Server Error"); + response = makeErrorResponse(); } // Allow CORS from all origins Map headers = new ArrayMap<>(); headers.put("Access-Control-Allow-Origin", "*"); + headers.put("Access-Control-Allow-Headers", "*"); response.setResponseHeaders(headers); return response; @@ -100,4 +81,40 @@ public class NativeX { window.getDecorView().setSystemUiVisibility(isDark ? 0 : View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR); } } + + protected WebResourceResponse routerGet(final String path) throws Exception { + String[] parts = path.split("/"); + + if (path.matches("^/image/preview/\\d+$")) { + return makeResponse(mImageService.getPreview(Long.parseLong(parts[3])), "image/jpeg"); + } else if (path.matches("^/image/full/\\d+$")) { + return makeResponse(mImageService.getFull(Long.parseLong(parts[3])), "image/jpeg"); + } else if (path.matches("^/image/info/\\d+$")) { + return makeResponse(mQuery.getImageInfo(Long.parseLong(parts[3]))); + } else if (path.matches("^/api/days$")) { + return makeResponse(mQuery.getDays()); + } else if (path.matches("/api/days/\\d+$")) { + return makeResponse(mQuery.getByDayId(Long.parseLong(parts[3]))); + } + + throw new Exception("Not Found"); + } + + protected WebResourceResponse makeResponse(byte[] bytes, String mimeType) { + if (bytes != null) { + return new WebResourceResponse(mimeType, "UTF-8", new ByteArrayInputStream(bytes)); + } + + return makeErrorResponse(); + } + + protected WebResourceResponse makeResponse(Object json) { + return makeResponse(json.toString().getBytes(), "application/json"); + } + + protected WebResourceResponse makeErrorResponse() { + WebResourceResponse response = new WebResourceResponse("application/json", "UTF-8", new ByteArrayInputStream("{}".getBytes())); + response.setStatusCodeAndReasonPhrase(500, "Internal Server Error"); + return response; + } } diff --git a/app/src/main/java/gallery/memories/service/DbService.java b/app/src/main/java/gallery/memories/service/DbService.java index 120ddc74..973ec324 100644 --- a/app/src/main/java/gallery/memories/service/DbService.java +++ b/app/src/main/java/gallery/memories/service/DbService.java @@ -6,7 +6,7 @@ import android.database.sqlite.SQLiteOpenHelper; public class DbService extends SQLiteOpenHelper { public DbService(Context context) { - super(context, "memories", null, 11); + super(context, "memories", null, 16); } public void onCreate(SQLiteDatabase db) { diff --git a/app/src/main/java/gallery/memories/service/ImageService.java b/app/src/main/java/gallery/memories/service/ImageService.java index e798518c..5fd66542 100644 --- a/app/src/main/java/gallery/memories/service/ImageService.java +++ b/app/src/main/java/gallery/memories/service/ImageService.java @@ -6,7 +6,6 @@ import android.graphics.Bitmap; import android.provider.MediaStore; import java.io.ByteArrayOutputStream; -import java.io.IOException; public class ImageService { Context mCtx; diff --git a/app/src/main/java/gallery/memories/service/TimelineQuery.java b/app/src/main/java/gallery/memories/service/TimelineQuery.java index 683ef991..c00160e5 100644 --- a/app/src/main/java/gallery/memories/service/TimelineQuery.java +++ b/app/src/main/java/gallery/memories/service/TimelineQuery.java @@ -150,6 +150,76 @@ public class TimelineQuery { } } + public JSONObject getImageInfo(final long id) throws Exception { + // Get image info from DB + try (Cursor cursor = mDb.rawQuery( + "SELECT local_id, date_taken, dayid FROM images WHERE local_id = ?", + new String[] { Long.toString(id) } + )) { + if (!cursor.moveToNext()) { + throw new Exception("Image not found"); + } + + final long localId = cursor.getLong(0); + final long dateTaken = cursor.getLong(1); + final long dayid = cursor.getLong(2); + + // All external storage images + Uri collection = MediaStore.Images.Media.EXTERNAL_CONTENT_URI; + + // 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.HEIGHT, + MediaStore.Images.Media.WIDTH, + MediaStore.Images.Media.SIZE, + }; + + // Filter for given day + String selection = MediaStore.Images.Media._ID + + " = " + localId; + + try (Cursor cursor2 = mCtx.getContentResolver().query( + collection, + projection, + selection, + null, + null + )) { + int idColumn = cursor2.getColumnIndexOrThrow(MediaStore.Images.Media._ID); + int nameColumn = cursor2.getColumnIndexOrThrow(MediaStore.Images.Media.DISPLAY_NAME); + int mimeColumn = cursor2.getColumnIndexOrThrow(MediaStore.Images.Media.MIME_TYPE); + int heightColumn = cursor2.getColumnIndexOrThrow(MediaStore.Images.Media.HEIGHT); + int widthColumn = cursor2.getColumnIndexOrThrow(MediaStore.Images.Media.WIDTH); + int sizeColumn = cursor2.getColumnIndexOrThrow(MediaStore.Images.Media.SIZE); + + if (!cursor2.moveToNext()) { + throw new Exception("Image not found"); + } + + long id2 = cursor2.getLong(idColumn); + String name = cursor2.getString(nameColumn); + String mime = cursor2.getString(mimeColumn); + long height = cursor2.getLong(heightColumn); + long width = cursor2.getLong(widthColumn); + long size = cursor2.getLong(sizeColumn); + + return new JSONObject() + .put("fileid", id2) + .put("basename", name) + .put("mimetype", mime) + .put("dayid", dayid) + .put("datetaken", dateTaken) + .put("h", height) + .put("w", width) + .put("size", size) + .put("permissions", "D"); + } + } + } + protected void fullSyncDb() { Uri collection = MediaStore.Images.Media.EXTERNAL_CONTENT_URI; @@ -211,13 +281,14 @@ public class TimelineQuery { } // This will use whatever is available - final long dayId = dateTaken / 86400000; + dateTaken /= 1000; + final long dayId = dateTaken / 86400; // Delete file with same local_id and insert new one mDb.beginTransaction(); mDb.execSQL("DELETE FROM images WHERE local_id = ?", new Object[] { id }); - mDb.execSQL("INSERT OR IGNORE INTO images (local_id, mtime, basename, dayid) VALUES (?, ?, ?, ?)", - new Object[] { id, mtime, name, dayId }); + mDb.execSQL("INSERT OR IGNORE INTO images (local_id, mtime, basename, date_taken, dayid) VALUES (?, ?, ?, ?, ?)", + new Object[] { id, mtime, name, dateTaken, dayId }); mDb.setTransactionSuccessful(); mDb.endTransaction();