新增ChatMessage.java建立聊天消息数据模型 #36

Merged
pjao9fvxr merged 2 commits from zhouzexin_branch into master 4 weeks ago

@ -0,0 +1,16 @@
package net.micode.notes.model;
public class ChatMessage {
public long id;
public int senderType; // 0:用户, 1:AI
public int msgType; // 0:普通文本, 1:提醒卡片
public String content;
public long createdAt;
public ChatMessage(int senderType, int msgType, String content) {
this.senderType = senderType;
this.msgType = msgType;
this.content = content;
this.createdAt = System.currentTimeMillis();
}
}

@ -0,0 +1,103 @@
package net.micode.notes.ui;
import android.content.ContentValues;
import android.content.Context;
import android.graphics.Color;
import android.graphics.Paint;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;
import net.micode.notes.R;
import net.micode.notes.data.Notes;
import net.micode.notes.data.Notes.NoteColumns;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
public class AgendaAdapter extends RecyclerView.Adapter<AgendaAdapter.AgendaViewHolder> {
private Context mContext;
private List<AgendaItem> mData = new ArrayList<>();
private OnAgendaActionListener mListener;
// [关键修复] 定义接口
public interface OnAgendaActionListener {
void onToggleComplete(AgendaItem item);
void onDelete(AgendaItem item);
}
public void setOnAgendaActionListener(OnAgendaActionListener listener) {
this.mListener = listener;
}
public static class AgendaItem {
public long id;
public String title;
public String timeLabel;
public long startTime;
public boolean isCompleted;
public boolean isSpecificTime() {
return timeLabel != null && timeLabel.contains(":");
}
}
public AgendaAdapter(Context context) {
this.mContext = context;
}
public void updateData(List<AgendaItem> newList) {
Collections.sort(newList, (a, b) -> {
if (a.isCompleted != b.isCompleted) return a.isCompleted ? 1 : -1;
if (a.isSpecificTime() != b.isSpecificTime()) return a.isSpecificTime() ? -1 : 1;
return Long.compare(a.startTime, b.startTime);
});
this.mData = newList;
notifyDataSetChanged();
}
@NonNull
@Override
public AgendaViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
View view = LayoutInflater.from(mContext).inflate(R.layout.agenda_item, parent, false);
return new AgendaViewHolder(view);
}
@Override
public void onBindViewHolder(@NonNull AgendaViewHolder holder, int position) {
AgendaItem item = mData.get(position);
holder.tvContent.setText(item.title);
holder.tvTime.setText(item.timeLabel);
if (item.isCompleted) {
holder.ivCheck.setImageResource(R.drawable.checkbox_checked);
holder.tvContent.setPaintFlags(holder.tvContent.getPaintFlags() | Paint.STRIKE_THRU_TEXT_FLAG);
holder.tvContent.setTextColor(Color.LTGRAY);
} else {
holder.ivCheck.setImageResource(R.drawable.checkbox_unchecked);
holder.tvContent.setPaintFlags(holder.tvContent.getPaintFlags() & (~Paint.STRIKE_THRU_TEXT_FLAG));
holder.tvContent.setTextColor(Color.parseColor("#333333"));
}
holder.ivCheck.setOnClickListener(v -> { if (mListener != null) mListener.onToggleComplete(item); });
holder.btnDelete.setOnClickListener(v -> { if (mListener != null) mListener.onDelete(item); });
}
@Override
public int getItemCount() { return mData.size(); }
public static class AgendaViewHolder extends RecyclerView.ViewHolder {
ImageView ivCheck, btnDelete;
TextView tvTime, tvContent;
public AgendaViewHolder(@NonNull View itemView) {
super(itemView);
ivCheck = itemView.findViewById(R.id.iv_agenda_check);
tvTime = itemView.findViewById(R.id.tv_agenda_time);
tvContent = itemView.findViewById(R.id.tv_agenda_content);
btnDelete = itemView.findViewById(R.id.btn_agenda_delete);
}
}
}

