This commit is contained in:
2025-04-15 21:38:31 +08:00
parent cf50c338f8
commit dd1359c293
17 changed files with 998 additions and 35 deletions

View File

@@ -45,7 +45,6 @@
<uses-permission android:name="android.permission.WAKE_LOCK" /> <uses-permission android:name="android.permission.WAKE_LOCK" />
<uses-permission android:name="android.permission.WRITE_SETTINGS" tools:ignore="ProtectedPermissions"/> <uses-permission android:name="android.permission.WRITE_SETTINGS" tools:ignore="ProtectedPermissions"/>
<application <application
android:allowBackup="true" android:allowBackup="true"
android:fullBackupContent="@xml/backup_rules" android:fullBackupContent="@xml/backup_rules"
@@ -123,6 +122,7 @@
android:screenOrientation="fullSensor" android:screenOrientation="fullSensor"
android:theme="@style/Theme.AppCompat.Light.NoActionBar" android:theme="@style/Theme.AppCompat.Light.NoActionBar"
tools:ignore="MissingClass" /> tools:ignore="MissingClass" />
<activity android:name=".ui.JMActivity" android:theme="@style/Theme.FindMaimaiUltra.NoActionBar" android:label="FindMaimai Engine"/>
<service <service
android:hardwareAccelerated="true" android:hardwareAccelerated="true"
android:name="org.astral.findmaimaiultra.utill.updater.vpn.core.LocalVpnService" android:name="org.astral.findmaimaiultra.utill.updater.vpn.core.LocalVpnService"

View File

