216 lines
7.4 KiB
Java
216 lines
7.4 KiB
Java
package com.ttstd.dialer.adapter;
|
|
|
|
import android.content.ClipData;
|
|
import android.content.ComponentName;
|
|
import android.graphics.Canvas;
|
|
import android.graphics.Point;
|
|
import android.graphics.drawable.Drawable;
|
|
import android.os.Build;
|
|
import android.os.Bundle;
|
|
import android.view.DragEvent;
|
|
import android.view.HapticFeedbackConstants;
|
|
import android.view.LayoutInflater;
|
|
import android.view.View;
|
|
import android.view.ViewGroup;
|
|
import android.widget.BaseAdapter;
|
|
import android.widget.TextView;
|
|
|
|
import androidx.annotation.NonNull;
|
|
import androidx.annotation.Nullable;
|
|
import androidx.constraintlayout.widget.ConstraintLayout;
|
|
import androidx.fragment.app.FragmentActivity;
|
|
import androidx.loader.app.LoaderManager;
|
|
import androidx.loader.content.Loader;
|
|
|
|
import com.shehuan.niv.NiceImageView;
|
|
import com.tencent.mmkv.MMKV;
|
|
import com.ttstd.dialer.R;
|
|
import com.ttstd.dialer.config.CommonConfig;
|
|
import com.ttstd.dialer.db.app.AppInfo;
|
|
import com.ttstd.dialer.utils.ApkUtils;
|
|
import com.ttstd.dialer.utils.Logger;
|
|
import com.ttstd.iconloader.IconCacheManager;
|
|
import com.ttstd.iconloader.IconLoader;
|
|
|
|
import org.jetbrains.annotations.NotNull;
|
|
|
|
import java.util.List;
|
|
|
|
public class AppGridAdapter extends BaseAdapter implements LoaderManager.LoaderCallbacks<Drawable> {
|
|
private static final String TAG = "AppGridAdapter";
|
|
|
|
private MMKV mMMKV = MMKV.mmkvWithID(CommonConfig.MMKV_ID, MMKV.MULTI_PROCESS_MODE);
|
|
|
|
private FragmentActivity mContext;
|
|
private LoaderManager mLoaderManager;
|
|
private IconCacheManager mIconCacheManager = IconCacheManager.getInstance();
|
|
private List<AppInfo> mAppInfos;
|
|
|
|
public AppGridAdapter(FragmentActivity context, LoaderManager loaderManager) {
|
|
mContext = context;
|
|
mLoaderManager = loaderManager;
|
|
}
|
|
|
|
public void setAppInfos(List<AppInfo> appInfos) {
|
|
mAppInfos = appInfos;
|
|
notifyDataSetChanged();
|
|
}
|
|
|
|
@Override
|
|
public int getCount() {
|
|
return mAppInfos == null ? 0 : mAppInfos.size();
|
|
}
|
|
|
|
@Override
|
|
public Object getItem(int position) {
|
|
return mAppInfos == null ? null : mAppInfos.get(position);
|
|
}
|
|
|
|
@Override
|
|
public long getItemId(int position) {
|
|
return position;
|
|
}
|
|
|
|
@Override
|
|
public View getView(int position, View convertView, ViewGroup parent) {
|
|
ViewHolder holder;
|
|
if (convertView == null) {
|
|
convertView = LayoutInflater.from(mContext).inflate(R.layout.item_grid_app, parent, false);
|
|
holder = new ViewHolder();
|
|
holder.root = convertView.findViewById(R.id.root);
|
|
holder.iv_icon = convertView.findViewById(R.id.iv_icon);
|
|
holder.tv_app_name = convertView.findViewById(R.id.tv_app_name);
|
|
convertView.setTag(holder);
|
|
} else {
|
|
holder = (ViewHolder) convertView.getTag();
|
|
}
|
|
|
|
AppInfo appInfo = mAppInfos.get(position);
|
|
Drawable drawable = mIconCacheManager.getIcon(appInfo.getComponentName().flattenToShortString());
|
|
if (drawable != null) {
|
|
holder.iv_icon.setImageDrawable(drawable);
|
|
} else {
|
|
mLoaderManager.restartLoader(position, null, this).forceLoad();
|
|
}
|
|
holder.tv_app_name.setText(appInfo.getLabel());
|
|
|
|
holder.root.setOnClickListener(new View.OnClickListener() {
|
|
@Override
|
|
public void onClick(View v) {
|
|
ApkUtils.openApp(mContext, appInfo.getComponentName());
|
|
}
|
|
});
|
|
|
|
holder.root.setOnLongClickListener(new View.OnLongClickListener() {
|
|
@Override
|
|
public boolean onLongClick(View v) {
|
|
if (!v.isAttachedToWindow()) {
|
|
Logger.e(TAG, "View is not attached to window");
|
|
return false;
|
|
}
|
|
|
|
// 添加触感反馈
|
|
v.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS);
|
|
|
|
ClipData data = ClipData.newPlainText("appInfo", appInfo.getPackageName());
|
|
View.DragShadowBuilder shadowBuilder = new LauncherDragShadowBuilder(v);
|
|
|
|
// 使用 post 确保在当前事件处理完成后再开始拖拽,提高稳定性并减少崩溃
|
|
v.post(() -> {
|
|
boolean started = false;
|
|
try {
|
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
|
|
started = v.startDragAndDrop(data, shadowBuilder, appInfo, View.DRAG_FLAG_OPAQUE);
|
|
} else {
|
|
started = v.startDrag(data, shadowBuilder, appInfo, 0);
|
|
}
|
|
} catch (Exception e) {
|
|
Logger.e(TAG, "startDragAndDrop error: " + e.getMessage());
|
|
}
|
|
|
|
Logger.e(TAG, "startDragAndDrop result: " + started);
|
|
if (started) {
|
|
v.setAlpha(0.5f);
|
|
}
|
|
});
|
|
|
|
return true;
|
|
}
|
|
});
|
|
|
|
holder.root.setOnDragListener(new View.OnDragListener() {
|
|
@Override
|
|
public boolean onDrag(View v, DragEvent event) {
|
|
switch (event.getAction()) {
|
|
case DragEvent.ACTION_DRAG_STARTED:
|
|
if (event.getLocalState() == appInfo) {
|
|
v.setAlpha(0.5f);
|
|
return true;
|
|
}
|
|
return false;
|
|
case DragEvent.ACTION_DRAG_ENDED:
|
|
v.setAlpha(1.0f);
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
});
|
|
|
|
return convertView;
|
|
}
|
|
|
|
private static class LauncherDragShadowBuilder extends View.DragShadowBuilder {
|
|
public LauncherDragShadowBuilder(View view) {
|
|
super(view);
|
|
}
|
|
|
|
@Override
|
|
public void onProvideShadowMetrics(Point outShadowSize, Point outShadowTouchPoint) {
|
|
float scale = 1.2f;
|
|
int width = (int) (getView().getWidth() * scale);
|
|
int height = (int) (getView().getHeight() * scale);
|
|
outShadowSize.set(width, height);
|
|
outShadowTouchPoint.set(width / 2, height / 2);
|
|
}
|
|
|
|
@Override
|
|
public void onDrawShadow(Canvas canvas) {
|
|
float scale = 1.2f;
|
|
canvas.save();
|
|
canvas.scale(scale, scale);
|
|
// 确保在绘制阴影时视图是不透明的
|
|
float originalAlpha = getView().getAlpha();
|
|
getView().setAlpha(1.0f);
|
|
getView().draw(canvas);
|
|
getView().setAlpha(originalAlpha);
|
|
canvas.restore();
|
|
}
|
|
}
|
|
|
|
@NonNull
|
|
@NotNull
|
|
@Override
|
|
public Loader<Drawable> onCreateLoader(int id, @Nullable @org.jetbrains.annotations.Nullable Bundle args) {
|
|
AppInfo appInfo = mAppInfos.get(id);
|
|
ComponentName componentName = appInfo.getComponentName();
|
|
return new IconLoader(mContext, componentName, mIconCacheManager);
|
|
}
|
|
|
|
@Override
|
|
public void onLoadFinished(@NonNull @NotNull Loader<Drawable> loader, Drawable data) {
|
|
int position = loader.getId();
|
|
notifyDataSetChanged();
|
|
}
|
|
|
|
@Override
|
|
public void onLoaderReset(@NonNull @NotNull Loader<Drawable> loader) {
|
|
Logger.e(TAG, "onLoaderReset: ");
|
|
}
|
|
|
|
static class ViewHolder {
|
|
ConstraintLayout root;
|
|
NiceImageView iv_icon;
|
|
TextView tv_app_name;
|
|
}
|
|
}
|