@ -0,0 +1,77 @@
package net.micode.notes.ui;
import android.graphics.Typeface;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;
import net.micode.notes.R;
import net.micode.notes.model.ChatMessage;
import java.util.List;
public class ChatAdapter extends RecyclerView.Adapter<ChatAdapter.ChatViewHolder> {
private List<ChatMessage> mMessages;
public ChatAdapter(List<ChatMessage> messages) {
this.mMessages = messages;
}
@Override
public int getItemViewType(int position) {
ChatMessage msg = mMessages.get(position);
if (msg.senderType == 0) return 0; // 用户消息
if (msg.msgType == 1) return 2; // 提醒卡片
return 1; // AI 普通回复
}
@NonNull
@Override
public ChatViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_chat_msg, parent, false);
return new ChatViewHolder(view);
}
@Override
public void onBindViewHolder(@NonNull ChatViewHolder holder, int position) {
ChatMessage msg = mMessages.get(position);
int viewType = getItemViewType(position);
// 隐藏所有,根据类型显示
holder.leftLayout.setVisibility(View.GONE);
holder.rightLayout.setVisibility(View.GONE);
if (viewType == 0) { // 用户
holder.rightLayout.setVisibility(View.VISIBLE);
holder.tvRight.setText(msg.content);
} else { // AI 或 提醒
holder.leftLayout.setVisibility(View.VISIBLE);
holder.tvLeft.setText(msg.content);
if (viewType == 2) { // 提醒特殊样式
holder.tvLeft.setBackgroundResource(R.drawable.bg_bubble_reminder);
holder.tvLeft.setTypeface(null, Typeface.BOLD);
holder.tvLeft.setText("📅 日程提醒:\n" + msg.content);
} else {
holder.tvLeft.setBackgroundResource(R.drawable.bg_bubble_ai);
holder.tvLeft.setTypeface(null, Typeface.NORMAL);
}
}
}
@Override
public int getItemCount() { return mMessages.size(); }
static class ChatViewHolder extends RecyclerView.ViewHolder {
View leftLayout, rightLayout;
TextView tvLeft, tvRight;
ChatViewHolder(View v) {
super(v);
leftLayout = v.findViewById(R.id.ll_left_layout);
rightLayout = v.findViewById(R.id.ll_right_layout);
tvLeft = v.findViewById(R.id.tv_msg_left);
tvRight = v.findViewById(R.id.tv_msg_right);
}
}
}

@ -0,0 +1,109 @@
package net.micode.notes.ui;
import android.app.Service;
import android.content.Context;
import android.content.Intent;
import android.graphics.PixelFormat;
import android.os.Build;
import android.os.IBinder;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
import android.view.WindowManager;
import net.micode.notes.R;
import net.micode.notes.data.Notes;
public class FloatingService extends Service {
private WindowManager windowManager;
private View floatingView;
private WindowManager.LayoutParams params;
@Override
public IBinder onBind(Intent intent) { return null; }
@Override
public void onCreate() {
super.onCreate();
windowManager = (WindowManager) getSystemService(WINDOW_SERVICE);
floatingView = LayoutInflater.from(this).inflate(R.layout.floating_window, null);
// 设置布局参数
int layoutFlag;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
layoutFlag = WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
} else {
layoutFlag = WindowManager.LayoutParams.TYPE_PHONE;
}
params = new WindowManager.LayoutParams(
WindowManager.LayoutParams.WRAP_CONTENT,
WindowManager.LayoutParams.WRAP_CONTENT,
layoutFlag,
WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE, // 保证外部可点击
PixelFormat.TRANSLUCENT
);
params.gravity = Gravity.TOP | Gravity.START;
params.x = 100;
params.y = 100;
// 挂载到窗口
windowManager.addView(floatingView, params);
// 设置交互逻辑
setupInteraction();
}
private void setupInteraction() {
View ball = floatingView.findViewById(R.id.iv_floating_ball);
ball.setOnTouchListener(new View.OnTouchListener() {
private int initialX, initialY;
private float initialTouchX, initialTouchY;
private long touchStartTime;
@Override
public boolean onTouch(View v, MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
initialX = params.x;
initialY = params.y;
initialTouchX = event.getRawX();
initialTouchY = event.getRawY();
touchStartTime = System.currentTimeMillis();
return true;
case MotionEvent.ACTION_MOVE:
params.x = initialX + (int) (event.getRawX() - initialTouchX);
params.y = initialY + (int) (event.getRawY() - initialTouchY);
windowManager.updateViewLayout(floatingView, params);
return true;
case MotionEvent.ACTION_UP:
// 如果按下时间短且移动距离小,判定为点击
if (System.currentTimeMillis() - touchStartTime < 200) {
openNewNote();
}
return true;
}
return false;
}
});
}
private void openNewNote() {
Intent intent = new Intent(this, NoteEditActivity.class);
intent.setAction(Intent.ACTION_INSERT_OR_EDIT);
intent.putExtra(Notes.INTENT_EXTRA_FOLDER_ID, Notes.ID_ROOT_FOLDER);
// [核心] Service 调起 Activity 必须加此 Flag
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
startActivity(intent);
}
@Override
public void onDestroy() {
super.onDestroy();
if (floatingView != null) windowManager.removeView(floatingView);
}
}
Loading…
Cancel
Save