@@ -0,0 +1,259 @@
package org.astral.findmaimaiultra.adapter;
import android.annotation.SuppressLint;
import android.content.ContentValues;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.net.Uri;
import android.os.Environment;
import android.provider.MediaStore;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.Toast;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;
import com.bumptech.glide.Glide;
import com.bumptech.glide.request.target.CustomTarget;
import com.bumptech.glide.request.transition.Transition;
import org.astral.findmaimaiultra.R;
import org.astral.findmaimaiultra.been.pixiv.jm.Album;
import org.astral.findmaimaiultra.utill.FileUtils;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.List;
public class PhotoAdapter extends RecyclerView.Adapter<PhotoAdapter.PhotoViewHolder> {
private Context context;
private Album album;
private List<String> imageUrls;
private List<Integer> nums;
private static List<Integer> loading = new ArrayList<>();
public void clearLoad() {
loading.clear();
}
public PhotoAdapter(Context context, List<String> imageUrls, List<Integer> nums, Album a) {
this.context = context;
this.imageUrls = imageUrls;
this.nums = nums;
this.album = a;
}
@NonNull
@Override
public PhotoViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
View view = LayoutInflater.from(context).inflate(R.layout.photo_item, parent, false);
return new PhotoViewHolder(view);
}
@Override
public void onBindViewHolder(@NonNull PhotoViewHolder holder, @SuppressLint("RecyclerView") int position) {
String imageUrl = imageUrls.get(position);
int num = nums.get(position);
String fileName = "image_" + album.getAlbum_id() + "_" + position + ".jpg";
File cacheFile = FileUtils.getCacheDir(context, fileName);
Log.d("HHHHHHHHHH", "Loading image at position: " + loading.toString());
if (cacheFile.exists() && (!loading.contains(position))) {
Log.d("HHHHHHHHHH", "Loading cached image at position: " + position);
// 加载缓存的图片并压缩到屏幕大小
Glide.with(context)
.asBitmap()
.load(cacheFile)
.override(holder.itemView.getWidth(), holder.itemView.getHeight()) // 压缩图片到屏幕大小
.into(new CustomTarget<Bitmap>() {
@Override
public void onResourceReady(@NonNull Bitmap resource, Transition<? super Bitmap> transition) {
Log.d("PhotoAdapter", "Image loaded successfully at position: " + position);
if (!loading.contains(position)) {
loading.add(position);
}
holder.imageView.setImageBitmap(resource);
holder.imageView.setScaleType(ImageView.ScaleType.CENTER_CROP);
// 设置长按监听器
holder.imageView.setOnLongClickListener(v -> {
saveImageToMediaStore(resource, fileName);
return true;
});
}
@Override
public void onLoadCleared(Drawable placeholder) {
Log.d("PhotoAdapter", "Image load cleared at position: " + position);
}
@Override
public void onLoadFailed(Drawable errorDrawable) {
super.onLoadFailed(errorDrawable);
Log.e("PhotoAdapter", "Image load failed at position: " + position);
}
});
} else if ((!loading.contains(position))) {
// 从网络加载并处理图片
Glide.with(context)
.asBitmap()
.load(imageUrl)
.override(holder.itemView.getWidth(), holder.itemView.getHeight()) // 压缩图片到屏幕大小
.into(new CustomTarget<Bitmap>() {
@Override
public void onResourceReady(@NonNull Bitmap resource, Transition<? super Bitmap> transition) {
Log.d("PhotoAdapter", "Image loaded successfully at position: " + position);
if (!loading.contains(position)) {
loading.add(position);
}
Bitmap decodedBitmap = decodeImage(resource, num);
holder.imageView.setImageBitmap(decodedBitmap);
holder.imageView.setScaleType(ImageView.ScaleType.CENTER_CROP);
// 保存处理后的图片到缓存
saveBitmapToCache(decodedBitmap, cacheFile);
// 设置长按监听器
holder.imageView.setOnLongClickListener(v -> {
saveImageToMediaStore(decodedBitmap, fileName);
return true;
});
}
@Override
public void onLoadCleared(Drawable placeholder) {
Log.d("PhotoAdapter", "Image load cleared at position: " + position);
}
@Override
public void onLoadFailed(Drawable errorDrawable) {
super.onLoadFailed(errorDrawable);
Log.e("PhotoAdapter", "Image load failed at position: " + position);
}
});
}
}
@Override
public int getItemCount() {
return imageUrls.size();
}
public static class PhotoViewHolder extends RecyclerView.ViewHolder {
ImageView imageView;
public PhotoViewHolder(@NonNull View itemView) {
super(itemView);
imageView = itemView.findViewById(R.id.photo);
}
}
private Bitmap decodeImage(Bitmap imgSrc, int num) {
if (num == 0) {
return imgSrc;
}
int w = imgSrc.getWidth();
int h = imgSrc.getHeight();
// 创建新的解密图片
Bitmap imgDecode = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(imgDecode);
int over = h % num;
for (int i = 0; i < num; i++) {
int move = h / num;
int ySrc = h - (move * (i + 1)) - over;
int yDst = move * i;
if (i == 0) {
move += over;
} else {
yDst += over;
}
Rect srcRect = new Rect(0, ySrc, w, ySrc + move);
Rect dstRect = new Rect(0, yDst, w, yDst + move);
canvas.drawBitmap(imgSrc, srcRect, dstRect, null);
}
return imgDecode;
}
private void saveBitmapToCache(Bitmap bitmap, File file) {
OutputStream outputStream = null;
try {
outputStream = new FileOutputStream(file);
bitmap.compress(Bitmap.CompressFormat.JPEG, 90, outputStream);
} catch (Exception e) {
e.printStackTrace();
} finally {
if (outputStream != null) {
try {
outputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
private void saveImageToMediaStore(Bitmap bitmap, String fileName) {
ContentValues values = new ContentValues();
values.put(MediaStore.Images.Media.DISPLAY_NAME, fileName);
values.put(MediaStore.Images.Media.MIME_TYPE, "image/jpeg");
values.put(MediaStore.Images.Media.RELATIVE_PATH, Environment.DIRECTORY_PICTURES + "/FindMaimaiUltra");
Uri uri = null;
OutputStream outputStream = null;
try {
uri = context.getContentResolver().insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values);
if (uri != null) {
outputStream = context.getContentResolver().openOutputStream(uri);
if (outputStream != null) {
bitmap.compress(Bitmap.CompressFormat.JPEG, 90, outputStream);
Toast.makeText(context, "图片已保存到相册", Toast.LENGTH_SHORT).show();
}
}
} catch (Exception e) {
e.printStackTrace();
if (uri != null) {
context.getContentResolver().delete(uri, null, null);
}
Toast.makeText(context, "保存图片失败", Toast.LENGTH_SHORT).show();
} finally {
if (outputStream != null) {
try {
outputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
// 添加删除缓存方法
public void clearCache() {
File cacheDir = FileUtils.getCacheDir(context, "");
if (cacheDir.exists() && cacheDir.isDirectory()) {
File[] files = cacheDir.listFiles();
if (files != null) {
for (File file : files) {
if (file.isFile()) {
file.delete();
}
}
}
}
}
}

View File

@@ -1,5 +1,6 @@
package org.astral.findmaimaiultra.adapter; package org.astral.findmaimaiultra.adapter;
import android.annotation.SuppressLint;
import android.graphics.Bitmap; import android.graphics.Bitmap;
import android.graphics.BitmapFactory; import android.graphics.BitmapFactory;
import android.util.Log; import android.util.Log;
@@ -49,16 +50,22 @@ public class PixivAdapter extends RecyclerView.Adapter<PixivAdapter.ViewHolder>
return new ViewHolder(view); return new ViewHolder(view);
} }
@SuppressLint("SetTextI18n")
@Override @Override
public void onBindViewHolder(@NonNull ViewHolder holder, int position) { public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
IllustData data = dataList.get(position); IllustData data = dataList.get(position);
holder.title.setText(data.getTitle()); holder.title.setText(data.getTitle());
// Load image using a library like Glide or Picasso // Load image using a library like Glide or Picasso
if (data.getUrl().startsWith("JM:")) {
holder.title.setText("[" + data.getId() + "] " +data.getTitle());
}else if (data.getId().startsWith("Place:")) {
holder.title.setText(data.getTitle() + " - " + data.getAlt());
}else {
String imageUrl = "http://43.153.174.191:45678/api/v1/pixiv/toTencent?id=" + data.getId(); String imageUrl = "http://43.153.174.191:45678/api/v1/pixiv/toTencent?id=" + data.getId();
if (imageUrl != null) { if (imageUrl != null) {
loadImage(holder.backgroundLayout, imageUrl); loadImage(holder.backgroundLayout, imageUrl);
} }
}
holder.itemView.setOnClickListener(v -> { holder.itemView.setOnClickListener(v -> {
if (listener != null) { if (listener != null) {

View File

@@ -0,0 +1,153 @@
package org.astral.findmaimaiultra.been.pixiv.jm;
import java.io.Serializable;
import java.util.List;
public class Album {
private String album_id;
private String scramble_id;
private String name;
private int page_count;
private String pub_date;
private String update_date;
private int likes;
private int views;
private int comment_count;
private List<String> works;
private List<String> actors;
private List<String> authors;
private List<String> tags;
private List<String> image_urls;
private List<RelatedItem> related_list;
private List<Integer> nums;
public List<Integer> getNums() {
return nums;
}
public void setNums(List<Integer> nums) {
this.nums = nums;
}
// Getters and Setters
public String getAlbum_id() {
return album_id;
}
public void setAlbum_id(String album_id) {
this.album_id = album_id;
}
public String getScramble_id() {
return scramble_id;
}
public void setScramble_id(String scramble_id) {
this.scramble_id = scramble_id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getPage_count() {
return page_count;
}
public void setPage_count(int page_count) {
this.page_count = page_count;
}
public String getPub_date() {
return pub_date;
}
public void setPub_date(String pub_date) {
this.pub_date = pub_date;
}
public String getUpdate_date() {
return update_date;
}
public void setUpdate_date(String update_date) {
this.update_date = update_date;
}
public int getLikes() {
return likes;
}
public void setLikes(int likes) {
this.likes = likes;
}
public int getViews() {
return views;
}
public void setViews(int views) {
this.views = views;
}
public int getComment_count() {
return comment_count;
}
public void setComment_count(int comment_count) {
this.comment_count = comment_count;
}
public List<String> getWorks() {
return works;
}
public void setWorks(List<String> works) {
this.works = works;
}
public List<String> getActors() {
return actors;
}
public void setActors(List<String> actors) {
this.actors = actors;
}
public List<String> getAuthors() {
return authors;
}
public void setAuthors(List<String> authors) {
this.authors = authors;
}
public List<String> getTags() {
return tags;
}
public void setTags(List<String> tags) {
this.tags = tags;
}
public List<String> getImage_urls() {
return image_urls;
}
public void setImage_urls(List<String> image_urls) {
this.image_urls = image_urls;
}
public List<RelatedItem> getRelated_list() {
return related_list;
}
public void setRelated_list(List<RelatedItem> related_list) {
this.related_list = related_list;
}
}

View File

@@ -0,0 +1,23 @@
package org.astral.findmaimaiultra.been.pixiv.jm;
public class AlbumItem {
private String album_id;
private String title;
// Getters and Setters
public String getAlbum_id() {
return album_id;
}
public void setAlbum_id(String album_id) {
this.album_id = album_id;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
}

View File

@@ -0,0 +1,41 @@
package org.astral.findmaimaiultra.been.pixiv.jm;
public class RelatedItem {
private String id;
private String author;
private String name;
private String image;
// Getters and Setters
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getAuthor() {
return author;
}
public void setAuthor(String author) {
this.author = author;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getImage() {
return image;
}
public void setImage(String image) {
this.image = image;
}
}

View File

@@ -0,0 +1,17 @@
package org.astral.findmaimaiultra.been.pixiv.jm;
import java.util.List;
public class SearchJM {
private List<AlbumItem> albums;
// Getter and Setter
public List<AlbumItem> getAlbums() {
return albums;
}
public void setAlbums(List<AlbumItem> albums) {
this.albums = albums;
}
}

View File

@@ -0,0 +1,70 @@
package org.astral.findmaimaiultra.ui;
import android.annotation.SuppressLint;
import android.content.Intent;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.view.animation.Animation;
import android.view.animation.AnimationUtils;
import android.widget.FrameLayout;
import android.widget.TextView;
import android.widget.Toast;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity;
import androidx.constraintlayout.widget.ConstraintLayout;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import com.google.android.material.bottomsheet.BottomSheetBehavior;
import com.google.gson.Gson;
import org.astral.findmaimaiultra.R;
import org.astral.findmaimaiultra.adapter.PhotoAdapter;
import org.astral.findmaimaiultra.been.pixiv.jm.Album;
public class JMActivity extends AppCompatActivity {
private FrameLayout overlay;
private boolean isOverlayVisible = false;
private BottomSheetBehavior<View> bottomSheetBehavior;
private PhotoAdapter photoAdapter;
@Override
@SuppressLint("MissingInflatedId")
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.jm_dialog);
initRecyclerView();
}
@SuppressLint({"ClickableViewAccessibility", "SetTextI18n"})
private void initRecyclerView() {
Intent intent = getIntent();
String res = intent.getStringExtra("album");
Album a = new Gson().fromJson(res, Album.class);
RecyclerView recyclerView = findViewById(R.id.recyclerView);
recyclerView.setLayoutManager(new LinearLayoutManager(this));
photoAdapter = new PhotoAdapter(this, a.getImage_urls(), a.getNums(), a);
photoAdapter.clearLoad();
recyclerView.setAdapter(photoAdapter);
bottomSheetBehavior = BottomSheetBehavior.from(findViewById(R.id.bottom_sheet));
bottomSheetBehavior.setPeekHeight(dpToPx(80));
TextView menu = findViewById(R.id.menu);
menu.setText(a.getName());
TextView dec = findViewById(R.id.dec);
dec.setText(a.getAuthors().toString().replaceAll( "\\[","").replaceAll( "]","")
+ " / " + a.getActors().toString().replaceAll( "\"","") .replaceAll( "\\[","").replaceAll( "]","")
+ " \n " + a.getTags().toString().replaceAll( "\"","") .replaceAll( "\\[","").replaceAll( "]","")
+ " \n " + a.getAlbum_id().replaceAll( "\"","").replaceAll( "\\[","").replaceAll( "]",""));
}
public int dpToPx(int dp) {
return (int) (dp * getResources().getDisplayMetrics().density);
}
@Override
protected void onDestroy() {
super.onDestroy();
if (photoAdapter != null) {
photoAdapter.clearCache();
}
}
}

View File

@@ -1,21 +1,14 @@
package org.astral.findmaimaiultra.ui.pixiv; package org.astral.findmaimaiultra.ui.pixiv;
import android.Manifest;
import android.annotation.SuppressLint; import android.annotation.SuppressLint;
import android.app.Activity;
import android.content.*; import android.content.*;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.graphics.Bitmap; import android.graphics.Bitmap;
import android.graphics.BitmapFactory; import android.graphics.BitmapFactory;
import android.graphics.Canvas; import android.graphics.Canvas;
import android.graphics.Paint; import android.graphics.Rect;
import android.graphics.drawable.BitmapDrawable; import android.graphics.drawable.Drawable;
import android.location.Address;
import android.location.*;
import android.net.Uri; import android.net.Uri;
import android.os.Bundle; import android.os.Bundle;
import android.os.Environment;
import android.os.Handler; import android.os.Handler;
import android.os.Looper; import android.os.Looper;
import android.util.Log; import android.util.Log;
@@ -23,52 +16,48 @@ import android.view.*;
import android.widget.*; import android.widget.*;
import androidx.activity.OnBackPressedCallback; import androidx.activity.OnBackPressedCallback;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AlertDialog; import androidx.appcompat.app.AlertDialog;
import androidx.appcompat.widget.SearchView; import androidx.appcompat.widget.SearchView;
import androidx.appcompat.widget.Toolbar;
import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat;
import androidx.fragment.app.Fragment; import androidx.fragment.app.Fragment;
import androidx.lifecycle.ViewModelProvider; import androidx.lifecycle.ViewModelProvider;
import androidx.recyclerview.widget.LinearLayoutManager; import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView; import androidx.recyclerview.widget.RecyclerView;
import com.bumptech.glide.Glide; import com.bumptech.glide.Glide;
import com.bumptech.glide.request.RequestOptions; import com.bumptech.glide.request.target.CustomTarget;
import com.bumptech.glide.request.target.Target;
import com.bumptech.glide.request.transition.Transition;
import com.google.android.material.button.MaterialButton; import com.google.android.material.button.MaterialButton;
import com.google.android.material.dialog.MaterialAlertDialogBuilder; import com.google.android.material.dialog.MaterialAlertDialogBuilder;
import com.google.android.material.snackbar.Snackbar; import com.google.android.material.snackbar.Snackbar;
import com.google.gson.Gson; import com.google.gson.Gson;
import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.reflect.TypeToken; import com.google.gson.reflect.TypeToken;
import okhttp3.*; import okhttp3.*;
import org.astral.findmaimaiultra.R; import org.astral.findmaimaiultra.R;
import org.astral.findmaimaiultra.adapter.MusicRatingAdapter;
import org.astral.findmaimaiultra.adapter.PixivAdapter; import org.astral.findmaimaiultra.adapter.PixivAdapter;
import org.astral.findmaimaiultra.adapter.PlaceAdapter; import org.astral.findmaimaiultra.been.Place;
import org.astral.findmaimaiultra.been.*; import org.astral.findmaimaiultra.been.pixiv.jm.Album;
import org.astral.findmaimaiultra.been.pixiv.jm.AlbumItem;
import org.astral.findmaimaiultra.been.pixiv.jm.SearchJM;
import org.astral.findmaimaiultra.been.pixiv.model.IllustData; import org.astral.findmaimaiultra.been.pixiv.model.IllustData;
import org.astral.findmaimaiultra.been.pixiv.model.PixivResponse; import org.astral.findmaimaiultra.been.pixiv.model.PixivResponse;
import org.astral.findmaimaiultra.been.pixiv.model.pages.PagePixivResponse; import org.astral.findmaimaiultra.been.pixiv.model.pages.PagePixivResponse;
import org.astral.findmaimaiultra.been.pixiv.model.pages.photo.Photo; import org.astral.findmaimaiultra.been.pixiv.model.pages.photo.Photo;
import org.astral.findmaimaiultra.been.pixiv.model.pages.photo.PhotoResponse; import org.astral.findmaimaiultra.been.pixiv.model.pages.photo.PhotoResponse;
import org.astral.findmaimaiultra.been.pixiv.model.pages.photo.Urls;
import org.astral.findmaimaiultra.databinding.FragmentHomeBinding;
import org.astral.findmaimaiultra.databinding.FragmentPixivBinding; import org.astral.findmaimaiultra.databinding.FragmentPixivBinding;
import org.astral.findmaimaiultra.ui.MainActivity; import org.astral.findmaimaiultra.ui.JMActivity;
import org.astral.findmaimaiultra.ui.PageActivity; import org.astral.findmaimaiultra.ui.PageActivity;
import org.astral.findmaimaiultra.ui.home.HomeViewModel; import org.astral.findmaimaiultra.ui.home.HomeViewModel;
import org.astral.findmaimaiultra.utill.AddressParser;
import org.astral.findmaimaiultra.utill.SharedViewModel; import org.astral.findmaimaiultra.utill.SharedViewModel;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import java.io.*; import java.io.*;
import java.lang.reflect.Type; import java.lang.reflect.Type;
import java.text.SimpleDateFormat; import java.net.URL;
import java.util.*; import java.util.*;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
public class PixivFragment extends Fragment { public class PixivFragment extends Fragment {
private RecyclerView recyclerView; private RecyclerView recyclerView;
private Handler handler = new Handler(Looper.getMainLooper()); private Handler handler = new Handler(Looper.getMainLooper());
@@ -142,19 +131,29 @@ public class PixivFragment extends Fragment {
recyclerView.setLayoutManager(new LinearLayoutManager(requireContext())); recyclerView.setLayoutManager(new LinearLayoutManager(requireContext()));
recyclerView.setAdapter(adapter); recyclerView.setAdapter(adapter);
// 设置搜索框的查询监听器 // 设置搜索框的查询监听器
searchView.setOnQueryTextListener(new SearchView.OnQueryTextListener() { searchView.setOnQueryTextListener(new SearchView.OnQueryTextListener() {
@Override @Override
public boolean onQueryTextSubmit(String query) { public boolean onQueryTextSubmit(String query) {
// 显示 Snackbar 提示“正在搜索” // 显示 Snackbar 提示“正在搜索”
RadioGroup radioGroup = binding.radioGroup2;
Snackbar snackbar = Snackbar.make(root, "正在搜索...", Snackbar.LENGTH_INDEFINITE); Snackbar snackbar = Snackbar.make(root, "正在搜索...", Snackbar.LENGTH_INDEFINITE);
snackbar.setAnchorView(searchView); // 设置 Snackbar 锚定到搜索框 snackbar.setAnchorView(searchView); // 设置 Snackbar 锚定到搜索框
snackbar.show(); snackbar.show();
//获取单选框
int checkedRadioButtonId = radioGroup.getCheckedRadioButtonId();
adapter.update(new ArrayList<>()); adapter.update(new ArrayList<>());
adapter.notifyDataSetChanged(); adapter.notifyDataSetChanged();
// 当用户提交查询时调用 fetchData 方法 // 当用户提交查询时调用 fetchData 方法
if (checkedRadioButtonId == R.id.pb) {
fetchData(query, 1, snackbar); fetchData(query, 1, snackbar);
} else if (checkedRadioButtonId == R.id.jmb) {
fetchDataJM(query, 1, snackbar);
}else if (checkedRadioButtonId == R.id.maimai) {
fetchDataMai(query, 1, snackbar);
}
// 显示搜索结果布局,隐藏 RecyclerView // 显示搜索结果布局,隐藏 RecyclerView
path1.setVisibility(View.GONE); path1.setVisibility(View.GONE);
path2.setVisibility(View.VISIBLE); path2.setVisibility(View.VISIBLE);
@@ -171,7 +170,106 @@ public class PixivFragment extends Fragment {
return root; return root;
} }
private void fetchDataMai(String query, int i, Snackbar snackbar) {
OkHttpClient client = new OkHttpClient();
Request request = new Request.Builder()
.url("http://mai.godserver.cn:11451/api/mai/v1/searchAll?query=" + query )
.build();
snackbar.dismiss();
client.newCall(request).enqueue(new Callback() {
@Override
public void onResponse(@NotNull Call call, @NotNull Response response) throws IOException {
String json = response.body().string();
if (response.isSuccessful()) {
List<Place> dataList = new Gson().fromJson(json, new TypeToken<List<Place>>() {
}.getType());
List<IllustData> dataList2 = new ArrayList<>();
for (Place place : dataList) {
IllustData illustData = new IllustData();
illustData.setId("Place:"+place.getId());
illustData.setTitle(place.getName());
illustData.setAlt(place.getProvince());
illustData.setUrl(place.getAddress());
illustData.setDescription(new Gson().toJson(place,Place.class));
dataList2.add(illustData);
}
handler.post(() -> {
path2.setVisibility(View.VISIBLE);
recyclerView.setVisibility(View.VISIBLE);
adapter.update(dataList2);
adapter.notifyDataSetChanged();
});
}
}
@Override
public void onFailure(@NotNull Call call, @NotNull IOException e) {
}
});
}
private void fetchDataJM(String query, int i, Snackbar snackbar) {
OkHttpClient client = new OkHttpClient();
int type =0;
Request request = new Request.Builder()
.url("http://jm.godserver.cn:35621/search?search_query=" + query + "&page=1")
.build();
if (query.matches("\\d+")) {
request = new Request.Builder()
.url("jm.godserver.cn:35621/album/" + query + "/")
.build();
type = 1;
}
snackbar = Snackbar.make(requireView(), "加载中", Snackbar.LENGTH_SHORT);
snackbar.show();
int finalType = type;
Snackbar finalSnackbar = snackbar;
client.newCall(request).enqueue(new Callback() {
@Override
public void onResponse(@NotNull Call call, @NotNull Response response) throws IOException {
String json = response.body().string();
if (response.isSuccessful()) {
if(finalType == 0) {
System.out.println(json);
Gson gson = new Gson();
Type listType = new TypeToken<List<AlbumItem>>() {}.getType();
List<AlbumItem> albumItems = gson.fromJson(json, listType);
List<IllustData> dataList = new ArrayList<>();
for (AlbumItem item : albumItems) {
IllustData illustData = new IllustData();
illustData.setId(item.getAlbum_id());
illustData.setTitle(item.getTitle());
illustData.setUrl("JM:" + item.getAlbum_id());
System.out.println(item.getAlbum_id());
dataList.add(illustData);
}
handler.post(() -> {
path2.setVisibility(View.VISIBLE);
recyclerView.setVisibility(View.VISIBLE);
adapter.update(dataList);
adapter.notifyDataSetChanged();
// 隐藏 Snackbar
finalSnackbar.dismiss();
});
}else if (finalType == 1) {
Album albumItem = new Gson().fromJson(json, Album.class);
openJMProject(albumItem);
}
}
}
@Override
public void onFailure(@NotNull Call call, @NotNull IOException e) {
}
});
}
private void fetchData(String word, int page, Snackbar snackbar) { private void fetchData(String word, int page, Snackbar snackbar) {
OkHttpClient client = new OkHttpClient(); OkHttpClient client = new OkHttpClient();
//设置超时时间60s //设置超时时间60s
client.newBuilder().connectTimeout(60, TimeUnit.SECONDS); client.newBuilder().connectTimeout(60, TimeUnit.SECONDS);
@@ -257,6 +355,18 @@ public class PixivFragment extends Fragment {
@SuppressLint("MissingInflatedId") @SuppressLint("MissingInflatedId")
private void openIllustData(IllustData illustData) { private void openIllustData(IllustData illustData) {
if (illustData.getUrl().startsWith("JM:")) {
Snackbar snackbar = Snackbar.make(binding.getRoot(), "正在获取数据", Snackbar.LENGTH_LONG);
snackbar.show();
openJM(illustData,snackbar);
return;
}
if (illustData.getId().startsWith("Place:")) {
Snackbar snackbar = Snackbar.make(binding.getRoot(), "正在获取数据", Snackbar.LENGTH_SHORT);
snackbar.show();
openPlace(illustData);
return;
}
MaterialAlertDialogBuilder builder = new MaterialAlertDialogBuilder(requireContext()); MaterialAlertDialogBuilder builder = new MaterialAlertDialogBuilder(requireContext());
builder.setTitle(illustData.getTitle()); builder.setTitle(illustData.getTitle());
Log.d("PixivFragment", "openIllustData: " + illustData.getTitle()); Log.d("PixivFragment", "openIllustData: " + illustData.getTitle());
@@ -354,6 +464,159 @@ public class PixivFragment extends Fragment {
// 显示对话框 // 显示对话框
dialog.show(); dialog.show();
} }
private void openPlace(IllustData illustData) {
Place place = new Gson().fromJson(illustData.getDescription(), Place.class);
Intent intent = new Intent(context, PageActivity.class);
intent.putExtra("id", place.getId());
intent.putExtra("name", place.getName());
intent.putExtra("address", place.getAddress());
intent.putExtra("province", place.getProvince());
intent.putExtra("city", place.getCity());
intent.putExtra("area", place.getArea());
intent.putExtra("x", place.getX());
intent.putExtra("y", place.getY());
intent.putExtra("count", place.getCount());
intent.putExtra("bad", place.getBad());
intent.putExtra("good", place.getGood());
intent.putExtra("num", place.getNum());
intent.putExtra("numJ", place.getNumJ());
intent.putExtra("meituan", place.getMeituan_link());
intent.putExtra("douyin", place.getDouyin_link());
startActivity(intent);
}
private void openJM(IllustData illustData,Snackbar snackbar) {
MaterialAlertDialogBuilder builder = new MaterialAlertDialogBuilder(requireContext());
builder.setTitle(illustData.getTitle());
//snackbar长显示
snackbar = Snackbar.make(requireView(), "正在加载", Snackbar.LENGTH_INDEFINITE);
snackbar.show();
OkHttpClient httpClient = new OkHttpClient();
//配置超时
httpClient.newBuilder().connectTimeout(120, TimeUnit.SECONDS);
httpClient.newBuilder().readTimeout(120, TimeUnit.SECONDS);
httpClient.newBuilder().writeTimeout(120, TimeUnit.SECONDS);
Request request = new Request.Builder()
.url("http://jm.godserver.cn:35621/album/" + illustData.getId() + "/")
.build();
Log.d("MainLaunch", "http://jm.godserver.cn:35621/album/" + illustData.getId() + "/");
//配置超时
Snackbar finalSnackbar = snackbar;
httpClient.newCall(request).enqueue(new Callback() {
@Override
public void onResponse(@NotNull Call call, @NotNull Response response) throws IOException {
String res = response.body().string();
if (response.isSuccessful()) {
Log.d("PixivFragment", "onResponse: " + res);
finalSnackbar.dismiss();
Album a = new Gson().fromJson(res, Album.class);
openJMProject(a);
}
}
@Override
public void onFailure(@NotNull Call call, @NotNull IOException e) {
handler.post(()->{
Toast.makeText(requireContext(), e.getMessage(), Toast.LENGTH_SHORT).show();
finalSnackbar.dismiss();
});
Log.d("PixivFragment", "onFailure: " + e.getMessage());
}
});
}
private Bitmap decodeImage(Bitmap imgSrc, int num) {
if (num == 0) {
return imgSrc;
}
int w = imgSrc.getWidth();
int h = imgSrc.getHeight();
// 创建新的解密图片
Bitmap imgDecode = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(imgDecode);
int over = h % num;
for (int i = 0; i < num; i++) {
int move = h / num;
int ySrc = h - (move * (i + 1)) - over;
int yDst = move * i;
if (i == 0) {
move += over;
} else {
yDst += over;
}
Rect srcRect = new Rect(0, ySrc, w, ySrc + move);
Rect dstRect = new Rect(0, yDst, w, yDst + move);
canvas.drawBitmap(imgSrc, srcRect, dstRect, null);
}
return imgDecode;
}
private void loadImageFromUrl(String url, int num, LinearLayout li) {
// 使用 Glide 加载图片
Glide.with(requireContext())
.asBitmap()
.load(url)
.into(new CustomTarget<Bitmap>() {
@Override
public void onResourceReady(@NonNull Bitmap resource, @Nullable Transition<? super Bitmap> transition) {
Bitmap decodedBitmap = decodeImage(resource, num); // 假设 num 为 4根据实际情况调整
// 更新 UI
handler.post(() -> {
ImageView photoView = new ImageView(getContext());
photoView.setImageBitmap(decodedBitmap);
photoView.setScaleType(ImageView.ScaleType.CENTER_CROP); // 设置图片缩放类型
photoView.setAdjustViewBounds(true); // 调整视图边界
// 设置布局参数
LinearLayout.LayoutParams layoutParams = new LinearLayout.LayoutParams(
LinearLayout.LayoutParams.MATCH_PARENT,
LinearLayout.LayoutParams.WRAP_CONTENT
);
layoutParams.setMargins(0, 0, 0, 0); // 设置间距为0
photoView.setLayoutParams(layoutParams);
li.addView(photoView);
Log.d("t111111111", url);
});
}
@Override
public void onLoadCleared(@Nullable Drawable placeholder) {
// 处理加载清除的情况
}
@Override
public void onLoadFailed(@Nullable Drawable errorDrawable) {
super.onLoadFailed(errorDrawable);
handler.post(() -> {
Toast.makeText(requireContext(), "图片加载失败", Toast.LENGTH_SHORT).show();
});
}
});
}
@SuppressLint("MissingInflatedId")
private void openJMProject(Album a) {
Intent intent = new Intent(requireContext(), JMActivity.class);
intent.putExtra("album", new Gson().toJson(a));
startActivity(intent);
}
private void download(String url) { private void download(String url) {
//复制 //复制
ClipboardManager clipboardManager = (ClipboardManager) requireContext().getSystemService(Context.CLIPBOARD_SERVICE); ClipboardManager clipboardManager = (ClipboardManager) requireContext().getSystemService(Context.CLIPBOARD_SERVICE);

View File

@@ -0,0 +1,16 @@
package org.astral.findmaimaiultra.utill;
import android.content.Context;
import android.os.Environment;
import java.io.File;
public class FileUtils {
public static File getCacheDir(Context context, String fileName) {
File cacheDir = new File(context.getExternalFilesDir(Environment.DIRECTORY_PICTURES), "FindMaimaiUltra");
if (!cacheDir.exists()) {
cacheDir.mkdirs();
}
return new File(cacheDir, fileName);
}
}

View File

@@ -0,0 +1,7 @@
<set xmlns:android="http://schemas.android.com/apk/res/android">
<translate
android:fromYDelta="-100%p"
android:toYDelta="0"
android:duration="300"/>
</set>

View File

@@ -0,0 +1,7 @@
<set xmlns:android="http://schemas.android.com/apk/res/android">
<translate
android:fromYDelta="0"
android:toYDelta="-100%p"
android:duration="300"/>
</set>

View File

@@ -1,4 +1,3 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout <androidx.constraintlayout.widget.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android" xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:app="http://schemas.android.com/apk/res-auto"
@@ -28,11 +27,39 @@
android:layout_margin="16dp" android:layout_margin="16dp"
style="@style/CustomSearchView" /> style="@style/CustomSearchView" />
<!-- 单选按钮组 -->
<RadioGroup
android:id="@+id/radioGroup2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:layout_marginTop="8dp">
<RadioButton
android:id="@+id/pb"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:checked="true"
android:text="Pixiv" />
<RadioButton
android:id="@+id/jmb"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="JM" />
<RadioButton
android:id="@+id/maimai"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Maimai" />
</RadioGroup>
<!-- TextView --> <!-- TextView -->
<com.google.android.material.textview.MaterialTextView <com.google.android.material.textview.MaterialTextView
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:text="FindPixiv" android:text="FindMaimai Engine"
android:textSize="18sp" android:textSize="18sp"
android:layout_marginTop="8dp" /> android:layout_marginTop="8dp" />
</LinearLayout> </LinearLayout>

View File

@@ -0,0 +1,51 @@
<androidx.constraintlayout.widget.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:id="@+id/back"
android:layout_height="match_parent">
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/recyclerView"
android:layout_width="0dp"
android:layout_height="0dp"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"/>
<androidx.coordinatorlayout.widget.CoordinatorLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent">
<androidx.coordinatorlayout.widget.CoordinatorLayout
android:id="@+id/bottom_sheet"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:minHeight="100dp"
android:background="@android:color/white"
app:layout_behavior="com.google.android.material.bottomsheet.BottomSheetBehavior">
<LinearLayout
android:id="@+id/bac"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:padding="16dp">
<TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:textSize="32sp"
android:textColor="@color/colorSecondaryVariant"
android:id="@+id/menu"/>
<TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:textSize="18sp"
android:textColor="@color/colorSecondaryVariant"
android:id="@+id/dec"/>
</LinearLayout>
</androidx.coordinatorlayout.widget.CoordinatorLayout>
</androidx.coordinatorlayout.widget.CoordinatorLayout>
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@@ -0,0 +1,11 @@
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/overlay"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#80000000"
android:visibility="gone"
android:clickable="true"
android:focusable="true">
<TextView android:layout_width="match_parent" android:layout_height="match_parent" android:id="@+id/title"/>
<!-- 你可以在这里添加其他 UI 元素,例如按钮或文本 -->
</FrameLayout>

View File

@@ -0,0 +1,11 @@
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<ImageView
android:id="@+id/photo"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:adjustViewBounds="true"
android:scaleType="centerCrop"/>
</FrameLayout>

View File

@@ -39,7 +39,7 @@
<string name="nav_header_title">FindMaimaiDX</string> <string name="nav_header_title">FindMaimaiDX</string>
<string name="nav_header_subtitle">Reisa</string> <string name="nav_header_subtitle">Reisa</string>
<string name="action_settings">设置</string> <string name="action_settings">设置</string>
<string name="menu_pixiv">pixiv</string> <string name="menu_pixiv">Image</string>
</resources> </resources>