From adcd2472d50c3fc228a3948e270e79d7f7dd85f1 Mon Sep 17 00:00:00 2001 From: Vologhat Date: Fri, 26 May 2023 16:35:43 +0300 Subject: [PATCH] rewrite and fix PhotoViewer hook - optimize code - create SearchEngine enum for image searchers - fix "search photo" on non-monet theme - fix icon colors in photo viewer context menu --- .../java/ru/vtosters/hooks/PhotoViewer.java | 357 +++++------------- .../lite/ui/fragments/MediaFragment.java | 34 +- .../ru/vtosters/lite/utils/SearchEngine.java | 56 +++ 3 files changed, 173 insertions(+), 274 deletions(-) create mode 100644 app/src/main/java/ru/vtosters/lite/utils/SearchEngine.java diff --git a/app/src/main/java/ru/vtosters/hooks/PhotoViewer.java b/app/src/main/java/ru/vtosters/hooks/PhotoViewer.java index 41f8ca88c..f9c1950b2 100644 --- a/app/src/main/java/ru/vtosters/hooks/PhotoViewer.java +++ b/app/src/main/java/ru/vtosters/hooks/PhotoViewer.java @@ -7,11 +7,11 @@ import android.content.ClipboardManager; import android.content.Context; import android.content.Intent; import android.net.Uri; +import android.text.TextUtils; import android.util.Log; import android.view.MenuItem; import android.view.View; import android.widget.Toast; -import androidx.annotation.Nullable; import androidx.core.content.FileProvider; import com.vk.core.dialogs.actionspopup.ActionsPopup; import com.vk.core.dialogs.alert.VkAlertDialog; @@ -22,319 +22,160 @@ import com.vtosters.lite.R; import com.vtosters.lite.attachments.DocumentAttachment; import com.vtosters.lite.attachments.PhotoAttachment; import okhttp3.*; -import okio.BufferedSink; import okio.Okio; -import org.json.JSONException; -import org.json.JSONObject; -import ru.vtosters.hooks.other.Preferences; import ru.vtosters.lite.di.singleton.VtOkHttpClient; import ru.vtosters.lite.themes.utils.RecolorUtils; import ru.vtosters.lite.utils.AndroidUtils; import ru.vtosters.lite.utils.LifecycleUtils; +import ru.vtosters.lite.utils.SearchEngine; import java.io.File; import java.io.IOException; -import java.net.URLEncoder; import java.util.List; public class PhotoViewer { static OkHttpClient client = VtOkHttpClient.getInstance(); - @SuppressLint("NonConstantResourceId") - public static boolean interceptClick(AttachmentWithMedia attachment, MenuItem item, View view) { - switch (item.getItemId()) { - case R.id.copy_image -> { - if (attachment instanceof PhotoAttachment) { - copyImage(getUrlFromPhotoAttachment((PhotoAttachment) attachment), String.valueOf(attachment.getId())); - } else if (attachment instanceof DocumentAttachment) { - var documentAttachment = (DocumentAttachment) attachment; - if (documentAttachment.J != null) - try { - copyImage(getUrlFromDocumentAttachment(documentAttachment), String.valueOf(attachment.getId())); - } catch (Exception e) { - ToastUtils.a(R.string.photo_get_error); - } - else - ToastUtils.a(R.string.photo_get_error); - } - return true; - } - case R.id.copy_photo_url -> { - if (attachment instanceof PhotoAttachment) { - copyPhotoUrl(getUrlFromPhotoAttachment((PhotoAttachment) attachment)); - } else if (attachment instanceof DocumentAttachment) { - var documentAttachment = (DocumentAttachment) attachment; - if (documentAttachment.J != null) - try { - copyPhotoUrl(getUrlFromDocumentAttachment(documentAttachment)); - } catch (Exception e) { - ToastUtils.a(R.string.photo_get_error); - } - else - ToastUtils.a(R.string.photo_get_error); - } - return true; - } - case R.id.open_original_photo -> { - if (attachment instanceof PhotoAttachment) { - openUrl(getUrlFromPhotoAttachment((PhotoAttachment) attachment)); - } else if (attachment instanceof DocumentAttachment) { - var documentAttachment = (DocumentAttachment) attachment; - if (documentAttachment.J != null) - try { - openUrl(getUrlFromDocumentAttachment(documentAttachment)); - } catch (Exception e) { - ToastUtils.a(R.string.photo_get_error); - } - else - ToastUtils.a(R.string.photo_get_error); - } - return true; - } - } - return true; - } - + //region INSERTION INTO SMALI @SuppressLint("UseCompatLoadingForDrawables") public static void addMenuItems(AttachmentWithMedia attachment, ActionsPopup.b actionPopup, int i, boolean z) { - actionPopup.a(R.string.copy, + actionPopup.a( + R.string.copy, RecolorUtils.fixActionMenuIcons(R.drawable.ic_copy_outline_28), false, () -> { - if (attachment instanceof PhotoAttachment) { - copyImage(getUrlFromPhotoAttachment((PhotoAttachment) attachment), String.valueOf(attachment.getId())); - } else if (attachment instanceof DocumentAttachment) { - var documentAttachment = (DocumentAttachment) attachment; - if (documentAttachment.J != null) - try { - copyImage(getUrlFromDocumentAttachment(documentAttachment), String.valueOf(attachment.getId())); - } catch (Exception e) { - ToastUtils.a(R.string.photo_get_error); - } - else - ToastUtils.a(R.string.photo_get_error); - } + copyImage(attachment); return null; } - ); - actionPopup.a(R.string.search_photo_content, + ).a( + R.string.search_photo_content, RecolorUtils.fixActionMenuIcons(R.drawable.ic_menu_search_outline_28), false, () -> { - if (attachment instanceof PhotoAttachment) { - searchDialog(LifecycleUtils.getCurrentActivity(), getUrlFromPhotoAttachment((PhotoAttachment) attachment)); - } else if (attachment instanceof DocumentAttachment) { - var documentAttachment = (DocumentAttachment) attachment; - if (documentAttachment.J != null) - try { - searchDialog(LifecycleUtils.getCurrentActivity(), getUrlFromDocumentAttachment(documentAttachment)); - } catch (Exception e) { - ToastUtils.a(R.string.photo_get_error); - } - else - ToastUtils.a(R.string.photo_get_error); - } + searchImageWithUrl(attachment); return null; } - ); - actionPopup.a(R.string.copy_photo_url_content, + ).a( + R.string.copy_photo_url_content, RecolorUtils.fixActionMenuIcons(R.drawable.ic_copy_outline_28), false, () -> { - if (attachment instanceof PhotoAttachment) { - copyPhotoUrl(getUrlFromPhotoAttachment((PhotoAttachment) attachment)); - } else if (attachment instanceof DocumentAttachment) { - var documentAttachment = (DocumentAttachment) attachment; - if (documentAttachment.J != null) - try { - copyPhotoUrl(getUrlFromDocumentAttachment(documentAttachment)); - } catch (Exception e) { - ToastUtils.a(R.string.photo_get_error); - } - else - ToastUtils.a(R.string.photo_get_error); - } + copyImageUrl(attachment); return null; } - ); - actionPopup.a(R.string.open_original_photo_content, + ).a( + R.string.open_original_photo_content, RecolorUtils.fixActionMenuIcons(R.drawable.ic_link_outline_28), false, () -> { - if (attachment instanceof PhotoAttachment) { - openUrl(getUrlFromPhotoAttachment((PhotoAttachment) attachment)); - } else if (attachment instanceof DocumentAttachment) { - var documentAttachment = (DocumentAttachment) attachment; - if (documentAttachment.J != null) - try { - openUrl(getUrlFromDocumentAttachment(documentAttachment)); - } catch (Exception e) { - ToastUtils.a(R.string.photo_get_error); - } - else - ToastUtils.a(R.string.photo_get_error); - } + openUrl(getImageUrlFromAttachment(attachment)); return null; } ); } - private static String getUrlFromDocumentAttachment(DocumentAttachment attachment) { - List imageSizes = null; - if (attachment.J != null) { - imageSizes = attachment.J.t1(); + @SuppressLint("NonConstantResourceId") + public static boolean interceptClick(AttachmentWithMedia attachment, MenuItem item, View view) { + switch (item.getItemId()) { + case R.id.copy_image -> copyImage(attachment); + case R.id.search_photo -> searchImageWithUrl(attachment); + case R.id.copy_photo_url -> copyImageUrl(attachment); + case R.id.open_original_photo -> openUrl(getImageUrlFromAttachment(attachment)); } - return getUrlFromImageSizeList(imageSizes); + return true; } + //endregion - private static String getUrlFromPhotoAttachment(PhotoAttachment attachment) { - var imageSizes = attachment.D.Q.t1(); - return getUrlFromImageSizeList(imageSizes); - } - - @Nullable - private static String getUrlFromImageSizeList(List imageSizes) { - if (imageSizes.isEmpty()) - return null; - var max = imageSizes.get(0); - for (int i = 1; i < imageSizes.size(); ++i) { - var size = imageSizes.get(i); - if (max != null && max.t1() < size.t1()) { - max = size; - } - } - return max.url; - } - - private static void searchDialog(Context context, String url) { - if (selectedEngine() == 0) { - var items = new String[]{ - "Yandex", "Google", "Bing", "TraceMoe", "Ascii2d", "Saucenao" - }; - - new VkAlertDialog.Builder(context) - .setItems(items, (dl, i) -> { - searchPhoto(i + 1, url); - }) - .show(); - } else { - searchPhoto(selectedEngine(), url); - } - } - - private static void copyImage(String link, String name) { - Request request = new Request.a().b(link).a(); - File file = new File(AndroidUtils.getGlobalContext().getExternalCacheDir(), name + ".jpg"); - - if (file.exists()) { - copyImageToClipboard(file); - return; - } - - new Thread() { + private static void copyImage(AttachmentWithMedia attachment) + { + var req = new Request.a() + .b(getImageUrlFromAttachment(attachment)) + .a(); + client.a(req).a(new Callback() + { @Override - public void run() { - try (Response response = client.a(request).execute()) { - try (BufferedSink sink = Okio.a(Okio.b(file))) { - sink.a(response.a().f()); - } - - copyImageToClipboard(file); - } catch (IOException e) { - e.printStackTrace(); - } - } - }.start(); - } - - private static void copyImageToClipboard(File file) { - Uri path = FileProvider.getUriForFile(AndroidUtils.getGlobalContext(), "com.vtosters.lite.common.VKFileProvider", file); - ClipData clip = ClipData.newUri(AndroidUtils.getGlobalContext().getContentResolver(), "Image", path); - ((ClipboardManager) AndroidUtils.getGlobalContext().getSystemService(Context.CLIPBOARD_SERVICE)).setPrimaryClip(clip); - } - - private static int selectedEngine() { - return Preferences.getPreferences().getInt("search_engine", 0); - } - - private static void searchPhoto(int i, String url) { - if (url == null || url.isEmpty()) { - Log.d("PhotoViewer", "url is null or empty"); - return; - } - - switch (i) { - case 1 -> searchWithYandex(url); - case 2 -> searchWithGoogle(url); - case 3 -> searchWithBing(url); - case 4 -> searchWithTraceMoe(url); - case 5 -> searchWithAscii2d(url); - case 6 -> searchWithSaucenao(url); - } - } - - private static void searchPhoto(String url) { - searchPhoto(selectedEngine(), url); - } - - private static void searchWithYandex(String url) { - var request = new Request.a() - .b(String.format("https://yandex.ru/images-apphost/image-download?url=%s&images_avatars_size=preview&images_avatars_namespace=images-cbir", - URLEncoder.encode(url))) - .a(); - client.a(request).a(new Callback() { - @Override - public void a(Call call, IOException e) { - Log.d("PhotoViewer", e.getMessage()); - } + public void a(Call call, IOException e) + { e.printStackTrace(); } @Override - public void a(Call call, Response response) throws IOException { - try { - JSONObject payload = new JSONObject(response.a().g()); - var cbirId = payload.optInt("image_shard") + "/" + payload.optString("image_id"); - var imageUrl = String.format("https://avatars.mds.yandex.net/get-images-cbir/%s/orig", cbirId); - var searchUrl = String.format("https://yandex.ru/images/search?rpt=imageview&url=%s&cbir_id=%s", imageUrl, cbirId); - openUrl(searchUrl); - } catch (JSONException e) { - e.printStackTrace(); + public void a(Call call, Response response) + throws IOException + { + var tmpImage = new File(AndroidUtils.getGlobalContext().getExternalCacheDir(), attachment.getId() + ".jpg"); + try(var resp = client.a(req).execute(); + var sink = Okio.a(Okio.b(tmpImage))) + { + sink.a(resp.a().f()); + var manager = (ClipboardManager) AndroidUtils.getGlobalContext().getSystemService(Context.CLIPBOARD_SERVICE); + var uri = FileProvider.getUriForFile( + AndroidUtils.getGlobalContext(), + "com.vtosters.lite.common.VKFileProvider", + tmpImage); + var data = ClipData.newUri( + AndroidUtils.getGlobalContext().getContentResolver(), + "image", + uri); + manager.setPrimaryClip(data); } } }); } - private static void searchWithGoogle(String url) { - openUrl("https://www.google.com/searchbyimage?image_url=" + URLEncoder.encode(url) + "&client=app"); + private static void searchImageWithUrl(AttachmentWithMedia attachment) + { + String url = getImageUrlFromAttachment(attachment); + if(TextUtils.isEmpty(url)) return; + int selectedEngine = SearchEngine.getDefaultSearchEngine(); + if (selectedEngine < 0) { + var items = new String[SearchEngine.values().length]; + for(int i = 0;i < SearchEngine.values().length; ++i) + items[i] = SearchEngine.values()[i].mTitle; + new VkAlertDialog.Builder(LifecycleUtils.getCurrentActivity()) + .setItems(items, (di, i) -> openUrl(SearchEngine.values()[i].buildSearchUrl(url))) + .show(); + } else openUrl(SearchEngine.values()[selectedEngine].buildSearchUrl(url)); } - private static void searchWithTraceMoe(String url) { - openUrl("https://trace.moe/?url=" + URLEncoder.encode(url)); - } - - private static void searchWithAscii2d(String url) { - openUrl("https://ascii2d.net/search/url/" + URLEncoder.encode(url)); - } - - private static void searchWithSaucenao(String url) { - openUrl("https://saucenao.com/search.php?url=" + URLEncoder.encode(url)); - } - - private static void searchWithBing(String url) { - openUrl("https://www.bing.com/images/search?view=detailv2&iss=SBI&form=SBIIDP&sbisrc=UrlPaste&q=imgurl:" - + URLEncoder.encode(url)); - } - - private static void copyPhotoUrl(String url) { - if (url == null || url.isEmpty()) return; + private static void copyImageUrl(AttachmentWithMedia attachment) + { + String url = getImageUrlFromAttachment(attachment); + if (TextUtils.isEmpty(url)) return; ClipboardManager manager = (ClipboardManager) AndroidUtils.getGlobalContext().getSystemService(Context.CLIPBOARD_SERVICE); manager.setPrimaryClip(ClipData.newPlainText("vk_photo_url", url)); Toast.makeText(AndroidUtils.getGlobalContext(), R.string.copied_to_clipboard, Toast.LENGTH_SHORT).show(); } - private static void openUrl(String url) { - var uri = Uri.parse(url); - var intent = new Intent(Intent.ACTION_VIEW, uri); - intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + private static void openUrl(String url) + { + if (TextUtils.isEmpty(url)) { + Log.d("PhotoViewer", "url is null or empty"); + return; + } + var intent = new Intent(Intent.ACTION_VIEW, Uri.parse(url)) + .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); AndroidUtils.getGlobalContext().startActivity(intent); } + + private static String getImageUrlFromAttachment(AttachmentWithMedia attachment) + { + List< ImageSize > imageSizes; + if(attachment instanceof PhotoAttachment) + imageSizes = ((PhotoAttachment) attachment).D.Q.t1(); + else if(attachment instanceof DocumentAttachment + && ((DocumentAttachment) attachment).J != null) + imageSizes = ((DocumentAttachment) attachment).J.t1(); + else + { + ToastUtils.a(R.string.photo_get_error); + return ""; + } + if(imageSizes.isEmpty()) return ""; + var max = imageSizes.get(0); + for(int i = 1; i < imageSizes.size(); i++) + { + var tmp = imageSizes.get(i); + if(max.t1() < tmp.t1()) max = tmp; + } + return max.url; + } } diff --git a/app/src/main/java/ru/vtosters/lite/ui/fragments/MediaFragment.java b/app/src/main/java/ru/vtosters/lite/ui/fragments/MediaFragment.java index 5d63d4dd9..5be2723fa 100644 --- a/app/src/main/java/ru/vtosters/lite/ui/fragments/MediaFragment.java +++ b/app/src/main/java/ru/vtosters/lite/ui/fragments/MediaFragment.java @@ -29,6 +29,7 @@ import ru.vtosters.lite.ui.adapters.ImagineArrayAdapter; import ru.vtosters.lite.utils.AccountManagerUtils; import ru.vtosters.lite.utils.AndroidUtils; import ru.vtosters.lite.utils.LifecycleUtils; +import ru.vtosters.lite.utils.SearchEngine; import java.io.IOException; import java.util.Arrays; @@ -133,26 +134,27 @@ public class MediaFragment extends TrackedMaterialPreferenceToolbarFragment { }); findPreference("select_photo_search_engine").setOnPreferenceClickListener(preference -> { - var deficon = ThemesUtils.recolorDrawable(AndroidUtils.getResources().getDrawable(R.drawable.ic_picture_outline_28)); - var choiceicon = ThemesUtils.recolorDrawable(AndroidUtils.getResources().getDrawable(R.drawable.link_outline_28)); + var items = new ImagineArrayAdapter.ImagineArrayAdapterItem[SearchEngine.values().length + 1]; + items[0] = new ImagineArrayAdapter.ImagineArrayAdapterItem( + ThemesUtils.recolorDrawable(AndroidUtils.getResources().getDrawable(R.drawable.link_outline_28)), + AndroidUtils.getString("by_choice")); + for(int i = 0; i < SearchEngine.values().length; ++i) + { + var engine = SearchEngine.values()[i]; + items[i + 1] = engine.mIconRes != R.drawable.ic_picture_outline_28 + ? new ImagineArrayAdapter.ImagineArrayAdapterItem(engine.mIconRes, engine.mTitle) + : new ImagineArrayAdapter.ImagineArrayAdapterItem( + ThemesUtils.recolorDrawable(AndroidUtils.getResources().getDrawable(R.drawable.ic_picture_outline_28)), + engine.mTitle); + } - var items = Arrays.asList( - new ImagineArrayAdapter.ImagineArrayAdapterItem(choiceicon, AndroidUtils.getString("by_choice")), - new ImagineArrayAdapter.ImagineArrayAdapterItem(R.drawable.yandex, "Yandex"), - new ImagineArrayAdapter.ImagineArrayAdapterItem(R.drawable.google, "Google"), - new ImagineArrayAdapter.ImagineArrayAdapterItem(R.drawable.microsoft, "Bing"), - new ImagineArrayAdapter.ImagineArrayAdapterItem(deficon, "TraceMoe"), - new ImagineArrayAdapter.ImagineArrayAdapterItem(deficon, "Ascii2d"), - new ImagineArrayAdapter.ImagineArrayAdapterItem(deficon, "Saucenao") - ); - - var adapter = new ImagineArrayAdapter(requireContext(), items); + var adapter = new ImagineArrayAdapter(requireContext(), Arrays.asList(items)); adapter.setSelected(Preferences.getPreferences().getInt("search_engine", 0)); new VkAlertDialog.Builder(getActivity()) - .setAdapter(adapter, (dialog, which) -> { - Preferences.getPreferences().edit().putInt("search_engine", which).apply(); - dialog.cancel(); + .setAdapter(adapter, (di, i) -> { + SearchEngine.setDefaultSearchEngine(i != 0 ? i - 1 : -1); + di.cancel(); }) .show(); return true; diff --git a/app/src/main/java/ru/vtosters/lite/utils/SearchEngine.java b/app/src/main/java/ru/vtosters/lite/utils/SearchEngine.java new file mode 100644 index 000000000..d0070586b --- /dev/null +++ b/app/src/main/java/ru/vtosters/lite/utils/SearchEngine.java @@ -0,0 +1,56 @@ +package ru.vtosters.lite.utils; + +import androidx.annotation.DrawableRes; +import ru.vtosters.hooks.other.Preferences; + +import java.net.URLEncoder; +import com.vtosters.lite.R; + +public enum SearchEngine +{ + YANDEX( + R.drawable.yandex, + "Yandex", + "https://yandex.com/images/search?rpt=imageview&url="), + GOOGLE( + R.drawable.google, + "Google", + "https://www.google.com/searchbyimage?client=app&image_url="), + BING( + R.drawable.microsoft, + "Bing", + "https://www.bing.com/images/search?view=detailv2&iss=SBI&form=SBIIDP&sbisrc=UrlPaste&q=imgurl:"), + TRACEMOE( + R.drawable.ic_picture_outline_28, + "trace.moe", + "https://trace.moe/?url="), + ASCII2D( + R.drawable.ic_picture_outline_28, + "Ascii2d", + "https://ascii2d.net/search/url/"), + SAUCENAO( + R.drawable.ic_picture_outline_28, + "SauceNAO", + "https://saucenao.com/search.php?url="),; + + + @DrawableRes + public int mIconRes; + public String mTitle; + private String mBaseUrl; + + SearchEngine(@DrawableRes int iconRes, String title, String baseUrl) + { + this.mIconRes = iconRes; + this.mTitle = title; + this.mBaseUrl = baseUrl; } + + public String buildSearchUrl(String targetUrl) + { return this.mBaseUrl + URLEncoder.encode(targetUrl); } + + public static int getDefaultSearchEngine() + { return Preferences.getPreferences().getInt("search_engine", -1); } + + public static void setDefaultSearchEngine(int i) + { Preferences.getPreferences().edit().putInt("search_engine", i).apply(); } +}