main
parent
b83552c46b
commit
28790c9c65
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
After Width: | Height: | Size: 2.9 MiB |
Binary file not shown.
@ -0,0 +1,26 @@
|
||||
package net.micode.myapplication;
|
||||
|
||||
import android.content.Context;
|
||||
|
||||
import androidx.test.platform.app.InstrumentationRegistry;
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
/**
|
||||
* Instrumented test, which will execute on an Android device.
|
||||
*
|
||||
* @see <a href="http://d.android.com/tools/testing">Testing documentation</a>
|
||||
*/
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
public class ExampleInstrumentedTest {
|
||||
@Test
|
||||
public void useAppContext() {
|
||||
// Context of the app under test.
|
||||
Context appContext = InstrumentationRegistry.getInstrumentation().getTargetContext();
|
||||
assertEquals("net.micode.myapplication", appContext.getPackageName());
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,38 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
|
||||
<!-- Android 13+ 读取媒体图片 -->
|
||||
<uses-permission android:name="android.permission.READ_MEDIA_IMAGES" />
|
||||
<!-- Android 12 及以下的读取外部存储(兼容相册选择) -->
|
||||
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" android:maxSdkVersion="32" />
|
||||
<!-- Android 9 及以下写入公共下载目录 -->
|
||||
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" android:maxSdkVersion="28" />
|
||||
|
||||
<!-- HanLP可能需要的网络权限 -->
|
||||
<uses-permission android:name="android.permission.INTERNET" />
|
||||
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
|
||||
|
||||
<application
|
||||
android:name="com.example.myapplication.MyNoteApp"
|
||||
android:allowBackup="true"
|
||||
android:label="课程笔记共享系统"
|
||||
android:icon="@mipmap/ic_launcher"
|
||||
android:roundIcon="@mipmap/ic_launcher_round"
|
||||
android:supportsRtl="true"
|
||||
android:theme="@style/Theme.AppCompat.Light.NoActionBar">
|
||||
<activity android:name="com.example.myapplication.ui.LoginUI" />
|
||||
<activity android:name="com.example.myapplication.ui.RegisterUI" />
|
||||
<activity android:name="com.example.myapplication.ui.MainUI"
|
||||
android:exported="true">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
<category android:name="android.intent.category.LAUNCHER"/>
|
||||
</intent-filter>
|
||||
</activity>
|
||||
<activity android:name="com.example.myapplication.ui.SearchUI" />
|
||||
<activity android:name="com.example.myapplication.ui.UploadUI" />
|
||||
<activity android:name="com.example.myapplication.ui.NoteDetailUI" />
|
||||
<activity android:name="com.example.myapplication.ui.UserCenterUI" />
|
||||
<activity android:name="com.example.myapplication.ui.NoteEditUI" />
|
||||
</application>
|
||||
</manifest>
|
||||
@ -0,0 +1,16 @@
|
||||
package com.example.myapplication;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.SharedPreferences;
|
||||
import android.widget.Toast;
|
||||
|
||||
public class Utils {
|
||||
public static void toast(Context ctx, String msg) {
|
||||
Toast.makeText(ctx, msg, Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
|
||||
public static String getUser(Context ctx) {
|
||||
SharedPreferences prefs = ctx.getSharedPreferences("app_prefs", Context.MODE_PRIVATE);
|
||||
return prefs.getString("currentUser", "张三");
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,31 @@
|
||||
package com.example.myapplication.domain;
|
||||
|
||||
public class Chapter {
|
||||
private long id;
|
||||
private String name;
|
||||
private int orderIndex;
|
||||
private int courseId;
|
||||
private String keywords;
|
||||
private String aliases;
|
||||
|
||||
public Chapter() {}
|
||||
|
||||
public Chapter(long id, String name, int orderIndex) {
|
||||
this.id = id;
|
||||
this.name = name;
|
||||
this.orderIndex = orderIndex;
|
||||
}
|
||||
|
||||
public long getId() { return id; }
|
||||
public void setId(long id) { this.id = id; }
|
||||
public String getName() { return name; }
|
||||
public void setName(String name) { this.name = name; }
|
||||
public int getOrderIndex() { return orderIndex; }
|
||||
public void setOrderIndex(int orderIndex) { this.orderIndex = orderIndex; }
|
||||
public int getCourseId() { return courseId; }
|
||||
public void setCourseId(int courseId) { this.courseId = courseId; }
|
||||
public String getKeywords() { return keywords; }
|
||||
public void setKeywords(String keywords) { this.keywords = keywords; }
|
||||
public String getAliases() { return aliases; }
|
||||
public void setAliases(String aliases) { this.aliases = aliases; }
|
||||
}
|
||||
@ -0,0 +1,30 @@
|
||||
package com.example.myapplication.domain;
|
||||
|
||||
public class Comment {
|
||||
private long id;
|
||||
private String uploaderId;
|
||||
private long noteId;
|
||||
private String content;
|
||||
private String createdAt; // ISO string
|
||||
|
||||
public Comment() {}
|
||||
|
||||
public Comment(long id, String uploaderId, long noteId, String content, String createdAt) {
|
||||
this.id = id;
|
||||
this.uploaderId = uploaderId;
|
||||
this.noteId = noteId;
|
||||
this.content = content;
|
||||
this.createdAt = createdAt;
|
||||
}
|
||||
|
||||
public long getId() { return id; }
|
||||
public void setId(long id) { this.id = id; }
|
||||
public String getUploaderId() { return uploaderId; }
|
||||
public void setUploaderId(String uploaderId) { this.uploaderId = uploaderId; }
|
||||
public long getNoteId() { return noteId; }
|
||||
public void setNoteId(long noteId) { this.noteId = noteId; }
|
||||
public String getContent() { return content; }
|
||||
public void setContent(String content) { this.content = content; }
|
||||
public String getCreatedAt() { return createdAt; }
|
||||
public void setCreatedAt(String createdAt) { this.createdAt = createdAt; }
|
||||
}
|
||||
@ -0,0 +1,26 @@
|
||||
package com.example.myapplication.domain;
|
||||
|
||||
public class Course {
|
||||
private long id;
|
||||
private String description;
|
||||
private String name;
|
||||
private String code;
|
||||
|
||||
public Course() {}
|
||||
|
||||
public Course(long id, String description, String name, String code) {
|
||||
this.id = id;
|
||||
this.description = description;
|
||||
this.name = name;
|
||||
this.code = code;
|
||||
}
|
||||
|
||||
public long getId() { return id; }
|
||||
public void setId(long id) { this.id = id; }
|
||||
public String getDescription() { return description; }
|
||||
public void setDescription(String description) { this.description = description; }
|
||||
public String getName() { return name; }
|
||||
public void setName(String name) { this.name = name; }
|
||||
public String getCode() { return code; }
|
||||
public void setCode(String code) { this.code = code; }
|
||||
}
|
||||
@ -0,0 +1,22 @@
|
||||
package com.example.myapplication.domain;
|
||||
|
||||
public class Like {
|
||||
private String userId;
|
||||
private long noteId;
|
||||
private String createdAt;
|
||||
|
||||
public Like() {}
|
||||
|
||||
public Like(String userId, long noteId, String createdAt) {
|
||||
this.userId = userId;
|
||||
this.noteId = noteId;
|
||||
this.createdAt = createdAt;
|
||||
}
|
||||
|
||||
public String getUserId() { return userId; }
|
||||
public void setUserId(String userId) { this.userId = userId; }
|
||||
public long getNoteId() { return noteId; }
|
||||
public void setNoteId(long noteId) { this.noteId = noteId; }
|
||||
public String getCreatedAt() { return createdAt; }
|
||||
public void setCreatedAt(String createdAt) { this.createdAt = createdAt; }
|
||||
}
|
||||
@ -0,0 +1,26 @@
|
||||
package com.example.myapplication.domain;
|
||||
|
||||
public class Major {
|
||||
private long id;
|
||||
private String name;
|
||||
private String code;
|
||||
private String description;
|
||||
|
||||
public Major() {}
|
||||
|
||||
public Major(long id, String name, String code, String description) {
|
||||
this.id = id;
|
||||
this.name = name;
|
||||
this.code = code;
|
||||
this.description = description;
|
||||
}
|
||||
|
||||
public long getId() { return id; }
|
||||
public void setId(long id) { this.id = id; }
|
||||
public String getName() { return name; }
|
||||
public void setName(String name) { this.name = name; }
|
||||
public String getCode() { return code; }
|
||||
public void setCode(String code) { this.code = code; }
|
||||
public String getDescription() { return description; }
|
||||
public void setDescription(String description) { this.description = description; }
|
||||
}
|
||||
@ -0,0 +1,76 @@
|
||||
package com.example.myapplication.domain;
|
||||
|
||||
public class Note {
|
||||
private long id;
|
||||
private String uploaderId;
|
||||
private long courseId;
|
||||
private String content;
|
||||
private int downloadCount;
|
||||
private int likeCount;
|
||||
private String created;
|
||||
private String title;
|
||||
private String filePath;
|
||||
private Integer publisherGrade; // 发布者年级
|
||||
private String publisherMajor; // 发布者专业
|
||||
|
||||
// 额外字段用于 NoteUploadActivity
|
||||
private String description; // 描述
|
||||
private String ocrText; // OCR识别文本
|
||||
private long uploadTime; // 上传时间
|
||||
private long updateTime; // 更新时间
|
||||
private long chapterId; // 章节ID
|
||||
|
||||
public Note() {}
|
||||
|
||||
public Note(long id, String uploaderId, long courseId, String content,
|
||||
int downloadCount, int likeCount, String created, String title, String filePath,
|
||||
Integer publisherGrade, String publisherMajor) {
|
||||
this.id = id;
|
||||
this.uploaderId = uploaderId;
|
||||
this.courseId = courseId;
|
||||
this.content = content;
|
||||
this.downloadCount = downloadCount;
|
||||
this.likeCount = likeCount;
|
||||
this.created = created;
|
||||
this.title = title;
|
||||
this.filePath = filePath;
|
||||
this.publisherGrade = publisherGrade;
|
||||
this.publisherMajor = publisherMajor;
|
||||
}
|
||||
|
||||
public int getLikeCount() { return likeCount; }
|
||||
public void setLikeCount(int likeCount) { this.likeCount = likeCount; }
|
||||
|
||||
public long getId() { return id; }
|
||||
public void setId(long id) { this.id = id; }
|
||||
public String getUploaderId() { return uploaderId; }
|
||||
public void setUploaderId(String uploaderId) { this.uploaderId = uploaderId; }
|
||||
public long getCourseId() { return courseId; }
|
||||
public void setCourseId(long courseId) { this.courseId = courseId; }
|
||||
public String getContent() { return content; }
|
||||
public void setContent(String content) { this.content = content; }
|
||||
public int getDownloadCount() { return downloadCount; }
|
||||
public void setDownloadCount(int downloadCount) { this.downloadCount = downloadCount; }
|
||||
public String getCreated() { return created; }
|
||||
public void setCreated(String created) { this.created = created; }
|
||||
public String getTitle() { return title; }
|
||||
public void setTitle(String title) { this.title = title; }
|
||||
public String getFilePath() { return filePath; }
|
||||
public void setFilePath(String filePath) { this.filePath = filePath; }
|
||||
public Integer getPublisherGrade() { return publisherGrade; }
|
||||
public void setPublisherGrade(Integer publisherGrade) { this.publisherGrade = publisherGrade; }
|
||||
public String getPublisherMajor() { return publisherMajor; }
|
||||
public void setPublisherMajor(String publisherMajor) { this.publisherMajor = publisherMajor; }
|
||||
|
||||
// 新增方法用于 NoteUploadActivity
|
||||
public String getDescription() { return description; }
|
||||
public void setDescription(String description) { this.description = description; }
|
||||
public String getOcrText() { return ocrText; }
|
||||
public void setOcrText(String ocrText) { this.ocrText = ocrText; }
|
||||
public long getUploadTime() { return uploadTime; }
|
||||
public void setUploadTime(long uploadTime) { this.uploadTime = uploadTime; }
|
||||
public long getUpdateTime() { return updateTime; }
|
||||
public void setUpdateTime(long updateTime) { this.updateTime = updateTime; }
|
||||
public long getChapterId() { return chapterId; }
|
||||
public void setChapterId(long chapterId) { this.chapterId = chapterId; }
|
||||
}
|
||||
@ -0,0 +1,66 @@
|
||||
package com.example.myapplication.domain;
|
||||
|
||||
public class User {
|
||||
private String account;
|
||||
private String name;
|
||||
private String password;
|
||||
private int type;
|
||||
private String mobile;
|
||||
private int points;
|
||||
private Integer grade; // 年级(大几)
|
||||
private Long majorId; // 专业ID
|
||||
|
||||
public User() {}
|
||||
|
||||
public User(String account, String name, String password, int type, String mobile) {
|
||||
this.account = account;
|
||||
this.name = name;
|
||||
this.password = password;
|
||||
this.type = type;
|
||||
this.mobile = mobile;
|
||||
this.points = 0;
|
||||
this.grade = null;
|
||||
this.majorId = null;
|
||||
}
|
||||
|
||||
// 带积分的构造
|
||||
public User(String account, String name, String password, int type, String mobile, int points) {
|
||||
this.account = account;
|
||||
this.name = name;
|
||||
this.password = password;
|
||||
this.type = type;
|
||||
this.mobile = mobile;
|
||||
this.points = points;
|
||||
this.grade = null;
|
||||
this.majorId = null;
|
||||
}
|
||||
|
||||
// 带年级与专业的构造
|
||||
public User(String account, String name, String password, int type, String mobile, int points, Integer grade, Long majorId) {
|
||||
this.account = account;
|
||||
this.name = name;
|
||||
this.password = password;
|
||||
this.type = type;
|
||||
this.mobile = mobile;
|
||||
this.points = points;
|
||||
this.grade = grade;
|
||||
this.majorId = majorId;
|
||||
}
|
||||
|
||||
public String getAccount() { return account; }
|
||||
public void setAccount(String account) { this.account = account; }
|
||||
public String getName() { return name; }
|
||||
public void setName(String name) { this.name = name; }
|
||||
public String getPassword() { return password; }
|
||||
public void setPassword(String password) { this.password = password; }
|
||||
public int getType() { return type; }
|
||||
public void setType(int type) { this.type = type; }
|
||||
public String getMobile() { return mobile; }
|
||||
public void setMobile(String mobile) { this.mobile = mobile; }
|
||||
public int getPoints() { return points; }
|
||||
public void setPoints(int points) { this.points = points; }
|
||||
public Integer getGrade() { return grade; }
|
||||
public void setGrade(Integer grade) { this.grade = grade; }
|
||||
public Long getMajorId() { return majorId; }
|
||||
public void setMajorId(Long majorId) { this.majorId = majorId; }
|
||||
}
|
||||
@ -0,0 +1,66 @@
|
||||
package com.example.myapplication.repository;
|
||||
|
||||
import android.content.ContentValues;
|
||||
import android.content.Context;
|
||||
import android.database.Cursor;
|
||||
import android.database.sqlite.SQLiteDatabase;
|
||||
|
||||
import com.example.myapplication.domain.Comment;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class CommentRepository {
|
||||
private final Context context;
|
||||
private DatabaseHelper helper;
|
||||
private SQLiteDatabase db;
|
||||
|
||||
public CommentRepository(Context context) { this.context = context.getApplicationContext(); }
|
||||
|
||||
public void openDatabase() {
|
||||
if (helper == null) helper = new DatabaseHelper(context);
|
||||
if (db == null || !db.isOpen()) db = helper.getWritableDatabase();
|
||||
}
|
||||
|
||||
public void closeDatabase() { if (db != null && db.isOpen()) db.close(); db = null; }
|
||||
|
||||
public void close() { closeDatabase(); }
|
||||
|
||||
private Comment map(Cursor c) {
|
||||
Comment cm = new Comment();
|
||||
cm.setId(c.getLong(c.getColumnIndexOrThrow("id")));
|
||||
cm.setUploaderId(c.getString(c.getColumnIndexOrThrow("user_id")));
|
||||
cm.setNoteId(c.getLong(c.getColumnIndexOrThrow("note_id")));
|
||||
cm.setContent(c.getString(c.getColumnIndexOrThrow("content")));
|
||||
cm.setCreatedAt(c.getString(c.getColumnIndexOrThrow("created_at")));
|
||||
return cm;
|
||||
}
|
||||
|
||||
public List<Comment> listByNoteId(long noteId) {
|
||||
openDatabase();
|
||||
List<Comment> list = new ArrayList<>();
|
||||
try (Cursor c = db.query("T_Comment", null, "note_id=?", new String[]{String.valueOf(noteId)}, null, null, "created_at DESC")) {
|
||||
while (c != null && c.moveToNext()) list.add(map(c));
|
||||
}
|
||||
closeDatabase();
|
||||
return list;
|
||||
}
|
||||
|
||||
public long add(String userId, long noteId, String content) {
|
||||
openDatabase();
|
||||
ContentValues cv = new ContentValues();
|
||||
cv.put("user_id", userId);
|
||||
cv.put("note_id", noteId);
|
||||
cv.put("content", content);
|
||||
long r = db.insert("T_Comment", null, cv);
|
||||
closeDatabase();
|
||||
return r;
|
||||
}
|
||||
|
||||
public boolean delete(long id, String userId) {
|
||||
openDatabase();
|
||||
int rows = db.delete("T_Comment", "id=? AND user_id=?", new String[]{String.valueOf(id), userId});
|
||||
closeDatabase();
|
||||
return rows > 0;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,211 @@
|
||||
package com.example.myapplication.repository;
|
||||
|
||||
import android.content.ContentValues;
|
||||
import android.content.Context;
|
||||
import android.database.Cursor;
|
||||
import android.database.sqlite.SQLiteDatabase;
|
||||
|
||||
import com.example.myapplication.domain.Chapter;
|
||||
import com.example.myapplication.domain.Course;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
public class CourseRepository {
|
||||
private final Context context;
|
||||
private DatabaseHelper helper;
|
||||
private SQLiteDatabase db;
|
||||
|
||||
public CourseRepository(Context context) { this.context = context.getApplicationContext(); }
|
||||
|
||||
public void openDatabase() {
|
||||
if (helper == null) helper = new DatabaseHelper(context);
|
||||
if (db == null || !db.isOpen()) db = helper.getWritableDatabase();
|
||||
}
|
||||
|
||||
public void closeDatabase() { if (db != null && db.isOpen()) db.close(); db = null; }
|
||||
|
||||
public void close() { closeDatabase(); }
|
||||
|
||||
public List<Course> getAllCourses() {
|
||||
openDatabase();
|
||||
List<Course> list = new ArrayList<>();
|
||||
try (Cursor c = db.query("T_Subject", null, null, null, null, null, "name ASC")) {
|
||||
while (c != null && c.moveToNext()) list.add(map(c));
|
||||
}
|
||||
closeDatabase();
|
||||
return list;
|
||||
}
|
||||
|
||||
public List<Course> getCoursesByMajorAndGrade(Long majorId, String gradeText) {
|
||||
openDatabase();
|
||||
List<Course> list = new ArrayList<>();
|
||||
if (majorId == null || gradeText == null || gradeText.isEmpty()) {
|
||||
closeDatabase();
|
||||
return list;
|
||||
}
|
||||
String sql = "SELECT s.* FROM T_Subject s INNER JOIN T_Major_Subject ms ON ms.subject_id = s.id WHERE ms.major_id = ? AND ms.grade = ? ORDER BY s.name ASC";
|
||||
try (Cursor c = db.rawQuery(sql, new String[]{String.valueOf(majorId), gradeText})) {
|
||||
while (c != null && c.moveToNext()) list.add(map(c));
|
||||
}
|
||||
closeDatabase();
|
||||
return list;
|
||||
}
|
||||
|
||||
public List<Course> getCoursesByMajor(Long majorId) {
|
||||
openDatabase();
|
||||
List<Course> list = new ArrayList<>();
|
||||
if (majorId == null) { closeDatabase(); return list; }
|
||||
String sql = "SELECT DISTINCT s.* FROM T_Subject s INNER JOIN T_Major_Subject ms ON ms.subject_id = s.id WHERE ms.major_id = ? ORDER BY s.name ASC";
|
||||
try (Cursor c = db.rawQuery(sql, new String[]{String.valueOf(majorId)})) {
|
||||
while (c != null && c.moveToNext()) list.add(map(c));
|
||||
}
|
||||
closeDatabase();
|
||||
return list;
|
||||
}
|
||||
|
||||
public Course getCourseById(long courseId) {
|
||||
openDatabase();
|
||||
Course course = null;
|
||||
try (Cursor c = db.query("T_Subject", null, "id=?", new String[]{String.valueOf(courseId)}, null, null, null)) {
|
||||
if (c != null && c.moveToFirst()) course = map(c);
|
||||
}
|
||||
closeDatabase();
|
||||
return course;
|
||||
}
|
||||
|
||||
public List<Course> findSimilarCourses(String keywords) {
|
||||
openDatabase();
|
||||
List<Course> list = new ArrayList<>();
|
||||
String like = "%" + (keywords == null ? "" : keywords) + "%";
|
||||
try (Cursor c = db.query("T_Subject", null, "name LIKE ? OR description LIKE ? OR code LIKE ?",
|
||||
new String[]{like, like, like}, null, null, "name ASC")) {
|
||||
while (c != null && c.moveToNext()) list.add(map(c));
|
||||
}
|
||||
closeDatabase();
|
||||
return list;
|
||||
}
|
||||
|
||||
public Map<Course, List<Chapter>> getCourseCatalog() {
|
||||
Map<Course, List<Chapter>> map = new HashMap<>();
|
||||
List<Course> courses = getAllCourses();
|
||||
for (Course c : courses) {
|
||||
List<Chapter> chapters = getChaptersBySubjectId(c.getId());
|
||||
map.put(c, chapters);
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
// Optional: helper to insert/update courses
|
||||
public long upsertCourse(String name, String code, String description) {
|
||||
openDatabase();
|
||||
ContentValues cv = new ContentValues();
|
||||
cv.put("name", name);
|
||||
cv.put("code", code);
|
||||
cv.put("description", description);
|
||||
long id = db.insert("T_Subject", null, cv);
|
||||
closeDatabase();
|
||||
return id;
|
||||
}
|
||||
|
||||
// 新增方法:插入课程及其章节
|
||||
public void insertCourseWithChapters(Course course, List<Chapter> chapters) {
|
||||
openDatabase();
|
||||
try {
|
||||
db.beginTransaction();
|
||||
|
||||
// 插入课程
|
||||
ContentValues courseValues = new ContentValues();
|
||||
courseValues.put("id", course.getId());
|
||||
courseValues.put("name", course.getName());
|
||||
courseValues.put("code", course.getCode());
|
||||
courseValues.put("description", course.getDescription());
|
||||
db.insert("T_Subject", null, courseValues);
|
||||
|
||||
// 插入章节
|
||||
for (Chapter chapter : chapters) {
|
||||
ContentValues chapterValues = new ContentValues();
|
||||
chapterValues.put("id", chapter.getId());
|
||||
chapterValues.put("name", chapter.getName());
|
||||
chapterValues.put("subject_id", chapter.getCourseId());
|
||||
chapterValues.put("order_num", chapter.getOrderIndex());
|
||||
if (chapter.getKeywords() != null) chapterValues.put("keywords", chapter.getKeywords());
|
||||
if (chapter.getAliases() != null) chapterValues.put("aliases", chapter.getAliases());
|
||||
db.insert("T_Chapter", null, chapterValues);
|
||||
}
|
||||
|
||||
db.setTransactionSuccessful();
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
} finally {
|
||||
db.endTransaction();
|
||||
closeDatabase();
|
||||
}
|
||||
}
|
||||
|
||||
private Course map(Cursor c) {
|
||||
return new Course(
|
||||
c.getLong(c.getColumnIndexOrThrow("id")),
|
||||
c.getString(c.getColumnIndexOrThrow("description")),
|
||||
c.getString(c.getColumnIndexOrThrow("name")),
|
||||
c.getString(c.getColumnIndexOrThrow("code"))
|
||||
);
|
||||
}
|
||||
|
||||
public List<Chapter> getChaptersBySubjectId(long subjectId) {
|
||||
openDatabase();
|
||||
List<Chapter> list = new ArrayList<>();
|
||||
try (Cursor c = db.query("T_Chapter", null, "subject_id=?", new String[]{String.valueOf(subjectId)}, null, null, "order_num ASC")) {
|
||||
while (c != null && c.moveToNext()) {
|
||||
Chapter ch = new Chapter();
|
||||
ch.setId(c.getLong(c.getColumnIndexOrThrow("id")));
|
||||
ch.setName(c.getString(c.getColumnIndexOrThrow("name")));
|
||||
int oi = 0; try { oi = c.getInt(c.getColumnIndexOrThrow("order_num")); } catch (Exception ignored) {}
|
||||
ch.setOrderIndex(oi);
|
||||
ch.setCourseId((int) subjectId);
|
||||
try {
|
||||
int kwi = c.getColumnIndex("keywords");
|
||||
if (kwi >= 0 && !c.isNull(kwi)) ch.setKeywords(c.getString(kwi));
|
||||
} catch (Exception ignored) {}
|
||||
try {
|
||||
int ali = c.getColumnIndex("aliases");
|
||||
if (ali >= 0 && !c.isNull(ali)) ch.setAliases(c.getString(ali));
|
||||
} catch (Exception ignored) {}
|
||||
list.add(ch);
|
||||
}
|
||||
}
|
||||
closeDatabase();
|
||||
return list;
|
||||
}
|
||||
|
||||
public List<Chapter> findChaptersByNameLike(String q, int limit) {
|
||||
openDatabase();
|
||||
List<Chapter> list = new ArrayList<>();
|
||||
String like = "%" + (q == null ? "" : q) + "%";
|
||||
String lim = String.valueOf(Math.max(1, limit));
|
||||
try (Cursor c = db.query("T_Chapter", null, "name LIKE ? OR code LIKE ?", new String[]{like, like}, null, null, "order_num ASC", lim)) {
|
||||
while (c != null && c.moveToNext()) {
|
||||
Chapter ch = new Chapter();
|
||||
ch.setId(c.getLong(c.getColumnIndexOrThrow("id")));
|
||||
ch.setName(c.getString(c.getColumnIndexOrThrow("name")));
|
||||
int oi = 0; try { oi = c.getInt(c.getColumnIndexOrThrow("order_num")); } catch (Exception ignored) {}
|
||||
int sid = 0; try { sid = c.getInt(c.getColumnIndexOrThrow("subject_id")); } catch (Exception ignored) {}
|
||||
ch.setOrderIndex(oi);
|
||||
ch.setCourseId(sid);
|
||||
try {
|
||||
int kwi = c.getColumnIndex("keywords");
|
||||
if (kwi >= 0 && !c.isNull(kwi)) ch.setKeywords(c.getString(kwi));
|
||||
} catch (Exception ignored) {}
|
||||
try {
|
||||
int ali = c.getColumnIndex("aliases");
|
||||
if (ali >= 0 && !c.isNull(ali)) ch.setAliases(c.getString(ali));
|
||||
} catch (Exception ignored) {}
|
||||
list.add(ch);
|
||||
}
|
||||
}
|
||||
closeDatabase();
|
||||
return list;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,33 @@
|
||||
package com.example.myapplication.repository;
|
||||
|
||||
import android.content.Context;
|
||||
import android.database.sqlite.SQLiteDatabase;
|
||||
|
||||
public final class DatabaseManager {
|
||||
private static DatabaseHelper helper;
|
||||
private static SQLiteDatabase db;
|
||||
|
||||
private DatabaseManager() {}
|
||||
|
||||
public static synchronized void init(Context ctx) {
|
||||
if (helper == null) {
|
||||
helper = new DatabaseHelper(ctx.getApplicationContext());
|
||||
db = helper.getWritableDatabase(); // 打开并保持
|
||||
}
|
||||
}
|
||||
|
||||
public static synchronized SQLiteDatabase getDb() {
|
||||
if (db == null && helper != null) {
|
||||
db = helper.getWritableDatabase();
|
||||
}
|
||||
return db;
|
||||
}
|
||||
|
||||
public static synchronized void close() {
|
||||
if (helper != null) {
|
||||
helper.close();
|
||||
db = null;
|
||||
helper = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,81 @@
|
||||
package com.example.myapplication.repository;
|
||||
|
||||
import android.content.ContentValues;
|
||||
import android.content.Context;
|
||||
import android.database.Cursor;
|
||||
import android.database.sqlite.SQLiteDatabase;
|
||||
|
||||
public class LikeRepository {
|
||||
private final Context context;
|
||||
private DatabaseHelper helper;
|
||||
private SQLiteDatabase db;
|
||||
|
||||
public LikeRepository(Context context) { this.context = context.getApplicationContext(); }
|
||||
|
||||
public void openDatabase() {
|
||||
if (helper == null) helper = new DatabaseHelper(context);
|
||||
if (db == null || !db.isOpen()) db = helper.getWritableDatabase();
|
||||
}
|
||||
|
||||
public void closeDatabase() { if (db != null && db.isOpen()) db.close(); db = null; }
|
||||
|
||||
public boolean addLike(String userId, long noteId) {
|
||||
openDatabase();
|
||||
boolean ok = false;
|
||||
db.beginTransaction();
|
||||
try {
|
||||
ContentValues cv = new ContentValues();
|
||||
cv.put("user_id", userId);
|
||||
cv.put("note_id", noteId);
|
||||
long r = db.insertWithOnConflict("T_Like", null, cv, SQLiteDatabase.CONFLICT_IGNORE);
|
||||
if (r != -1) {
|
||||
db.execSQL("UPDATE T_Note SET like_count = COALESCE(like_count,0) + 1 WHERE id=?", new Object[]{noteId});
|
||||
ok = true;
|
||||
}
|
||||
db.setTransactionSuccessful();
|
||||
} finally {
|
||||
db.endTransaction();
|
||||
}
|
||||
closeDatabase();
|
||||
return ok;
|
||||
}
|
||||
|
||||
public boolean removeLike(String userId, long noteId) {
|
||||
openDatabase();
|
||||
boolean ok = false;
|
||||
db.beginTransaction();
|
||||
try {
|
||||
int rows = db.delete("T_Like", "user_id=? AND note_id=?", new String[]{userId, String.valueOf(noteId)});
|
||||
if (rows > 0) {
|
||||
db.execSQL("UPDATE T_Note SET like_count = CASE WHEN COALESCE(like_count,0) > 0 THEN like_count - 1 ELSE 0 END WHERE id=?", new Object[]{noteId});
|
||||
ok = true;
|
||||
}
|
||||
db.setTransactionSuccessful();
|
||||
} finally {
|
||||
db.endTransaction();
|
||||
}
|
||||
closeDatabase();
|
||||
return ok;
|
||||
}
|
||||
|
||||
public boolean checkUserLikeStatus(String userId, long noteId) {
|
||||
openDatabase();
|
||||
boolean exists = false;
|
||||
try (Cursor c = db.query("T_Like", new String[]{"user_id"}, "user_id=? AND note_id=?",
|
||||
new String[]{userId, String.valueOf(noteId)}, null, null, null)) {
|
||||
exists = c != null && c.moveToFirst();
|
||||
}
|
||||
closeDatabase();
|
||||
return exists;
|
||||
}
|
||||
|
||||
public int getLikeCount(long noteId) {
|
||||
openDatabase();
|
||||
int count = 0;
|
||||
try (Cursor c = db.rawQuery("SELECT COUNT(*) FROM T_Like WHERE note_id=?", new String[]{String.valueOf(noteId)})) {
|
||||
if (c != null && c.moveToFirst()) count = c.getInt(0);
|
||||
}
|
||||
closeDatabase();
|
||||
return count;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,56 @@
|
||||
package com.example.myapplication.repository;
|
||||
|
||||
import android.content.Context;
|
||||
import android.database.Cursor;
|
||||
import android.database.sqlite.SQLiteDatabase;
|
||||
|
||||
import com.example.myapplication.domain.Major;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class MajorRepository {
|
||||
private final Context context;
|
||||
private DatabaseHelper helper;
|
||||
private SQLiteDatabase db;
|
||||
|
||||
public MajorRepository(Context context) { this.context = context.getApplicationContext(); }
|
||||
|
||||
public void openDatabase() {
|
||||
if (helper == null) helper = new DatabaseHelper(context);
|
||||
if (db == null || !db.isOpen()) db = helper.getWritableDatabase();
|
||||
}
|
||||
|
||||
public void closeDatabase() { if (db != null && db.isOpen()) db.close(); db = null; }
|
||||
|
||||
public List<Major> findByNameLike(String keywords, int limit) {
|
||||
openDatabase();
|
||||
List<Major> list = new ArrayList<>();
|
||||
String like = "%" + (keywords == null ? "" : keywords.trim()) + "%";
|
||||
try (Cursor c = db.query("T_Major", null, "name LIKE ? OR code LIKE ?",
|
||||
new String[]{like, like}, null, null, "name ASC", String.valueOf(Math.max(1, limit)) )) {
|
||||
while (c != null && c.moveToNext()) list.add(map(c));
|
||||
}
|
||||
closeDatabase();
|
||||
return list;
|
||||
}
|
||||
|
||||
public Major getById(long id) {
|
||||
openDatabase();
|
||||
Major m = null;
|
||||
try (Cursor c = db.query("T_Major", null, "id=?", new String[]{String.valueOf(id)}, null, null, null)) {
|
||||
if (c != null && c.moveToFirst()) m = map(c);
|
||||
}
|
||||
closeDatabase();
|
||||
return m;
|
||||
}
|
||||
|
||||
private Major map(Cursor c) {
|
||||
return new Major(
|
||||
c.getLong(c.getColumnIndexOrThrow("id")),
|
||||
c.getString(c.getColumnIndexOrThrow("name")),
|
||||
c.getString(c.getColumnIndexOrThrow("code")),
|
||||
c.getString(c.getColumnIndexOrThrow("description"))
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,154 @@
|
||||
package com.example.myapplication.repository;
|
||||
|
||||
import android.content.ContentValues;
|
||||
import android.content.Context;
|
||||
import android.database.Cursor;
|
||||
import android.database.sqlite.SQLiteDatabase;
|
||||
|
||||
import com.example.myapplication.domain.User;
|
||||
|
||||
public class UserRepository {
|
||||
private final Context context;
|
||||
private DatabaseHelper helper;
|
||||
private SQLiteDatabase db;
|
||||
|
||||
public UserRepository(Context context) {
|
||||
this.context = context.getApplicationContext();
|
||||
}
|
||||
|
||||
public void openDatabase() {
|
||||
if (helper == null) helper = new DatabaseHelper(context);
|
||||
if (db == null || !db.isOpen()) db = helper.getWritableDatabase();
|
||||
}
|
||||
|
||||
public void closeDatabase() {
|
||||
if (db != null && db.isOpen()) db.close();
|
||||
db = null;
|
||||
}
|
||||
|
||||
public boolean insertUser(User user) {
|
||||
openDatabase();
|
||||
ContentValues cv = new ContentValues();
|
||||
cv.put("account", user.getAccount());
|
||||
cv.put("name", user.getName());
|
||||
cv.put("password", user.getPassword());
|
||||
cv.put("type", user.getType());
|
||||
cv.put("point", user.getPoints());
|
||||
if (user.getGrade() != null) cv.put("grade", String.valueOf(user.getGrade())); else cv.putNull("grade");
|
||||
if (user.getMajorId() != null) cv.put("major_id", user.getMajorId()); else cv.putNull("major_id");
|
||||
long r = db.insertWithOnConflict("T_User", null, cv, SQLiteDatabase.CONFLICT_REPLACE);
|
||||
closeDatabase();
|
||||
return r != -1;
|
||||
}
|
||||
|
||||
public boolean insertUserWithGradeText(User user, String gradeText) {
|
||||
openDatabase();
|
||||
ContentValues cv = new ContentValues();
|
||||
cv.put("account", user.getAccount());
|
||||
cv.put("name", user.getName());
|
||||
cv.put("password", user.getPassword());
|
||||
cv.put("type", user.getType());
|
||||
cv.put("point", user.getPoints());
|
||||
if (gradeText != null && !gradeText.isEmpty()) cv.put("grade", gradeText); else cv.putNull("grade");
|
||||
if (user.getMajorId() != null) cv.put("major_id", user.getMajorId()); else cv.putNull("major_id");
|
||||
long r = db.insertWithOnConflict("T_User", null, cv, SQLiteDatabase.CONFLICT_REPLACE);
|
||||
closeDatabase();
|
||||
return r != -1;
|
||||
}
|
||||
|
||||
public boolean deleteUser(User user) {
|
||||
openDatabase();
|
||||
int rows = db.delete("T_User", "account=?", new String[]{user.getAccount()});
|
||||
closeDatabase();
|
||||
return rows > 0;
|
||||
}
|
||||
|
||||
public boolean updateUser(User user) {
|
||||
openDatabase();
|
||||
ContentValues cv = new ContentValues();
|
||||
cv.put("name", user.getName());
|
||||
cv.put("password", user.getPassword());
|
||||
cv.put("type", user.getType());
|
||||
if (user.getPoints() >= 0) cv.put("point", user.getPoints());
|
||||
if (user.getGrade() != null) cv.put("grade", String.valueOf(user.getGrade()));
|
||||
if (user.getMajorId() != null) cv.put("major_id", user.getMajorId());
|
||||
int rows = db.update("T_User", cv, "account=?", new String[]{user.getAccount()});
|
||||
closeDatabase();
|
||||
return rows > 0;
|
||||
}
|
||||
|
||||
public User getUserByAccount(String account) {
|
||||
openDatabase();
|
||||
User user = null;
|
||||
try (Cursor c = db.query("T_User", null, "account=?", new String[]{account}, null, null, null)) {
|
||||
if (c != null && c.moveToFirst()) {
|
||||
Integer grade = null;
|
||||
try {
|
||||
int gi = c.getColumnIndex("grade");
|
||||
if (gi >= 0 && !c.isNull(gi)) {
|
||||
String gs = c.getString(gi);
|
||||
try { grade = gs == null ? null : Integer.parseInt(gs.trim()); } catch (Exception e) { grade = null; }
|
||||
}
|
||||
} catch (Exception ignored) {}
|
||||
Long majorId = null;
|
||||
try { int mi = c.getColumnIndex("major_id"); if (mi >= 0 && !c.isNull(mi)) majorId = c.getLong(mi); } catch (Exception ignored) {}
|
||||
user = new User(
|
||||
c.getString(c.getColumnIndexOrThrow("account")),
|
||||
c.getString(c.getColumnIndexOrThrow("name")),
|
||||
c.getString(c.getColumnIndexOrThrow("password")),
|
||||
c.getInt(c.getColumnIndexOrThrow("type")),
|
||||
null,
|
||||
c.getInt(c.getColumnIndexOrThrow("point")),
|
||||
grade,
|
||||
majorId
|
||||
);
|
||||
}
|
||||
}
|
||||
closeDatabase();
|
||||
return user;
|
||||
}
|
||||
|
||||
public boolean verifyUserValidity(String account, String password) {
|
||||
openDatabase();
|
||||
boolean ok = false;
|
||||
try (Cursor c = db.query("T_User", new String[]{"account"}, "account=? AND password=?",
|
||||
new String[]{account, password}, null, null, null)) {
|
||||
ok = c != null && c.moveToFirst();
|
||||
}
|
||||
closeDatabase();
|
||||
return ok;
|
||||
}
|
||||
|
||||
public int getPoints(String account) {
|
||||
openDatabase();
|
||||
int pts = 0;
|
||||
try (Cursor c = db.query("T_User", new String[]{"point"}, "account=?",
|
||||
new String[]{account}, null, null, null)) {
|
||||
if (c != null && c.moveToFirst()) pts = c.getInt(0);
|
||||
}
|
||||
closeDatabase();
|
||||
return pts;
|
||||
}
|
||||
|
||||
public String getGradeText(String account) {
|
||||
openDatabase();
|
||||
String grade = null;
|
||||
try (Cursor c = db.query("T_User", new String[]{"grade"}, "account=?",
|
||||
new String[]{account}, null, null, null)) {
|
||||
if (c != null && c.moveToFirst()) {
|
||||
int idx = c.getColumnIndex("grade");
|
||||
if (idx >= 0 && !c.isNull(idx)) grade = c.getString(idx);
|
||||
}
|
||||
}
|
||||
closeDatabase();
|
||||
return grade;
|
||||
}
|
||||
|
||||
public boolean addPoints(String account, int delta) {
|
||||
openDatabase();
|
||||
db.execSQL("UPDATE T_User SET point = COALESCE(point,0) + ? WHERE account=?",
|
||||
new Object[]{delta, account});
|
||||
closeDatabase();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,75 @@
|
||||
package com.example.myapplication.service;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.SharedPreferences;
|
||||
|
||||
public class AuthService {
|
||||
public static boolean login(Context ctx, String username, String password) {
|
||||
if (username == null || password == null) return false;
|
||||
username = username.trim();
|
||||
password = password.trim();
|
||||
if (username.isEmpty() || password.isEmpty()) return false;
|
||||
|
||||
// verify against repository
|
||||
com.example.myapplication.repository.UserRepository repo = new com.example.myapplication.repository.UserRepository(ctx);
|
||||
boolean valid = repo.verifyUserValidity(username, password);
|
||||
if (!valid) return false;
|
||||
|
||||
SharedPreferences prefs = ctx.getSharedPreferences("app_prefs", Context.MODE_PRIVATE);
|
||||
prefs.edit()
|
||||
.putString("currentUser", username)
|
||||
.putBoolean("isLoggedIn", true)
|
||||
.apply();
|
||||
return true;
|
||||
}
|
||||
|
||||
public static void logout(Context ctx) {
|
||||
SharedPreferences prefs = ctx.getSharedPreferences("app_prefs", Context.MODE_PRIVATE);
|
||||
prefs.edit()
|
||||
.remove("isLoggedIn")
|
||||
.remove("currentUser")
|
||||
.remove("searchKeyword")
|
||||
.remove("filterSubject")
|
||||
.remove("currentNoteId")
|
||||
.apply();
|
||||
}
|
||||
|
||||
public static boolean isLoggedIn(Context ctx) {
|
||||
SharedPreferences prefs = ctx.getSharedPreferences("app_prefs", Context.MODE_PRIVATE);
|
||||
return prefs.getBoolean("isLoggedIn", false);
|
||||
}
|
||||
|
||||
public static String getCurrentUser(Context ctx) {
|
||||
SharedPreferences prefs = ctx.getSharedPreferences("app_prefs", Context.MODE_PRIVATE);
|
||||
return prefs.getString("currentUser", "张三");
|
||||
}
|
||||
|
||||
public static String validateRegistration(String username, String password, String confirm, String gradeName, Long majorId) {
|
||||
if (username == null || password == null || confirm == null || gradeName == null || majorId == null) return "请输入完整信息";
|
||||
username = username.trim();
|
||||
password = password.trim();
|
||||
confirm = confirm.trim();
|
||||
if (username.length() < 3 || username.length() > 20) {
|
||||
return "用户名长度应为3-20个字符";
|
||||
}
|
||||
if (password.length() < 6) {
|
||||
return "密码长度不能少于6位";
|
||||
}
|
||||
if (!password.equals(confirm)) {
|
||||
return "两次输入的密码不一致";
|
||||
}
|
||||
java.util.List<String> allowed = java.util.Arrays.asList("大一","大二","大三","大四");
|
||||
if (!allowed.contains(gradeName)) {
|
||||
return "请选择正确的年级";
|
||||
}
|
||||
return null; // null 表示校验通过
|
||||
}
|
||||
|
||||
// Persist user to repository after validation
|
||||
public static boolean register(Context ctx, String username, String password, String gradeName, Long majorId) {
|
||||
com.example.myapplication.repository.UserRepository repo = new com.example.myapplication.repository.UserRepository(ctx);
|
||||
com.example.myapplication.domain.User u =
|
||||
new com.example.myapplication.domain.User(username, username, password, 0, "", 10, null, majorId);
|
||||
return repo.insertUserWithGradeText(u, gradeName);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,168 @@
|
||||
package com.example.myapplication.service;
|
||||
|
||||
import android.content.Context;
|
||||
import android.text.TextUtils;
|
||||
import com.example.myapplication.repository.CourseRepository;
|
||||
import com.example.myapplication.domain.Course;
|
||||
import com.example.myapplication.domain.Chapter;
|
||||
import com.hankcs.hanlp.HanLP;
|
||||
import com.hankcs.hanlp.seg.common.Term;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Set;
|
||||
|
||||
public class ClassificationService {
|
||||
public static class Result {
|
||||
public String category = "";
|
||||
public String chapter = "";
|
||||
public String bestId = "";
|
||||
public String bestTitle = "";
|
||||
public double confidence = 0.0;
|
||||
public boolean isSegmented = false;
|
||||
public java.util.List<Candidate> topCandidates = new java.util.ArrayList<>();
|
||||
}
|
||||
|
||||
public static class Candidate {
|
||||
public long chapterId;
|
||||
public long subjectId;
|
||||
public String title;
|
||||
public double score;
|
||||
}
|
||||
|
||||
private final Context context;
|
||||
private final CourseRepository courseRepository;
|
||||
private boolean hanLPInitialized = false;
|
||||
|
||||
public ClassificationService(Context ctx) {
|
||||
this.context = ctx == null ? null : ctx.getApplicationContext();
|
||||
this.courseRepository = new CourseRepository(this.context);
|
||||
try {
|
||||
List<Term> testSegment = HanLP.segment("测试HanLP");
|
||||
hanLPInitialized = testSegment != null;
|
||||
} catch (Throwable e) {
|
||||
hanLPInitialized = false;
|
||||
}
|
||||
}
|
||||
|
||||
public Result classifyWithSegmentation(String text) {
|
||||
Result out = new Result();
|
||||
if (text == null || text.trim().isEmpty()) return out;
|
||||
Set<String> noteTokens = extractKeywords(text);
|
||||
List<Course> courses = courseRepository.getAllCourses();
|
||||
double bestScore = 0.0;
|
||||
Course bestCourse = null;
|
||||
Chapter bestChapter = null;
|
||||
java.util.List<Candidate> all = new java.util.ArrayList<>();
|
||||
for (Course course : courses) {
|
||||
List<Chapter> chapters = courseRepository.getChaptersBySubjectId(course.getId());
|
||||
for (Chapter ch : chapters) {
|
||||
double score = calculateMatchScore(noteTokens, course, ch);
|
||||
if (score > 0) {
|
||||
if (score > bestScore) {
|
||||
bestScore = score;
|
||||
bestCourse = course;
|
||||
bestChapter = ch;
|
||||
}
|
||||
Candidate cand = new Candidate();
|
||||
cand.chapterId = ch.getId();
|
||||
cand.subjectId = course.getId();
|
||||
cand.title = safe(ch.getName());
|
||||
cand.score = score;
|
||||
all.add(cand);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (bestCourse != null && bestChapter != null) {
|
||||
out.category = safe(courseName(bestCourse));
|
||||
out.chapter = safe(bestChapter.getName());
|
||||
out.bestId = String.valueOf(bestChapter.getId());
|
||||
out.bestTitle = out.chapter;
|
||||
out.confidence = Math.max(0.0, Math.min(1.0, bestScore));
|
||||
}
|
||||
if (!all.isEmpty()) {
|
||||
java.util.Collections.sort(all, (a,b) -> Double.compare(b.score, a.score));
|
||||
int k = Math.min(3, all.size());
|
||||
for (int i = 0; i < k; i++) out.topCandidates.add(all.get(i));
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
private String courseName(Course c) {
|
||||
String n = c.getName();
|
||||
if (TextUtils.isEmpty(n)) n = c.getDescription();
|
||||
return n == null ? "" : n;
|
||||
}
|
||||
|
||||
private String safe(String s) { return s == null ? "" : s; }
|
||||
|
||||
private Set<String> extractKeywords(String text) {
|
||||
Set<String> tokens = new HashSet<>();
|
||||
if (hanLPInitialized) {
|
||||
try {
|
||||
List<Term> terms = HanLP.segment(text);
|
||||
for (Term t : terms) {
|
||||
String w = t.word;
|
||||
if (w != null && w.length() > 1) tokens.add(w.toLowerCase(Locale.ROOT));
|
||||
}
|
||||
return tokens;
|
||||
} catch (Throwable ignored) {}
|
||||
}
|
||||
String[] parts = text.toLowerCase(Locale.ROOT).split("[^\u4e00-\u9fa5a-zA-Z0-9]+");
|
||||
for (String p : parts) if (p.length() > 1) tokens.add(p);
|
||||
return tokens;
|
||||
}
|
||||
|
||||
private double calculateMatchScore(Set<String> noteTokens, Course course, Chapter ch) {
|
||||
double total = 0.0;
|
||||
List<String> titleTokens = tokenize(ch.getName());
|
||||
List<String> keywordTokens = splitComma(ch.getKeywords());
|
||||
List<String> aliasTokens = splitComma(ch.getAliases());
|
||||
total += overlapScore(noteTokens, keywordTokens) * 0.5;
|
||||
total += overlapScore(noteTokens, titleTokens) * 0.3;
|
||||
total += overlapScore(noteTokens, aliasTokens) * 0.2;
|
||||
if (total < 0.2) {
|
||||
List<String> courseTokens = tokenize(courseName(course));
|
||||
total = Math.max(total, overlapScore(noteTokens, courseTokens) * 0.2);
|
||||
}
|
||||
return total;
|
||||
}
|
||||
|
||||
private List<String> tokenize(String text) {
|
||||
List<String> out = new ArrayList<>();
|
||||
if (text == null || text.isEmpty()) return out;
|
||||
if (hanLPInitialized) {
|
||||
try {
|
||||
List<Term> terms = HanLP.segment(text);
|
||||
for (Term t : terms) {
|
||||
String w = t.word;
|
||||
if (w != null && w.length() > 1) out.add(w.toLowerCase(Locale.ROOT));
|
||||
}
|
||||
return out;
|
||||
} catch (Throwable ignored) {}
|
||||
}
|
||||
String[] parts = text.toLowerCase(Locale.ROOT).split("[^\u4e00-\u9fa5a-zA-Z0-9]+");
|
||||
for (String p : parts) if (p.length() > 1) out.add(p);
|
||||
return out;
|
||||
}
|
||||
|
||||
private List<String> splitComma(String s) {
|
||||
List<String> out = new ArrayList<>();
|
||||
if (s == null || s.trim().isEmpty()) return out;
|
||||
String[] arr = s.split(",");
|
||||
for (String x : arr) {
|
||||
String v = x.trim();
|
||||
if (!v.isEmpty()) out.add(v.toLowerCase(Locale.ROOT));
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
private double overlapScore(Set<String> noteTokens, List<String> targetTokens) {
|
||||
if (noteTokens.isEmpty() || targetTokens == null || targetTokens.isEmpty()) return 0.0;
|
||||
int hit = 0;
|
||||
for (String t : targetTokens) if (noteTokens.contains(t)) hit++;
|
||||
double base = (double) hit / (double) Math.max(1, targetTokens.size());
|
||||
return Math.max(0.0, Math.min(1.0, base));
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,37 @@
|
||||
package com.example.myapplication.service;
|
||||
|
||||
import android.content.Context;
|
||||
|
||||
import com.example.myapplication.domain.Comment;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class CommentService {
|
||||
public static List<Comment> listComments(Context ctx, long noteId) {
|
||||
com.example.myapplication.repository.CommentRepository repo = new com.example.myapplication.repository.CommentRepository(ctx);
|
||||
return repo.listByNoteId(noteId);
|
||||
}
|
||||
|
||||
public static String validateContent(String content) {
|
||||
if (content == null) return "内容不能为空";
|
||||
String t = content.trim();
|
||||
if (t.isEmpty()) return "内容不能为空";
|
||||
if (t.length() > 500) return "内容过长";
|
||||
return null;
|
||||
}
|
||||
|
||||
public static boolean addComment(Context ctx, String userId, long noteId, String content) {
|
||||
String err = validateContent(content);
|
||||
if (err != null) return false;
|
||||
if (userId == null || userId.trim().isEmpty()) return false;
|
||||
com.example.myapplication.repository.CommentRepository repo = new com.example.myapplication.repository.CommentRepository(ctx);
|
||||
long r = repo.add(userId, noteId, content.trim());
|
||||
return r != -1;
|
||||
}
|
||||
|
||||
public static boolean deleteOwn(Context ctx, long id) {
|
||||
String userId = com.example.myapplication.service.AuthService.getCurrentUser(ctx);
|
||||
com.example.myapplication.repository.CommentRepository repo = new com.example.myapplication.repository.CommentRepository(ctx);
|
||||
return repo.delete(id, userId);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,118 @@
|
||||
package com.example.myapplication.service;
|
||||
|
||||
import android.content.ContentResolver;
|
||||
import android.content.ContentValues;
|
||||
import android.content.Context;
|
||||
import android.net.Uri;
|
||||
import android.os.Build;
|
||||
import android.os.Environment;
|
||||
import android.provider.MediaStore;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.OutputStream;
|
||||
|
||||
public class DownloadService {
|
||||
public static boolean saveToDownloads(Context ctx, String srcPath, String displayName, String mime) {
|
||||
if (srcPath == null || srcPath.isEmpty()) return false;
|
||||
File src = new File(srcPath);
|
||||
if (!src.exists()) return false;
|
||||
if (displayName == null || displayName.trim().isEmpty()) {
|
||||
displayName = src.getName();
|
||||
}
|
||||
if (mime == null || mime.isEmpty()) mime = guessMime(displayName);
|
||||
|
||||
try {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
|
||||
ContentResolver resolver = ctx.getContentResolver();
|
||||
ContentValues values = new ContentValues();
|
||||
values.put(MediaStore.Downloads.DISPLAY_NAME, displayName);
|
||||
values.put(MediaStore.Downloads.MIME_TYPE, mime);
|
||||
values.put(MediaStore.Downloads.RELATIVE_PATH, Environment.DIRECTORY_DOWNLOADS);
|
||||
Uri uri = resolver.insert(MediaStore.Downloads.EXTERNAL_CONTENT_URI, values);
|
||||
if (uri == null) return false;
|
||||
try (FileInputStream in = new FileInputStream(src);
|
||||
OutputStream out = resolver.openOutputStream(uri)) {
|
||||
if (out == null) return false;
|
||||
byte[] buf = new byte[8192];
|
||||
int len;
|
||||
while ((len = in.read(buf)) != -1) out.write(buf, 0, len);
|
||||
out.flush();
|
||||
}
|
||||
return true;
|
||||
} else {
|
||||
File dir = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS);
|
||||
if (dir != null && !dir.exists()) dir.mkdirs();
|
||||
File dst = new File(dir, displayName);
|
||||
try (FileInputStream in = new FileInputStream(src);
|
||||
FileOutputStream out = new FileOutputStream(dst)) {
|
||||
byte[] buf = new byte[8192];
|
||||
int len;
|
||||
while ((len = in.read(buf)) != -1) out.write(buf, 0, len);
|
||||
out.flush();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
} catch (Exception e) {
|
||||
android.util.Log.e("DownloadService", "saveToDownloads failed", e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public static String guessMime(String name) {
|
||||
String lower = name.toLowerCase();
|
||||
if (lower.endsWith(".pdf")) return "application/pdf";
|
||||
if (lower.endsWith(".jpg") || lower.endsWith(".jpeg")) return "image/jpeg";
|
||||
if (lower.endsWith(".png")) return "image/png";
|
||||
if (lower.endsWith(".txt")) return "text/plain";
|
||||
if (lower.endsWith(".doc")) return "application/msword";
|
||||
if (lower.endsWith(".docx")) return "application/vnd.openxmlformats-officedocument.wordprocessingml.document";
|
||||
return "application/octet-stream";
|
||||
}
|
||||
|
||||
public static boolean saveTextToDownloads(Context ctx, String displayName, String mime, String content) {
|
||||
android.net.Uri uri = saveTextToDownloadsReturnUri(ctx, displayName, mime, content);
|
||||
return uri != null;
|
||||
}
|
||||
|
||||
public static android.net.Uri saveTextToDownloadsReturnUri(Context ctx, String displayName, String mime, String content) {
|
||||
if (displayName == null || displayName.trim().isEmpty()) displayName = System.currentTimeMillis() + ".txt";
|
||||
if (mime == null || mime.isEmpty()) mime = "text/plain";
|
||||
if (content == null) content = "";
|
||||
try {
|
||||
byte[] data = content.getBytes("UTF-8");
|
||||
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.Q) {
|
||||
android.content.ContentResolver resolver = ctx.getContentResolver();
|
||||
android.content.ContentValues values = new android.content.ContentValues();
|
||||
values.put(android.provider.MediaStore.MediaColumns.DISPLAY_NAME, displayName);
|
||||
values.put(android.provider.MediaStore.MediaColumns.MIME_TYPE, mime);
|
||||
values.put(android.provider.MediaStore.MediaColumns.RELATIVE_PATH, android.os.Environment.DIRECTORY_DOWNLOADS);
|
||||
values.put(android.provider.MediaStore.MediaColumns.IS_PENDING, 1);
|
||||
android.net.Uri uri = resolver.insert(android.provider.MediaStore.Files.getContentUri("external"), values);
|
||||
if (uri == null) return null;
|
||||
try (java.io.OutputStream out = resolver.openOutputStream(uri)) {
|
||||
if (out == null) return null;
|
||||
out.write(data);
|
||||
out.flush();
|
||||
}
|
||||
android.content.ContentValues done = new android.content.ContentValues();
|
||||
done.put(android.provider.MediaStore.MediaColumns.IS_PENDING, 0);
|
||||
resolver.update(uri, done, null, null);
|
||||
return uri;
|
||||
} else {
|
||||
java.io.File dir = android.os.Environment.getExternalStoragePublicDirectory(android.os.Environment.DIRECTORY_DOWNLOADS);
|
||||
if (dir != null && !dir.exists()) dir.mkdirs();
|
||||
java.io.File dst = new java.io.File(dir, displayName);
|
||||
try (java.io.FileOutputStream out = new java.io.FileOutputStream(dst)) {
|
||||
out.write(data);
|
||||
out.flush();
|
||||
}
|
||||
return android.net.Uri.fromFile(dst);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
android.util.Log.e("DownloadService", "saveTextToDownloadsReturnUri failed", e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,28 @@
|
||||
package com.example.myapplication.service;
|
||||
|
||||
public class LikeService {
|
||||
public static int increment(int current) {
|
||||
return current + 1;
|
||||
}
|
||||
|
||||
// 新增:使用点赞仓库
|
||||
public static boolean addLike(android.content.Context ctx, String userId, long noteId) {
|
||||
com.example.myapplication.repository.LikeRepository repo = new com.example.myapplication.repository.LikeRepository(ctx);
|
||||
return repo.addLike(userId, noteId);
|
||||
}
|
||||
|
||||
public static boolean removeLike(android.content.Context ctx, String userId, long noteId) {
|
||||
com.example.myapplication.repository.LikeRepository repo = new com.example.myapplication.repository.LikeRepository(ctx);
|
||||
return repo.removeLike(userId, noteId);
|
||||
}
|
||||
|
||||
public static boolean hasLiked(android.content.Context ctx, String userId, long noteId) {
|
||||
com.example.myapplication.repository.LikeRepository repo = new com.example.myapplication.repository.LikeRepository(ctx);
|
||||
return repo.checkUserLikeStatus(userId, noteId);
|
||||
}
|
||||
|
||||
public static int likeCount(android.content.Context ctx, long noteId) {
|
||||
com.example.myapplication.repository.LikeRepository repo = new com.example.myapplication.repository.LikeRepository(ctx);
|
||||
return repo.getLikeCount(noteId);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,20 @@
|
||||
package com.example.myapplication.service;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.SharedPreferences;
|
||||
|
||||
public class NoteService {
|
||||
public static void setCurrentNoteId(Context ctx, int id) {
|
||||
SharedPreferences prefs = ctx.getSharedPreferences("app_prefs", Context.MODE_PRIVATE);
|
||||
prefs.edit().putInt("currentNoteId", id).apply();
|
||||
}
|
||||
|
||||
public static int getCurrentNoteId(Context ctx) {
|
||||
SharedPreferences prefs = ctx.getSharedPreferences("app_prefs", Context.MODE_PRIVATE);
|
||||
return prefs.getInt("currentNoteId", -1);
|
||||
}
|
||||
|
||||
public static int incrementLikeCount(int current) {
|
||||
return current + 1;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,20 @@
|
||||
package com.example.myapplication.service;
|
||||
|
||||
public class RegistrationService {
|
||||
public static String validateRegistration(String username, String password, String confirm) {
|
||||
if (username == null || password == null || confirm == null) return "请输入完整信息";
|
||||
username = username.trim();
|
||||
password = password.trim();
|
||||
confirm = confirm.trim();
|
||||
if (username.length() < 3 || username.length() > 20) {
|
||||
return "用户名长度应为3-20个字符";
|
||||
}
|
||||
if (password.length() < 6) {
|
||||
return "密码长度不能少于6位";
|
||||
}
|
||||
if (!password.equals(confirm)) {
|
||||
return "两次输入的密码不一致";
|
||||
}
|
||||
return null; // null 表示校验通过
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,16 @@
|
||||
package com.example.myapplication.service;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.SharedPreferences;
|
||||
|
||||
public class SearchService {
|
||||
public static void saveKeyword(Context ctx, String keyword) {
|
||||
SharedPreferences prefs = ctx.getSharedPreferences("app_prefs", Context.MODE_PRIVATE);
|
||||
prefs.edit().putString("searchKeyword", keyword).apply();
|
||||
}
|
||||
|
||||
public static java.util.List<com.example.myapplication.domain.Note> search(Context ctx, String query) {
|
||||
com.example.myapplication.repository.NoteRepository repo = new com.example.myapplication.repository.NoteRepository(ctx);
|
||||
return repo.findNotesByKeywords(query, null);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,69 @@
|
||||
package com.example.myapplication.service;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.Bitmap;
|
||||
import android.util.Log;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.InputStream;
|
||||
|
||||
public class TesseractService {
|
||||
private static final String TAG = "TesseractService";
|
||||
private static final String TESSDATA_DIR = "tessdata";
|
||||
private static final String[] REQUIRED_LANGS = new String[]{"chi_sim.traineddata", "eng.traineddata"};
|
||||
|
||||
public static boolean ensureTessDataInstalled(Context ctx) {
|
||||
try {
|
||||
File tessDir = new File(ctx.getFilesDir(), TESSDATA_DIR);
|
||||
if (!tessDir.exists()) tessDir.mkdirs();
|
||||
// 拷贝 assets/tessdata 下的模型到 files/tessdata
|
||||
for (String lang : REQUIRED_LANGS) {
|
||||
File out = new File(tessDir, lang);
|
||||
if (out.exists() && out.length() > 0) continue;
|
||||
try (InputStream is = ctx.getAssets().open(TESSDATA_DIR + "/" + lang)) {
|
||||
try (FileOutputStream fos = new FileOutputStream(out)) {
|
||||
byte[] buf = new byte[8192];
|
||||
int r;
|
||||
while ((r = is.read(buf)) > 0) fos.write(buf, 0, r);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
Log.w(TAG, "缺少模型文件: " + lang + ",请将其放入 assets/tessdata/", e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, "安装tessdata失败", e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public static String recognizeBitmap(Context ctx, Bitmap bmp, String lang, int psm, int oem) {
|
||||
if (bmp == null) return "";
|
||||
boolean ok = ensureTessDataInstalled(ctx);
|
||||
if (!ok) return "";
|
||||
String dataPath = ctx.getFilesDir().getAbsolutePath();
|
||||
com.googlecode.tesseract.android.TessBaseAPI api = new com.googlecode.tesseract.android.TessBaseAPI();
|
||||
try {
|
||||
if (!api.init(dataPath, lang)) {
|
||||
Log.e(TAG, "Tesseract初始化失败");
|
||||
return "";
|
||||
}
|
||||
try {
|
||||
api.setPageSegMode(psm);
|
||||
} catch (Throwable ignored) {}
|
||||
try {
|
||||
api.setVariable("user_defined_dpi", "300");
|
||||
} catch (Throwable ignored) {}
|
||||
api.setImage(bmp);
|
||||
String text = api.getUTF8Text();
|
||||
return text == null ? "" : text;
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, "Tesseract识别失败", e);
|
||||
return "";
|
||||
} finally {
|
||||
try { api.end(); } catch (Throwable ignored) {}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,94 @@
|
||||
package com.example.myapplication.service;
|
||||
|
||||
import android.content.ContentResolver;
|
||||
import android.content.Context;
|
||||
import android.database.Cursor;
|
||||
import android.net.Uri;
|
||||
import android.os.Handler;
|
||||
import android.provider.OpenableColumns;
|
||||
import android.util.Log;
|
||||
|
||||
import com.google.mlkit.vision.common.InputImage;
|
||||
import com.google.mlkit.vision.text.TextRecognition;
|
||||
import com.google.mlkit.vision.text.chinese.ChineseTextRecognizerOptions;
|
||||
|
||||
// Keep only PDFBox and I/O for text extraction
|
||||
import com.tom_roush.pdfbox.pdmodel.PDDocument;
|
||||
import com.tom_roush.pdfbox.text.PDFTextStripper;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
|
||||
public class UploadService {
|
||||
public interface ProgressListener {
|
||||
void onProgress(int progress);
|
||||
void onCompleted();
|
||||
}
|
||||
|
||||
public static void simulateUpload(Handler handler, ProgressListener listener) {
|
||||
for (int i = 1; i <= 10; i++) {
|
||||
int progress = i * 10;
|
||||
handler.postDelayed(() -> {
|
||||
if (listener != null) listener.onProgress(progress);
|
||||
if (progress == 100 && listener != null) listener.onCompleted();
|
||||
}, i * 200);
|
||||
}
|
||||
}
|
||||
|
||||
public static String getMimeType(Context ctx, Uri uri) {
|
||||
ContentResolver cr = ctx.getContentResolver();
|
||||
return cr.getType(uri);
|
||||
}
|
||||
|
||||
public static String getDisplayName(Context ctx, Uri uri) {
|
||||
String name = null;
|
||||
try (Cursor cursor = ctx.getContentResolver()
|
||||
.query(uri, new String[]{OpenableColumns.DISPLAY_NAME}, null, null, null)) {
|
||||
if (cursor != null && cursor.moveToFirst()) {
|
||||
name = cursor.getString(0);
|
||||
}
|
||||
} catch (Exception ignored) {}
|
||||
return name;
|
||||
}
|
||||
|
||||
// Removed: OcrCallback interface and any runTextRecognition code.
|
||||
// OCR must be performed via OCRService.recognize(...)
|
||||
|
||||
public static String extractPdfText(Context ctx, Uri uri) throws IOException {
|
||||
InputStream inputStream = ctx.getContentResolver().openInputStream(uri);
|
||||
if (inputStream == null) return "";
|
||||
java.io.File tmp = java.io.File.createTempFile("ocr_pdf_", ".pdf", ctx.getCacheDir());
|
||||
try (java.io.FileOutputStream fos = new java.io.FileOutputStream(tmp)) {
|
||||
byte[] buf = new byte[8192];
|
||||
int len;
|
||||
while ((len = inputStream.read(buf)) != -1) fos.write(buf, 0, len);
|
||||
} finally { inputStream.close(); }
|
||||
|
||||
String full = com.example.myapplication.service.OCRService.extractTextFromFile(ctx, tmp.getAbsolutePath());
|
||||
try { tmp.delete(); } catch (Exception ignored) {}
|
||||
return full == null ? "" : full;
|
||||
}
|
||||
|
||||
public static String extractPlainText(Context ctx, Uri uri) throws IOException {
|
||||
InputStream inputStream = ctx.getContentResolver().openInputStream(uri);
|
||||
ByteArrayOutputStream result = new ByteArrayOutputStream();
|
||||
byte[] buffer = new byte[1024];
|
||||
int length;
|
||||
while ((length = inputStream.read(buffer)) != -1) {
|
||||
result.write(buffer, 0, length);
|
||||
}
|
||||
inputStream.close();
|
||||
return result.toString("UTF-8");
|
||||
}
|
||||
|
||||
public static String guessSubject(String lower, String[] dictMath, String[] dictPhysics, String[] dictChem, String[] dictEnglish, String[] dictCS, String[] dictRobotics) {
|
||||
for (String k : dictRobotics) { if (lower.contains(k.toLowerCase())) return "机器人学导论"; }
|
||||
for (String k : dictCS) { if (lower.contains(k.toLowerCase())) return "计算机基础"; }
|
||||
for (String k : dictMath) { if (lower.contains(k.toLowerCase())) return "数学"; }
|
||||
for (String k : dictPhysics) { if (lower.contains(k.toLowerCase())) return "物理"; }
|
||||
for (String k : dictChem) { if (lower.contains(k.toLowerCase())) return "化学"; }
|
||||
for (String k : dictEnglish) { if (lower.contains(k.toLowerCase())) return "英语"; }
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,16 @@
|
||||
package com.example.myapplication.service;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.SharedPreferences;
|
||||
|
||||
public class UserService {
|
||||
public static String getUser(Context ctx) {
|
||||
SharedPreferences prefs = ctx.getSharedPreferences("app_prefs", Context.MODE_PRIVATE);
|
||||
String account = prefs.getString("currentUser", null);
|
||||
if (account == null) return "张三";
|
||||
|
||||
com.example.myapplication.repository.UserRepository repo = new com.example.myapplication.repository.UserRepository(ctx);
|
||||
com.example.myapplication.domain.User u = repo.getUserByAccount(account);
|
||||
return u != null ? u.getName() : account;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,44 @@
|
||||
package com.example.myapplication.ui;
|
||||
import com.example.myapplication.R;
|
||||
import com.example.myapplication.Utils;
|
||||
import com.example.myapplication.service.AuthService;
|
||||
import android.content.Intent;
|
||||
import android.content.SharedPreferences;
|
||||
import android.os.Bundle;
|
||||
import android.widget.Button;
|
||||
import android.widget.EditText;
|
||||
import android.widget.TextView;
|
||||
import androidx.appcompat.app.AppCompatActivity;
|
||||
|
||||
public class LoginUI extends AppCompatActivity {
|
||||
|
||||
private SharedPreferences prefs;
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
setContentView(R.layout.activity_login);
|
||||
|
||||
prefs = getSharedPreferences("app_prefs", MODE_PRIVATE);
|
||||
|
||||
EditText usernameEt = findViewById(R.id.usernameInput);
|
||||
EditText passwordEt = findViewById(R.id.passwordInput);
|
||||
Button loginBtn = findViewById(R.id.loginBtn);
|
||||
TextView registerLink = findViewById(R.id.registerLink);
|
||||
|
||||
loginBtn.setOnClickListener(v -> {
|
||||
String username = usernameEt.getText().toString().trim();
|
||||
String password = passwordEt.getText().toString().trim();
|
||||
if (!AuthService.login(this, username, password)) {
|
||||
Utils.toast(this, "请输入用户名和密码");
|
||||
return;
|
||||
}
|
||||
Utils.toast(this, "登录成功!即将跳转到主界面");
|
||||
startActivity(new Intent(this, MainUI.class));
|
||||
finish();
|
||||
});
|
||||
|
||||
registerLink.setOnClickListener(v ->
|
||||
startActivity(new Intent(this, RegisterUI.class)));
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,78 @@
|
||||
package com.example.myapplication.ui;
|
||||
|
||||
import androidx.appcompat.app.AppCompatActivity;
|
||||
|
||||
public class NoteEditUI extends AppCompatActivity {
|
||||
private long noteId;
|
||||
|
||||
@Override
|
||||
protected void onCreate(android.os.Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
setContentView(com.example.myapplication.R.layout.activity_note_edit);
|
||||
|
||||
noteId = getIntent().getLongExtra("noteId", -1);
|
||||
if (noteId <= 0) {
|
||||
int nid = com.example.myapplication.service.NoteService.getCurrentNoteId(this);
|
||||
noteId = nid;
|
||||
}
|
||||
android.widget.EditText titleEt = findViewById(com.example.myapplication.R.id.editNoteTitle);
|
||||
android.widget.EditText contentEt = findViewById(com.example.myapplication.R.id.editNoteContent);
|
||||
android.widget.Button saveBtn = findViewById(com.example.myapplication.R.id.saveNoteBtn);
|
||||
android.widget.Button cancelBtn = findViewById(com.example.myapplication.R.id.cancelEditBtn);
|
||||
android.widget.Button deleteBtn = findViewById(com.example.myapplication.R.id.deleteNoteBtn);
|
||||
|
||||
if (noteId > 0) {
|
||||
com.example.myapplication.repository.NoteRepository repo = new com.example.myapplication.repository.NoteRepository(this);
|
||||
com.example.myapplication.domain.Note note = repo.getNoteById(noteId);
|
||||
if (note != null) {
|
||||
if (titleEt != null) titleEt.setText(note.getTitle() != null ? note.getTitle() : "");
|
||||
if (contentEt != null) contentEt.setText(note.getContent() != null ? note.getContent() : "");
|
||||
}
|
||||
}
|
||||
|
||||
saveBtn.setOnClickListener(v -> {
|
||||
String title = titleEt.getText().toString().trim();
|
||||
String content = contentEt.getText().toString().trim();
|
||||
if (title.isEmpty()) {
|
||||
com.example.myapplication.Utils.toast(this, "标题不能为空");
|
||||
return;
|
||||
}
|
||||
// 仅允许上传者编辑
|
||||
com.example.myapplication.repository.NoteRepository repo0 = new com.example.myapplication.repository.NoteRepository(this);
|
||||
com.example.myapplication.domain.Note n0 = repo0.getNoteById(noteId);
|
||||
String currentUser = com.example.myapplication.service.AuthService.getCurrentUser(this);
|
||||
if (n0 != null && n0.getUploaderId() != null && !n0.getUploaderId().equals(currentUser)) {
|
||||
com.example.myapplication.Utils.toast(this, "仅作者可编辑");
|
||||
return;
|
||||
}
|
||||
android.content.ContentValues cv = new android.content.ContentValues();
|
||||
cv.put("title", title);
|
||||
cv.put("content", content);
|
||||
boolean ok = new com.example.myapplication.repository.NoteRepository(this).updateNote(noteId, cv);
|
||||
com.example.myapplication.Utils.toast(this, ok ? "保存成功" : "保存失败");
|
||||
if (ok) finish();
|
||||
});
|
||||
|
||||
cancelBtn.setOnClickListener(v -> finish());
|
||||
|
||||
deleteBtn.setOnClickListener(v -> {
|
||||
android.app.AlertDialog.Builder b = new android.app.AlertDialog.Builder(this);
|
||||
b.setTitle("删除笔记");
|
||||
b.setMessage("确定删除这条笔记吗?");
|
||||
b.setPositiveButton("删除", (d, which) -> {
|
||||
com.example.myapplication.repository.NoteRepository repo0 = new com.example.myapplication.repository.NoteRepository(this);
|
||||
com.example.myapplication.domain.Note n0 = repo0.getNoteById(noteId);
|
||||
String currentUser = com.example.myapplication.service.AuthService.getCurrentUser(this);
|
||||
if (n0 != null && n0.getUploaderId() != null && !n0.getUploaderId().equals(currentUser)) {
|
||||
com.example.myapplication.Utils.toast(this, "仅作者可删除");
|
||||
return;
|
||||
}
|
||||
boolean ok = new com.example.myapplication.repository.NoteRepository(this).deleteNote(noteId);
|
||||
com.example.myapplication.Utils.toast(this, ok ? "已删除" : "删除失败");
|
||||
if (ok) finish();
|
||||
});
|
||||
b.setNegativeButton("取消", (d, which) -> {});
|
||||
b.show();
|
||||
});
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,78 @@
|
||||
package com.example.myapplication.ui;
|
||||
import com.example.myapplication.R;
|
||||
import com.example.myapplication.Utils;
|
||||
import com.example.myapplication.service.AuthService;
|
||||
import android.content.Intent;
|
||||
import android.os.Bundle;
|
||||
import android.widget.Button;
|
||||
import android.widget.EditText;
|
||||
import androidx.appcompat.app.AppCompatActivity;
|
||||
|
||||
public class RegisterUI extends AppCompatActivity {
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
setContentView(R.layout.activity_register);
|
||||
|
||||
EditText usernameEt = findViewById(R.id.usernameInput);
|
||||
EditText passwordEt = findViewById(R.id.passwordInput);
|
||||
EditText confirmEt = findViewById(R.id.confirmInput);
|
||||
android.widget.Spinner gradeSp = findViewById(R.id.gradeSpinner);
|
||||
android.widget.AutoCompleteTextView majorEt = findViewById(R.id.majorInput);
|
||||
final long[] selectedMajorId = new long[]{-1};
|
||||
majorEt.setThreshold(1);
|
||||
majorEt.addTextChangedListener(new android.text.TextWatcher() {
|
||||
@Override public void beforeTextChanged(CharSequence s, int start, int count, int after) {}
|
||||
@Override public void onTextChanged(CharSequence s, int start, int before, int count) {
|
||||
selectedMajorId[0] = -1;
|
||||
String q = s == null ? "" : s.toString();
|
||||
com.example.myapplication.repository.MajorRepository mr = new com.example.myapplication.repository.MajorRepository(RegisterUI.this);
|
||||
java.util.List<com.example.myapplication.domain.Major> majors = mr.findByNameLike(q, 10);
|
||||
java.util.List<String> names = new java.util.ArrayList<>();
|
||||
for (com.example.myapplication.domain.Major m : majors) names.add(m.getName());
|
||||
android.widget.ArrayAdapter<String> ad = new android.widget.ArrayAdapter<>(RegisterUI.this, android.R.layout.simple_dropdown_item_1line, names);
|
||||
majorEt.setAdapter(ad);
|
||||
majorEt.showDropDown();
|
||||
}
|
||||
@Override public void afterTextChanged(android.text.Editable s) {}
|
||||
});
|
||||
majorEt.setOnItemClickListener((parent, view, position, id) -> {
|
||||
String name = (String) parent.getItemAtPosition(position);
|
||||
com.example.myapplication.repository.MajorRepository mr = new com.example.myapplication.repository.MajorRepository(RegisterUI.this);
|
||||
java.util.List<com.example.myapplication.domain.Major> majors = mr.findByNameLike(name, 1);
|
||||
if (majors != null && !majors.isEmpty()) selectedMajorId[0] = majors.get(0).getId();
|
||||
});
|
||||
Button registerBtn = findViewById(R.id.registerBtn);
|
||||
|
||||
java.util.List<String> grades = java.util.Arrays.asList("大一", "大二", "大三", "大四");
|
||||
android.widget.ArrayAdapter<String> adapter = new android.widget.ArrayAdapter<>(
|
||||
this, android.R.layout.simple_spinner_item, grades);
|
||||
adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
|
||||
gradeSp.setAdapter(adapter);
|
||||
|
||||
registerBtn.setOnClickListener(v -> {
|
||||
String username = usernameEt.getText().toString().trim();
|
||||
String password = passwordEt.getText().toString().trim();
|
||||
String confirm = confirmEt.getText().toString().trim();
|
||||
|
||||
String gradeName = null;
|
||||
try { gradeName = (String) gradeSp.getSelectedItem(); } catch (Exception ignored) {}
|
||||
Long majorId = selectedMajorId[0] >= 0 ? selectedMajorId[0] : null;
|
||||
String err = AuthService.validateRegistration(username, password, confirm, gradeName, majorId);
|
||||
if (err != null) {
|
||||
Utils.toast(this, err);
|
||||
return;
|
||||
}
|
||||
|
||||
boolean ok = AuthService.register(this, username, password, gradeName, majorId);
|
||||
if (!ok) {
|
||||
Utils.toast(this, "注册失败,请稍后再试");
|
||||
return;
|
||||
}
|
||||
|
||||
Utils.toast(this, "注册成功!请登录");
|
||||
startActivity(new Intent(this, LoginUI.class));
|
||||
finish();
|
||||
});
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,72 @@
|
||||
package com.example.myapplication.ui;
|
||||
import com.example.myapplication.R;
|
||||
import android.os.Bundle;
|
||||
import android.widget.Button;
|
||||
import android.widget.EditText;
|
||||
import android.widget.TextView;
|
||||
import androidx.appcompat.app.AppCompatActivity;
|
||||
|
||||
public class SearchUI extends AppCompatActivity {
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
setContentView(R.layout.activity_search);
|
||||
|
||||
Button backBtn = findViewById(R.id.backBtn);
|
||||
Button searchBtn = findViewById(R.id.searchBtn);
|
||||
EditText searchInput = findViewById(R.id.searchInput);
|
||||
TextView resultsInfo = findViewById(R.id.resultsInfo);
|
||||
android.widget.LinearLayout resultsContainer = findViewById(R.id.resultsContainer);
|
||||
|
||||
backBtn.setOnClickListener(v -> finish());
|
||||
searchBtn.setOnClickListener(v -> {
|
||||
String q = searchInput.getText() == null ? "" : searchInput.getText().toString().trim();
|
||||
java.util.List<com.example.myapplication.domain.Note> notes = com.example.myapplication.service.SearchService.search(this, q);
|
||||
resultsContainer.removeAllViews();
|
||||
if (notes == null || notes.isEmpty()) {
|
||||
resultsInfo.setText("未找到匹配的笔记");
|
||||
android.widget.TextView empty = new android.widget.TextView(this);
|
||||
empty.setText("试试输入章节、科目或专业名称进行搜索");
|
||||
empty.setTextColor(android.graphics.Color.parseColor("#666666"));
|
||||
resultsContainer.addView(empty);
|
||||
} else {
|
||||
resultsInfo.setText("共 " + notes.size() + " 条结果");
|
||||
int pad = (int) (12 * getResources().getDisplayMetrics().density);
|
||||
for (com.example.myapplication.domain.Note n : notes) {
|
||||
android.widget.LinearLayout item = new android.widget.LinearLayout(this);
|
||||
item.setOrientation(android.widget.LinearLayout.VERTICAL);
|
||||
item.setBackgroundColor(android.graphics.Color.WHITE);
|
||||
item.setPadding(pad, pad, pad, pad);
|
||||
|
||||
android.widget.TextView title = new android.widget.TextView(this);
|
||||
title.setText(n.getTitle() != null ? n.getTitle() : "(无标题)");
|
||||
title.setTextColor(android.graphics.Color.parseColor("#333333"));
|
||||
title.setTypeface(android.graphics.Typeface.DEFAULT_BOLD);
|
||||
|
||||
android.widget.TextView meta = new android.widget.TextView(this);
|
||||
String created = n.getCreated() == null ? "" : n.getCreated();
|
||||
int likes = n.getLikeCount();
|
||||
meta.setText((created.isEmpty() ? "📅 未知" : "📅 " + created) + " 👍 " + likes);
|
||||
meta.setTextColor(android.graphics.Color.parseColor("#666666"));
|
||||
|
||||
android.widget.TextView desc = new android.widget.TextView(this);
|
||||
String content = n.getContent();
|
||||
if (content == null) content = "";
|
||||
desc.setText(content.length() > 120 ? content.substring(0, 120) + "..." : content);
|
||||
desc.setTextColor(android.graphics.Color.parseColor("#888888"));
|
||||
|
||||
item.addView(title);
|
||||
item.addView(meta);
|
||||
item.addView(desc);
|
||||
item.setOnClickListener(v2 -> {
|
||||
com.example.myapplication.service.NoteService.setCurrentNoteId(this, (int) n.getId());
|
||||
android.content.Intent intent = new android.content.Intent(this, com.example.myapplication.ui.NoteDetailUI.class);
|
||||
intent.putExtra("noteId", (int) n.getId());
|
||||
startActivity(intent);
|
||||
});
|
||||
resultsContainer.addView(item);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,225 @@
|
||||
package com.example.myapplication.ui;
|
||||
import com.example.myapplication.R;
|
||||
import com.example.myapplication.Utils;
|
||||
import com.example.myapplication.service.UserService;
|
||||
import android.os.Bundle;
|
||||
import android.widget.Button;
|
||||
import android.widget.EditText;
|
||||
import android.widget.TextView;
|
||||
import android.text.TextUtils;
|
||||
import androidx.appcompat.app.AppCompatActivity;
|
||||
|
||||
public class UserCenterUI extends AppCompatActivity {
|
||||
private TextView statUploaded, statLikes, statPoints, majorNameTv, gradeNameTv;
|
||||
private EditText usernameEt;
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
setContentView(R.layout.activity_user_center);
|
||||
|
||||
findViewById(R.id.backBtn).setOnClickListener(v -> finish());
|
||||
|
||||
statUploaded = findViewById(R.id.statUploaded);
|
||||
statLikes = findViewById(R.id.statLikes);
|
||||
statPoints = findViewById(R.id.statPoints);
|
||||
majorNameTv = findViewById(R.id.majorNameTv);
|
||||
gradeNameTv = findViewById(R.id.gradeNameTv);
|
||||
|
||||
usernameEt = findViewById(R.id.usernameInput);
|
||||
|
||||
|
||||
// 可选:去掉写死示例,统一由 refreshStats() 赋值
|
||||
statUploaded.setText("15");
|
||||
statLikes.setText("128");
|
||||
|
||||
usernameEt.setText(com.example.myapplication.service.UserService.getUser(this));
|
||||
|
||||
|
||||
|
||||
// 刷新数据库统计
|
||||
refreshStats();
|
||||
// 动态渲染我的笔记
|
||||
renderMyNotes();
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onResume() {
|
||||
super.onResume();
|
||||
refreshStats();
|
||||
renderMyNotes();
|
||||
}
|
||||
|
||||
private void refreshStats() {
|
||||
String account = com.example.myapplication.service.AuthService.getCurrentUser(this);
|
||||
com.example.myapplication.repository.NoteRepository noteRepo =
|
||||
new com.example.myapplication.repository.NoteRepository(this);
|
||||
java.util.List<com.example.myapplication.domain.Note> myNotes =
|
||||
noteRepo.getNotesByUser(account);
|
||||
int uploadedCount = myNotes != null ? myNotes.size() : 0;
|
||||
|
||||
int totalLikes = new com.example.myapplication.repository.NoteRepository(this)
|
||||
.getTotalLikesByUser(account);
|
||||
|
||||
com.example.myapplication.repository.UserRepository userRepo =
|
||||
new com.example.myapplication.repository.UserRepository(this);
|
||||
int points = userRepo.getPoints(account);
|
||||
|
||||
com.example.myapplication.domain.User u = userRepo.getUserByAccount(account);
|
||||
String majorName = "未设置";
|
||||
String gradeText = null;
|
||||
if (u != null && u.getMajorId() != null) {
|
||||
com.example.myapplication.repository.MajorRepository mr = new com.example.myapplication.repository.MajorRepository(this);
|
||||
com.example.myapplication.domain.Major m = mr.getById(u.getMajorId());
|
||||
if (m != null && m.getName() != null) majorName = m.getName();
|
||||
}
|
||||
|
||||
statUploaded.setText(String.valueOf(uploadedCount));
|
||||
statLikes.setText(String.valueOf(totalLikes));
|
||||
statPoints.setText(String.valueOf(points));
|
||||
if (majorNameTv != null) majorNameTv.setText("专业:" + majorName);
|
||||
gradeText = new com.example.myapplication.repository.UserRepository(this).getGradeText(account);
|
||||
if (gradeNameTv != null) gradeNameTv.setText("年级:" + (gradeText == null ? "未设置" : gradeText));
|
||||
}
|
||||
|
||||
// 动态渲染“我的笔记”
|
||||
private void renderMyNotes() {
|
||||
android.widget.LinearLayout container = findViewById(R.id.myNotesContainer);
|
||||
if (container == null) return;
|
||||
|
||||
container.removeAllViews();
|
||||
|
||||
String account = com.example.myapplication.service.AuthService.getCurrentUser(this);
|
||||
com.example.myapplication.repository.NoteRepository repo =
|
||||
new com.example.myapplication.repository.NoteRepository(this);
|
||||
java.util.List<com.example.myapplication.domain.Note> notes =
|
||||
repo.getNotesByUser(account);
|
||||
|
||||
if (notes == null || notes.isEmpty()) {
|
||||
android.widget.TextView empty = new android.widget.TextView(this);
|
||||
empty.setText("暂无笔记,点击“上传笔记”添加吧~");
|
||||
empty.setTextColor(android.graphics.Color.parseColor("#666666"));
|
||||
container.addView(empty);
|
||||
return;
|
||||
}
|
||||
|
||||
float density = getResources().getDisplayMetrics().density;
|
||||
int pad = (int) (12 * density);
|
||||
int mt = (int) (12 * density);
|
||||
|
||||
com.example.myapplication.repository.LikeRepository likeRepo =
|
||||
new com.example.myapplication.repository.LikeRepository(this);
|
||||
|
||||
for (com.example.myapplication.domain.Note n : notes) {
|
||||
android.widget.LinearLayout item = new android.widget.LinearLayout(this);
|
||||
item.setOrientation(android.widget.LinearLayout.VERTICAL);
|
||||
item.setBackgroundResource(R.drawable.card_background);
|
||||
item.setPadding(pad, pad, pad, pad);
|
||||
|
||||
android.widget.LinearLayout.LayoutParams lp =
|
||||
new android.widget.LinearLayout.LayoutParams(
|
||||
android.widget.LinearLayout.LayoutParams.MATCH_PARENT,
|
||||
android.widget.LinearLayout.LayoutParams.WRAP_CONTENT);
|
||||
lp.setMargins(0, mt, 0, 0);
|
||||
item.setLayoutParams(lp);
|
||||
item.setElevation(4f);
|
||||
|
||||
android.widget.TextView titleTv = new android.widget.TextView(this);
|
||||
titleTv.setText(n.getTitle() != null ? n.getTitle() : "(无标题)");
|
||||
titleTv.setTextColor(getResources().getColor(R.color.text_primary));
|
||||
titleTv.setTypeface(android.graphics.Typeface.DEFAULT_BOLD);
|
||||
titleTv.setTextSize(16);
|
||||
|
||||
int likes = n.getLikeCount();
|
||||
String created = n.getCreated() != null ? n.getCreated() : "";
|
||||
android.widget.TextView metaTv = new android.widget.TextView(this);
|
||||
metaTv.setText((TextUtils.isEmpty(created) ? "📅 未知" : "📅 " + created) + " 👍 " + likes);
|
||||
metaTv.setTextColor(getResources().getColor(R.color.text_secondary));
|
||||
metaTv.setTextSize(14);
|
||||
|
||||
item.addView(titleTv);
|
||||
item.addView(metaTv);
|
||||
|
||||
android.widget.LinearLayout actions = new android.widget.LinearLayout(this);
|
||||
actions.setOrientation(android.widget.LinearLayout.HORIZONTAL);
|
||||
android.widget.LinearLayout.LayoutParams alp = new android.widget.LinearLayout.LayoutParams(
|
||||
android.widget.LinearLayout.LayoutParams.WRAP_CONTENT,
|
||||
android.widget.LinearLayout.LayoutParams.WRAP_CONTENT
|
||||
);
|
||||
alp.topMargin = (int) (12 * density);
|
||||
actions.setLayoutParams(alp);
|
||||
|
||||
android.widget.Button viewBtn = new android.widget.Button(this);
|
||||
viewBtn.setText("查看");
|
||||
viewBtn.setAllCaps(false);
|
||||
viewBtn.setPadding(pad, pad / 2, pad, pad / 2);
|
||||
viewBtn.setTextColor(android.graphics.Color.WHITE);
|
||||
androidx.core.view.ViewCompat.setBackgroundTintList(viewBtn,
|
||||
android.content.res.ColorStateList.valueOf(getResources().getColor(R.color.primary)));
|
||||
|
||||
android.widget.Button editBtn = new android.widget.Button(this);
|
||||
editBtn.setText("编辑");
|
||||
editBtn.setAllCaps(false);
|
||||
editBtn.setPadding(pad, pad / 2, pad, pad / 2);
|
||||
editBtn.setTextColor(android.graphics.Color.WHITE);
|
||||
androidx.core.view.ViewCompat.setBackgroundTintList(editBtn,
|
||||
android.content.res.ColorStateList.valueOf(getResources().getColor(R.color.success)));
|
||||
|
||||
android.widget.Button delBtn = new android.widget.Button(this);
|
||||
delBtn.setText("删除");
|
||||
delBtn.setAllCaps(false);
|
||||
delBtn.setPadding(pad, pad / 2, pad, pad / 2);
|
||||
delBtn.setTextColor(android.graphics.Color.WHITE);
|
||||
androidx.core.view.ViewCompat.setBackgroundTintList(delBtn,
|
||||
android.content.res.ColorStateList.valueOf(getResources().getColor(R.color.danger)));
|
||||
|
||||
android.widget.LinearLayout.LayoutParams btnLp = new android.widget.LinearLayout.LayoutParams(
|
||||
android.widget.LinearLayout.LayoutParams.WRAP_CONTENT,
|
||||
android.widget.LinearLayout.LayoutParams.WRAP_CONTENT
|
||||
);
|
||||
actions.addView(viewBtn, btnLp);
|
||||
android.widget.Space sp1 = new android.widget.Space(this);
|
||||
actions.addView(sp1, new android.widget.LinearLayout.LayoutParams((int)(8 * density), 1));
|
||||
actions.addView(editBtn, btnLp);
|
||||
android.widget.Space sp2 = new android.widget.Space(this);
|
||||
actions.addView(sp2, new android.widget.LinearLayout.LayoutParams((int)(8 * density), 1));
|
||||
actions.addView(delBtn, btnLp);
|
||||
|
||||
viewBtn.setOnClickListener(v -> {
|
||||
com.example.myapplication.service.NoteService.setCurrentNoteId(this, (int) n.getId());
|
||||
android.content.Intent intent = new android.content.Intent(this, com.example.myapplication.ui.NoteDetailUI.class);
|
||||
intent.putExtra("noteId", (int) n.getId());
|
||||
startActivity(intent);
|
||||
});
|
||||
|
||||
editBtn.setOnClickListener(v -> {
|
||||
com.example.myapplication.service.NoteService.setCurrentNoteId(this, (int) n.getId());
|
||||
android.content.Intent intent = new android.content.Intent(this, com.example.myapplication.ui.NoteEditUI.class);
|
||||
startActivity(intent);
|
||||
});
|
||||
|
||||
delBtn.setOnClickListener(v -> {
|
||||
android.app.AlertDialog.Builder b = new android.app.AlertDialog.Builder(this);
|
||||
b.setTitle("删除笔记");
|
||||
b.setMessage("确定要删除这条笔记吗?");
|
||||
b.setPositiveButton("删除", (d, which) -> {
|
||||
boolean okDel = new com.example.myapplication.repository.NoteRepository(this).deleteNote(n.getId());
|
||||
if (okDel) {
|
||||
com.example.myapplication.Utils.toast(this, "已删除");
|
||||
refreshStats();
|
||||
renderMyNotes();
|
||||
} else {
|
||||
com.example.myapplication.Utils.toast(this, "删除失败");
|
||||
}
|
||||
});
|
||||
b.setNegativeButton("取消", (d, which) -> {});
|
||||
b.show();
|
||||
});
|
||||
|
||||
item.addView(actions);
|
||||
|
||||
container.addView(item);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,95 @@
|
||||
package net.micode.myapplication.database;
|
||||
|
||||
import android.content.Context;
|
||||
import android.database.sqlite.SQLiteDatabase;
|
||||
import android.database.sqlite.SQLiteOpenHelper;
|
||||
|
||||
public class DatabaseHelper extends SQLiteOpenHelper {
|
||||
private static final String DATABASE_NAME = "MyNote.db";
|
||||
private static final int DATABASE_VERSION = 1;
|
||||
|
||||
// 表名
|
||||
public static final String TABLE_COURSES = "courses";
|
||||
public static final String TABLE_CHAPTERS = "chapters";
|
||||
public static final String TABLE_NOTES = "notes";
|
||||
|
||||
// Courses表列名
|
||||
public static final String COLUMN_COURSE_ID = "course_id";
|
||||
public static final String COLUMN_COURSE_NAME = "course_name";
|
||||
public static final String COLUMN_SCHOOL = "school";
|
||||
|
||||
// Chapters表列名
|
||||
public static final String COLUMN_CHAPTER_ID = "chapter_id";
|
||||
public static final String COLUMN_CHAPTER_NUMBER = "chapter_number";
|
||||
public static final String COLUMN_CHAPTER_TITLE = "chapter_title";
|
||||
public static final String COLUMN_KEYWORDS = "keywords";
|
||||
public static final String COLUMN_ALIASES = "aliases";
|
||||
|
||||
// Notes表列名
|
||||
public static final String COLUMN_NOTE_ID = "note_id";
|
||||
public static final String COLUMN_TITLE = "title";
|
||||
public static final String COLUMN_DESCRIPTION = "description";
|
||||
public static final String COLUMN_OCR_TEXT = "ocr_text";
|
||||
public static final String COLUMN_FILE_PATH = "file_path";
|
||||
public static final String COLUMN_UPLOAD_TIME = "upload_time";
|
||||
public static final String COLUMN_UPDATE_TIME = "update_time";
|
||||
|
||||
// 创建Courses表的SQL语句
|
||||
private static final String CREATE_TABLE_COURSES = "CREATE TABLE " + TABLE_COURSES + " (" +
|
||||
COLUMN_COURSE_ID + " TEXT PRIMARY KEY, " +
|
||||
COLUMN_COURSE_NAME + " TEXT NOT NULL, " +
|
||||
COLUMN_SCHOOL + " TEXT" +
|
||||
");";
|
||||
|
||||
// 创建Chapters表的SQL语句
|
||||
private static final String CREATE_TABLE_CHAPTERS = "CREATE TABLE " + TABLE_CHAPTERS + " (" +
|
||||
COLUMN_CHAPTER_ID + " TEXT PRIMARY KEY, " +
|
||||
COLUMN_COURSE_ID + " TEXT NOT NULL, " +
|
||||
COLUMN_CHAPTER_NUMBER + " INTEGER NOT NULL, " +
|
||||
COLUMN_CHAPTER_TITLE + " TEXT NOT NULL, " +
|
||||
COLUMN_KEYWORDS + " TEXT, " +
|
||||
COLUMN_ALIASES + " TEXT, " +
|
||||
"FOREIGN KEY(" + COLUMN_COURSE_ID + ") REFERENCES " + TABLE_COURSES + "(" + COLUMN_COURSE_ID + ")" +
|
||||
");";
|
||||
|
||||
// 创建Notes表的SQL语句
|
||||
private static final String CREATE_TABLE_NOTES = "CREATE TABLE " + TABLE_NOTES + " (" +
|
||||
COLUMN_NOTE_ID + " TEXT PRIMARY KEY, " +
|
||||
COLUMN_TITLE + " TEXT NOT NULL, " +
|
||||
COLUMN_DESCRIPTION + " TEXT, " +
|
||||
COLUMN_OCR_TEXT + " TEXT, " +
|
||||
COLUMN_COURSE_ID + " TEXT, " +
|
||||
COLUMN_CHAPTER_ID + " TEXT, " +
|
||||
COLUMN_FILE_PATH + " TEXT, " +
|
||||
COLUMN_UPLOAD_TIME + " INTEGER, " +
|
||||
COLUMN_UPDATE_TIME + " INTEGER, " +
|
||||
"FOREIGN KEY(" + COLUMN_COURSE_ID + ") REFERENCES " + TABLE_COURSES + "(" + COLUMN_COURSE_ID + "), " +
|
||||
"FOREIGN KEY(" + COLUMN_CHAPTER_ID + ") REFERENCES " + TABLE_CHAPTERS + "(" + COLUMN_CHAPTER_ID + ")" +
|
||||
");";
|
||||
|
||||
public DatabaseHelper(Context context) {
|
||||
super(context, DATABASE_NAME, null, DATABASE_VERSION);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreate(SQLiteDatabase db) {
|
||||
db.execSQL(CREATE_TABLE_COURSES);
|
||||
db.execSQL(CREATE_TABLE_CHAPTERS);
|
||||
db.execSQL(CREATE_TABLE_NOTES);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
|
||||
db.execSQL("DROP TABLE IF EXISTS " + TABLE_NOTES);
|
||||
db.execSQL("DROP TABLE IF EXISTS " + TABLE_CHAPTERS);
|
||||
db.execSQL("DROP TABLE IF EXISTS " + TABLE_COURSES);
|
||||
onCreate(db);
|
||||
}
|
||||
|
||||
// 清除所有数据的方法
|
||||
public void clearAllData(SQLiteDatabase db) {
|
||||
db.execSQL("DELETE FROM " + TABLE_NOTES);
|
||||
db.execSQL("DELETE FROM " + TABLE_CHAPTERS);
|
||||
db.execSQL("DELETE FROM " + TABLE_COURSES);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,101 @@
|
||||
package net.micode.myapplication.model;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class Chapter {
|
||||
private String chapterId;
|
||||
private String courseId;
|
||||
private int chapterNumber;
|
||||
private String chapterTitle;
|
||||
private List<String> keywords;
|
||||
private List<String> aliases;
|
||||
|
||||
public Chapter() {
|
||||
this.keywords = new ArrayList<>();
|
||||
this.aliases = new ArrayList<>();
|
||||
}
|
||||
|
||||
public Chapter(String chapterId, String courseId, int chapterNumber, String chapterTitle) {
|
||||
this.chapterId = chapterId;
|
||||
this.courseId = courseId;
|
||||
this.chapterNumber = chapterNumber;
|
||||
this.chapterTitle = chapterTitle;
|
||||
this.keywords = new ArrayList<>();
|
||||
this.aliases = new ArrayList<>();
|
||||
}
|
||||
|
||||
public String getChapterId() {
|
||||
return chapterId;
|
||||
}
|
||||
|
||||
public void setChapterId(String chapterId) {
|
||||
this.chapterId = chapterId;
|
||||
}
|
||||
|
||||
public String getCourseId() {
|
||||
return courseId;
|
||||
}
|
||||
|
||||
public void setCourseId(String courseId) {
|
||||
this.courseId = courseId;
|
||||
}
|
||||
|
||||
public int getChapterNumber() {
|
||||
return chapterNumber;
|
||||
}
|
||||
|
||||
public void setChapterNumber(int chapterNumber) {
|
||||
this.chapterNumber = chapterNumber;
|
||||
}
|
||||
|
||||
public String getChapterTitle() {
|
||||
return chapterTitle;
|
||||
}
|
||||
|
||||
public void setChapterTitle(String chapterTitle) {
|
||||
this.chapterTitle = chapterTitle;
|
||||
}
|
||||
|
||||
public List<String> getKeywords() {
|
||||
return keywords;
|
||||
}
|
||||
|
||||
public void setKeywords(List<String> keywords) {
|
||||
this.keywords = keywords;
|
||||
}
|
||||
|
||||
public List<String> getAliases() {
|
||||
return aliases;
|
||||
}
|
||||
|
||||
public void setAliases(List<String> aliases) {
|
||||
this.aliases = aliases;
|
||||
}
|
||||
|
||||
public void addKeyword(String keyword) {
|
||||
if (this.keywords == null) {
|
||||
this.keywords = new ArrayList<>();
|
||||
}
|
||||
this.keywords.add(keyword);
|
||||
}
|
||||
|
||||
public void addAlias(String alias) {
|
||||
if (this.aliases == null) {
|
||||
this.aliases = new ArrayList<>();
|
||||
}
|
||||
this.aliases.add(alias);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Chapter{" +
|
||||
"chapterId='" + chapterId + '\'' +
|
||||
", courseId='" + courseId + '\'' +
|
||||
", chapterNumber=" + chapterNumber +
|
||||
", chapterTitle='" + chapterTitle + '\'' +
|
||||
", keywords=" + keywords +
|
||||
", aliases=" + aliases +
|
||||
'}';
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,60 @@
|
||||
package net.micode.myapplication.model;
|
||||
|
||||
public class ClassificationResult {
|
||||
private Course course;
|
||||
private Chapter chapter;
|
||||
private double confidenceScore;
|
||||
private String reason;
|
||||
|
||||
public ClassificationResult() {
|
||||
}
|
||||
|
||||
public ClassificationResult(Course course, Chapter chapter, double confidenceScore, String reason) {
|
||||
this.course = course;
|
||||
this.chapter = chapter;
|
||||
this.confidenceScore = confidenceScore;
|
||||
this.reason = reason;
|
||||
}
|
||||
|
||||
public Course getCourse() {
|
||||
return course;
|
||||
}
|
||||
|
||||
public void setCourse(Course course) {
|
||||
this.course = course;
|
||||
}
|
||||
|
||||
public Chapter getChapter() {
|
||||
return chapter;
|
||||
}
|
||||
|
||||
public void setChapter(Chapter chapter) {
|
||||
this.chapter = chapter;
|
||||
}
|
||||
|
||||
public double getConfidenceScore() {
|
||||
return confidenceScore;
|
||||
}
|
||||
|
||||
public void setConfidenceScore(double confidenceScore) {
|
||||
this.confidenceScore = confidenceScore;
|
||||
}
|
||||
|
||||
public String getReason() {
|
||||
return reason;
|
||||
}
|
||||
|
||||
public void setReason(String reason) {
|
||||
this.reason = reason;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "ClassificationResult{" +
|
||||
"course=" + (course != null ? course.getCourseName() : "null") +
|
||||
", chapter=" + (chapter != null ? chapter.getChapterTitle() : "null") +
|
||||
", confidenceScore=" + confidenceScore +
|
||||
", reason='" + reason + '\'' +
|
||||
'}';
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,49 @@
|
||||
package net.micode.myapplication.model;
|
||||
|
||||
public class Course {
|
||||
private String courseId;
|
||||
private String courseName;
|
||||
private String school;
|
||||
|
||||
public Course() {
|
||||
}
|
||||
|
||||
public Course(String courseId, String courseName, String school) {
|
||||
this.courseId = courseId;
|
||||
this.courseName = courseName;
|
||||
this.school = school;
|
||||
}
|
||||
|
||||
public String getCourseId() {
|
||||
return courseId;
|
||||
}
|
||||
|
||||
public void setCourseId(String courseId) {
|
||||
this.courseId = courseId;
|
||||
}
|
||||
|
||||
public String getCourseName() {
|
||||
return courseName;
|
||||
}
|
||||
|
||||
public void setCourseName(String courseName) {
|
||||
this.courseName = courseName;
|
||||
}
|
||||
|
||||
public String getSchool() {
|
||||
return school;
|
||||
}
|
||||
|
||||
public void setSchool(String school) {
|
||||
this.school = school;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Course{" +
|
||||
"courseId='" + courseId + '\'' +
|
||||
", courseName='" + courseName + '\'' +
|
||||
", school='" + school + '\'' +
|
||||
'}';
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,111 @@
|
||||
package net.micode.myapplication.model;
|
||||
|
||||
public class Note {
|
||||
private String noteId;
|
||||
private String title;
|
||||
private String description;
|
||||
private String ocrText;
|
||||
private String courseId;
|
||||
private String chapterId;
|
||||
private String filePath;
|
||||
private long uploadTime;
|
||||
private long updateTime;
|
||||
|
||||
public Note() {
|
||||
this.uploadTime = System.currentTimeMillis();
|
||||
this.updateTime = this.uploadTime;
|
||||
}
|
||||
|
||||
public Note(String noteId, String title, String description, String filePath) {
|
||||
this.noteId = noteId;
|
||||
this.title = title;
|
||||
this.description = description;
|
||||
this.filePath = filePath;
|
||||
this.uploadTime = System.currentTimeMillis();
|
||||
this.updateTime = this.uploadTime;
|
||||
}
|
||||
|
||||
public String getNoteId() {
|
||||
return noteId;
|
||||
}
|
||||
|
||||
public void setNoteId(String noteId) {
|
||||
this.noteId = noteId;
|
||||
}
|
||||
|
||||
public String getTitle() {
|
||||
return title;
|
||||
}
|
||||
|
||||
public void setTitle(String title) {
|
||||
this.title = title;
|
||||
}
|
||||
|
||||
public String getDescription() {
|
||||
return description;
|
||||
}
|
||||
|
||||
public void setDescription(String description) {
|
||||
this.description = description;
|
||||
}
|
||||
|
||||
public String getOcrText() {
|
||||
return ocrText;
|
||||
}
|
||||
|
||||
public void setOcrText(String ocrText) {
|
||||
this.ocrText = ocrText;
|
||||
}
|
||||
|
||||
public String getCourseId() {
|
||||
return courseId;
|
||||
}
|
||||
|
||||
public void setCourseId(String courseId) {
|
||||
this.courseId = courseId;
|
||||
}
|
||||
|
||||
public String getChapterId() {
|
||||
return chapterId;
|
||||
}
|
||||
|
||||
public void setChapterId(String chapterId) {
|
||||
this.chapterId = chapterId;
|
||||
}
|
||||
|
||||
public String getFilePath() {
|
||||
return filePath;
|
||||
}
|
||||
|
||||
public void setFilePath(String filePath) {
|
||||
this.filePath = filePath;
|
||||
}
|
||||
|
||||
public long getUploadTime() {
|
||||
return uploadTime;
|
||||
}
|
||||
|
||||
public void setUploadTime(long uploadTime) {
|
||||
this.uploadTime = uploadTime;
|
||||
}
|
||||
|
||||
public long getUpdateTime() {
|
||||
return updateTime;
|
||||
}
|
||||
|
||||
public void setUpdateTime(long updateTime) {
|
||||
this.updateTime = updateTime;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Note{" +
|
||||
"noteId='" + noteId + '\'' +
|
||||
", title='" + title + '\'' +
|
||||
", description='" + description + '\'' +
|
||||
", courseId='" + courseId + '\'' +
|
||||
", chapterId='" + chapterId + '\'' +
|
||||
", uploadTime=" + uploadTime +
|
||||
'}';
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,183 @@
|
||||
package net.micode.myapplication.repository;
|
||||
|
||||
import android.content.ContentValues;
|
||||
import android.content.Context;
|
||||
import android.database.Cursor;
|
||||
import android.database.sqlite.SQLiteDatabase;
|
||||
|
||||
import net.micode.myapplication.database.DatabaseHelper;
|
||||
import net.micode.myapplication.model.Note;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class NoteRepository {
|
||||
private DatabaseHelper dbHelper;
|
||||
|
||||
public NoteRepository(Context context) {
|
||||
this.dbHelper = new DatabaseHelper(context);
|
||||
}
|
||||
|
||||
public long insertNote(Note note) {
|
||||
SQLiteDatabase db = dbHelper.getWritableDatabase();
|
||||
ContentValues values = new ContentValues();
|
||||
values.put(DatabaseHelper.COLUMN_NOTE_ID, note.getNoteId());
|
||||
values.put(DatabaseHelper.COLUMN_TITLE, note.getTitle());
|
||||
values.put(DatabaseHelper.COLUMN_DESCRIPTION, note.getDescription());
|
||||
values.put(DatabaseHelper.COLUMN_OCR_TEXT, note.getOcrText());
|
||||
values.put(DatabaseHelper.COLUMN_COURSE_ID, note.getCourseId());
|
||||
values.put(DatabaseHelper.COLUMN_CHAPTER_ID, note.getChapterId());
|
||||
values.put(DatabaseHelper.COLUMN_FILE_PATH, note.getFilePath());
|
||||
values.put(DatabaseHelper.COLUMN_UPLOAD_TIME, note.getUploadTime());
|
||||
values.put(DatabaseHelper.COLUMN_UPDATE_TIME, note.getUpdateTime());
|
||||
|
||||
return db.insert(DatabaseHelper.TABLE_NOTES, null, values);
|
||||
}
|
||||
|
||||
public Note getNoteById(String noteId) {
|
||||
SQLiteDatabase db = dbHelper.getReadableDatabase();
|
||||
Cursor cursor = db.query(DatabaseHelper.TABLE_NOTES,
|
||||
null,
|
||||
DatabaseHelper.COLUMN_NOTE_ID + " = ?",
|
||||
new String[]{noteId},
|
||||
null, null, null);
|
||||
|
||||
if (cursor != null && cursor.moveToFirst()) {
|
||||
Note note = cursorToNote(cursor);
|
||||
cursor.close();
|
||||
return note;
|
||||
}
|
||||
|
||||
if (cursor != null) {
|
||||
cursor.close();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public List<Note> getAllNotes() {
|
||||
List<Note> notes = new ArrayList<>();
|
||||
SQLiteDatabase db = dbHelper.getReadableDatabase();
|
||||
Cursor cursor = db.query(DatabaseHelper.TABLE_NOTES, null, null, null, null, null,
|
||||
DatabaseHelper.COLUMN_UPLOAD_TIME + " DESC");
|
||||
|
||||
if (cursor != null && cursor.moveToFirst()) {
|
||||
do {
|
||||
notes.add(cursorToNote(cursor));
|
||||
} while (cursor.moveToNext());
|
||||
cursor.close();
|
||||
}
|
||||
|
||||
return notes;
|
||||
}
|
||||
|
||||
public List<Note> getNotesByCourse(String courseId) {
|
||||
List<Note> notes = new ArrayList<>();
|
||||
SQLiteDatabase db = dbHelper.getReadableDatabase();
|
||||
Cursor cursor = db.query(DatabaseHelper.TABLE_NOTES,
|
||||
null,
|
||||
DatabaseHelper.COLUMN_COURSE_ID + " = ?",
|
||||
new String[]{courseId},
|
||||
null, null, DatabaseHelper.COLUMN_UPLOAD_TIME + " DESC");
|
||||
|
||||
if (cursor != null && cursor.moveToFirst()) {
|
||||
do {
|
||||
notes.add(cursorToNote(cursor));
|
||||
} while (cursor.moveToNext());
|
||||
cursor.close();
|
||||
}
|
||||
|
||||
return notes;
|
||||
}
|
||||
|
||||
public List<Note> getNotesByChapter(String chapterId) {
|
||||
List<Note> notes = new ArrayList<>();
|
||||
SQLiteDatabase db = dbHelper.getReadableDatabase();
|
||||
Cursor cursor = db.query(DatabaseHelper.TABLE_NOTES,
|
||||
null,
|
||||
DatabaseHelper.COLUMN_CHAPTER_ID + " = ?",
|
||||
new String[]{chapterId},
|
||||
null, null, DatabaseHelper.COLUMN_UPLOAD_TIME + " DESC");
|
||||
|
||||
if (cursor != null && cursor.moveToFirst()) {
|
||||
do {
|
||||
notes.add(cursorToNote(cursor));
|
||||
} while (cursor.moveToNext());
|
||||
cursor.close();
|
||||
}
|
||||
|
||||
return notes;
|
||||
}
|
||||
|
||||
public int updateNote(Note note) {
|
||||
SQLiteDatabase db = dbHelper.getWritableDatabase();
|
||||
ContentValues values = new ContentValues();
|
||||
values.put(DatabaseHelper.COLUMN_TITLE, note.getTitle());
|
||||
values.put(DatabaseHelper.COLUMN_DESCRIPTION, note.getDescription());
|
||||
values.put(DatabaseHelper.COLUMN_OCR_TEXT, note.getOcrText());
|
||||
values.put(DatabaseHelper.COLUMN_COURSE_ID, note.getCourseId());
|
||||
values.put(DatabaseHelper.COLUMN_CHAPTER_ID, note.getChapterId());
|
||||
values.put(DatabaseHelper.COLUMN_FILE_PATH, note.getFilePath());
|
||||
values.put(DatabaseHelper.COLUMN_UPDATE_TIME, System.currentTimeMillis());
|
||||
|
||||
return db.update(DatabaseHelper.TABLE_NOTES, values,
|
||||
DatabaseHelper.COLUMN_NOTE_ID + " = ?",
|
||||
new String[]{note.getNoteId()});
|
||||
}
|
||||
|
||||
public int deleteNote(String noteId) {
|
||||
SQLiteDatabase db = dbHelper.getWritableDatabase();
|
||||
return db.delete(DatabaseHelper.TABLE_NOTES,
|
||||
DatabaseHelper.COLUMN_NOTE_ID + " = ?",
|
||||
new String[]{noteId});
|
||||
}
|
||||
|
||||
public int deleteNotesByCourse(String courseId) {
|
||||
SQLiteDatabase db = dbHelper.getWritableDatabase();
|
||||
return db.delete(DatabaseHelper.TABLE_NOTES,
|
||||
DatabaseHelper.COLUMN_COURSE_ID + " = ?",
|
||||
new String[]{courseId});
|
||||
}
|
||||
|
||||
private Note cursorToNote(Cursor cursor) {
|
||||
Note note = new Note();
|
||||
note.setNoteId(cursor.getString(cursor.getColumnIndexOrThrow(DatabaseHelper.COLUMN_NOTE_ID)));
|
||||
note.setTitle(cursor.getString(cursor.getColumnIndexOrThrow(DatabaseHelper.COLUMN_TITLE)));
|
||||
note.setDescription(cursor.getString(cursor.getColumnIndexOrThrow(DatabaseHelper.COLUMN_DESCRIPTION)));
|
||||
note.setOcrText(cursor.getString(cursor.getColumnIndexOrThrow(DatabaseHelper.COLUMN_OCR_TEXT)));
|
||||
note.setCourseId(cursor.getString(cursor.getColumnIndexOrThrow(DatabaseHelper.COLUMN_COURSE_ID)));
|
||||
note.setChapterId(cursor.getString(cursor.getColumnIndexOrThrow(DatabaseHelper.COLUMN_CHAPTER_ID)));
|
||||
note.setFilePath(cursor.getString(cursor.getColumnIndexOrThrow(DatabaseHelper.COLUMN_FILE_PATH)));
|
||||
note.setUploadTime(cursor.getLong(cursor.getColumnIndexOrThrow(DatabaseHelper.COLUMN_UPLOAD_TIME)));
|
||||
note.setUpdateTime(cursor.getLong(cursor.getColumnIndexOrThrow(DatabaseHelper.COLUMN_UPDATE_TIME)));
|
||||
return note;
|
||||
}
|
||||
|
||||
// 搜索笔记
|
||||
public List<Note> searchNotes(String query) {
|
||||
List<Note> notes = new ArrayList<>();
|
||||
SQLiteDatabase db = dbHelper.getReadableDatabase();
|
||||
|
||||
String selection = DatabaseHelper.COLUMN_TITLE + " LIKE ? OR " +
|
||||
DatabaseHelper.COLUMN_DESCRIPTION + " LIKE ? OR " +
|
||||
DatabaseHelper.COLUMN_OCR_TEXT + " LIKE ?";
|
||||
String[] selectionArgs = new String[]{"%" + query + "%", "%" + query + "%", "%" + query + "%"};
|
||||
|
||||
Cursor cursor = db.query(DatabaseHelper.TABLE_NOTES,
|
||||
null, selection, selectionArgs, null, null,
|
||||
DatabaseHelper.COLUMN_UPLOAD_TIME + " DESC");
|
||||
|
||||
if (cursor != null && cursor.moveToFirst()) {
|
||||
do {
|
||||
notes.add(cursorToNote(cursor));
|
||||
} while (cursor.moveToNext());
|
||||
cursor.close();
|
||||
}
|
||||
|
||||
return notes;
|
||||
}
|
||||
|
||||
// 关闭数据库连接
|
||||
public void close() {
|
||||
dbHelper.close();
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,314 @@
|
||||
package net.micode.myapplication.service;
|
||||
|
||||
import android.content.Context;
|
||||
import android.util.Log;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.JsonArray;
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.JsonObject;
|
||||
import com.google.gson.JsonParser;
|
||||
|
||||
import net.micode.myapplication.model.Chapter;
|
||||
import net.micode.myapplication.model.Course;
|
||||
import net.micode.myapplication.repository.CourseRepository;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class CourseOutlineImporter {
|
||||
private static final String TAG = "CourseOutlineImporter";
|
||||
private CourseRepository courseRepository;
|
||||
private Gson gson;
|
||||
|
||||
public CourseOutlineImporter(CourseRepository courseRepository) {
|
||||
this.courseRepository = courseRepository;
|
||||
this.gson = new Gson();
|
||||
}
|
||||
|
||||
/**
|
||||
* 从assets文件夹导入课程大纲
|
||||
*/
|
||||
public boolean importCourseOutlineFromAssets(Context context, String fileName) {
|
||||
try {
|
||||
InputStream inputStream = context.getAssets().open(fileName);
|
||||
return importCourseOutline(inputStream);
|
||||
} catch (IOException e) {
|
||||
Log.e(TAG, "无法读取assets文件: " + fileName, e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 从输入流导入课程大纲
|
||||
*/
|
||||
public boolean importCourseOutline(InputStream inputStream) {
|
||||
try {
|
||||
JsonObject jsonObject = JsonParser.parseReader(new InputStreamReader(inputStream)).getAsJsonObject();
|
||||
return parseAndImportCourseOutline(jsonObject);
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, "解析JSON失败", e);
|
||||
return false;
|
||||
} finally {
|
||||
try {
|
||||
inputStream.close();
|
||||
} catch (IOException e) {
|
||||
Log.e(TAG, "关闭输入流失败", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析并导入课程大纲
|
||||
*/
|
||||
private boolean parseAndImportCourseOutline(JsonObject jsonObject) {
|
||||
try {
|
||||
// 解析课程基本信息
|
||||
Course course = parseCourseInfo(jsonObject);
|
||||
if (course == null) {
|
||||
Log.e(TAG, "解析课程信息失败");
|
||||
return false;
|
||||
}
|
||||
|
||||
// 解析章节信息
|
||||
List<Chapter> chapters = parseChapters(jsonObject, course.getCourseId());
|
||||
if (chapters.isEmpty()) {
|
||||
Log.w(TAG, "没有找到章节信息");
|
||||
}
|
||||
|
||||
// 导入到数据库
|
||||
return importToDatabase(course, chapters);
|
||||
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, "解析课程大纲失败", e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析课程基本信息
|
||||
*/
|
||||
private Course parseCourseInfo(JsonObject jsonObject) {
|
||||
try {
|
||||
String courseId = getStringValue(jsonObject, "id");
|
||||
String title = getStringValue(jsonObject, "title");
|
||||
|
||||
if (courseId == null || title == null) {
|
||||
Log.e(TAG, "课程ID或标题为空");
|
||||
return null;
|
||||
}
|
||||
|
||||
Course course = new Course();
|
||||
course.setCourseId(courseId);
|
||||
course.setCourseName(title);
|
||||
|
||||
// 可选字段
|
||||
String school = getStringValue(jsonObject, "school");
|
||||
if (school != null) {
|
||||
course.setSchool(school);
|
||||
}
|
||||
|
||||
return course;
|
||||
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, "解析课程基本信息失败", e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析章节信息
|
||||
*/
|
||||
private List<Chapter> parseChapters(JsonObject jsonObject, String courseId) {
|
||||
List<Chapter> chapters = new ArrayList<>();
|
||||
|
||||
try {
|
||||
JsonArray nodes = jsonObject.getAsJsonArray("nodes");
|
||||
if (nodes == null) {
|
||||
Log.w(TAG, "没有找到nodes数组");
|
||||
return chapters;
|
||||
}
|
||||
|
||||
int chapterNumber = 1;
|
||||
for (JsonElement element : nodes) {
|
||||
if (element.isJsonObject()) {
|
||||
JsonObject chapterObj = element.getAsJsonObject();
|
||||
Chapter chapter = parseChapter(chapterObj, courseId, chapterNumber);
|
||||
if (chapter != null) {
|
||||
chapters.add(chapter);
|
||||
chapterNumber++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, "解析章节信息失败", e);
|
||||
}
|
||||
|
||||
return chapters;
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析单个章节
|
||||
*/
|
||||
private Chapter parseChapter(JsonObject chapterObj, String courseId, int chapterNumber) {
|
||||
try {
|
||||
String chapterId = getStringValue(chapterObj, "id");
|
||||
String chapterTitle = getStringValue(chapterObj, "title");
|
||||
|
||||
if (chapterId == null || chapterTitle == null) {
|
||||
Log.w(TAG, "章节ID或标题为空");
|
||||
return null;
|
||||
}
|
||||
|
||||
Chapter chapter = new Chapter();
|
||||
chapter.setChapterId(chapterId);
|
||||
chapter.setCourseId(courseId);
|
||||
chapter.setChapterNumber(chapterNumber);
|
||||
chapter.setChapterTitle(chapterTitle);
|
||||
|
||||
// 解析关键词
|
||||
List<String> keywords = parseKeywords(chapterObj);
|
||||
for (String keyword : keywords) {
|
||||
chapter.addKeyword(keyword);
|
||||
}
|
||||
|
||||
// 解析别名
|
||||
List<String> aliases = parseAliases(chapterObj);
|
||||
for (String alias : aliases) {
|
||||
chapter.addAlias(alias);
|
||||
}
|
||||
|
||||
return chapter;
|
||||
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, "解析单个章节失败", e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析关键词
|
||||
*/
|
||||
private List<String> parseKeywords(JsonObject obj) {
|
||||
List<String> keywords = new ArrayList<>();
|
||||
|
||||
try {
|
||||
JsonArray keywordsArray = obj.getAsJsonArray("keywords");
|
||||
if (keywordsArray != null) {
|
||||
for (JsonElement element : keywordsArray) {
|
||||
if (element.isJsonPrimitive()) {
|
||||
keywords.add(element.getAsString());
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
Log.w(TAG, "解析关键词失败", e);
|
||||
}
|
||||
|
||||
return keywords;
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析别名
|
||||
*/
|
||||
private List<String> parseAliases(JsonObject obj) {
|
||||
List<String> aliases = new ArrayList<>();
|
||||
|
||||
try {
|
||||
JsonArray aliasesArray = obj.getAsJsonArray("aliases");
|
||||
if (aliasesArray != null) {
|
||||
for (JsonElement element : aliasesArray) {
|
||||
if (element.isJsonPrimitive()) {
|
||||
aliases.add(element.getAsString());
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
Log.w(TAG, "解析别名失败", e);
|
||||
}
|
||||
|
||||
return aliases;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取字符串值
|
||||
*/
|
||||
private String getStringValue(JsonObject obj, String key) {
|
||||
try {
|
||||
JsonElement element = obj.get(key);
|
||||
if (element != null && element.isJsonPrimitive()) {
|
||||
return element.getAsString();
|
||||
}
|
||||
} catch (Exception e) {
|
||||
Log.w(TAG, "获取字符串值失败: " + key, e);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 导入到数据库
|
||||
*/
|
||||
private boolean importToDatabase(Course course, List<Chapter> chapters) {
|
||||
try {
|
||||
// 检查课程是否已存在
|
||||
Course existingCourse = courseRepository.getCourseById(course.getCourseId());
|
||||
if (existingCourse != null) {
|
||||
Log.i(TAG, "课程已存在,将更新数据: " + course.getCourseId());
|
||||
// 可以选择更新或跳过
|
||||
}
|
||||
|
||||
// 批量插入课程和章节
|
||||
courseRepository.insertCourseWithChapters(course, chapters);
|
||||
boolean success = true; // 方法没有返回值,假设成功
|
||||
|
||||
if (success) {
|
||||
Log.i(TAG, String.format("成功导入课程: %s (%d 个章节)",
|
||||
course.getCourseName(), chapters.size()));
|
||||
} else {
|
||||
Log.e(TAG, "导入课程失败");
|
||||
}
|
||||
|
||||
return success;
|
||||
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, "导入到数据库失败", e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量导入多个课程大纲
|
||||
*/
|
||||
public boolean importMultipleCourseOutlines(Context context, List<String> fileNames) {
|
||||
boolean allSuccess = true;
|
||||
int successCount = 0;
|
||||
|
||||
for (String fileName : fileNames) {
|
||||
boolean success = importCourseOutlineFromAssets(context, fileName);
|
||||
if (success) {
|
||||
successCount++;
|
||||
} else {
|
||||
allSuccess = false;
|
||||
Log.e(TAG, "导入失败: " + fileName);
|
||||
}
|
||||
}
|
||||
|
||||
Log.i(TAG, String.format("批量导入完成: %d/%d 成功", successCount, fileNames.size()));
|
||||
return allSuccess;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取导入状态信息
|
||||
*/
|
||||
public String getImportStatusMessage(boolean success, String fileName) {
|
||||
if (success) {
|
||||
return String.format("成功导入课程大纲: %s", fileName);
|
||||
} else {
|
||||
return String.format("导入失败: %s", fileName);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,32 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:aapt="http://schemas.android.com/aapt"
|
||||
android:width="108dp"
|
||||
android:height="108dp"
|
||||
android:viewportWidth="108"
|
||||
android:viewportHeight="108">
|
||||
<!-- 背景圆角卡片 -->
|
||||
<path android:pathData="M18,18h72v72h-72z" android:fillColor="#FFFFFF"/>
|
||||
<!-- 主笔记纸张 -->
|
||||
<path android:pathData="M24,24h60a8,8 0 0 1 8,8v44a8,8 0 0 1 -8,8H24a8,8 0 0 1 -8,-8V32a8,8 0 0 1 8,-8z">
|
||||
<aapt:attr name="android:fillColor">
|
||||
<gradient android:type="linear" android:startX="24" android:startY="24" android:endX="84" android:endY="84">
|
||||
<item android:offset="0" android:color="#F0F8FF"/>
|
||||
<item android:offset="1" android:color="#E6F2FF"/>
|
||||
</gradient>
|
||||
</aapt:attr>
|
||||
</path>
|
||||
<!-- 纸张左侧装订线 -->
|
||||
<path android:pathData="M32,32h2v48h-2z" android:fillColor="#4A90E2" android:alpha="0.3"/>
|
||||
<!-- 横线 -->
|
||||
<path android:pathData="M40,42h36" android:strokeColor="#4A90E2" android:strokeWidth="2" android:alpha="0.4"/>
|
||||
<path android:pathData="M40,50h36" android:strokeColor="#4A90E2" android:strokeWidth="2" android:alpha="0.4"/>
|
||||
<path android:pathData="M40,58h36" android:strokeColor="#4A90E2" android:strokeWidth="2" android:alpha="0.4"/>
|
||||
<path android:pathData="M40,66h36" android:strokeColor="#4A90E2" android:strokeWidth="2" android:alpha="0.4"/>
|
||||
<!-- 铅笔图标 -->
|
||||
<path android:pathData="M68,38l12,12l-6,6l-12,-12z" android:fillColor="#FFD93D"/>
|
||||
<path android:pathData="M74,44l-8,-8l4,-4l8,8z" android:fillColor="#FF6B6B"/>
|
||||
<!-- 铅笔芯 -->
|
||||
<path android:pathData="M66,36l2,2" android:strokeColor="#333333" android:strokeWidth="2"/>
|
||||
<!-- 笔记标题高亮 -->
|
||||
<path android:pathData="M40,38h20" android:strokeColor="#4A90E2" android:strokeWidth="4"/>
|
||||
</vector>
|
||||
@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<solid android:color="@color/bg_card"/>
|
||||
<corners android:radius="12dp"/>
|
||||
<stroke
|
||||
android:width="1dp"
|
||||
android:color="@color/divider"/>
|
||||
</shape>
|
||||
@ -0,0 +1,10 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="18dp"
|
||||
android:height="18dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
<path
|
||||
android:fillColor="@android:color/white"
|
||||
android:pathData="M21,19V5c0,-1.1 -0.9,-2 -2,-2H5c-1.1,0 -2,0.9 -2,2v14c0,1.1 0.9,2 2,2h14c1.1,0 2,-0.9 2,-2zM8.5,13.5l2.5,3.01L14.5,12l4.5,6H5l3.5,-4.5z"/>
|
||||
</vector>
|
||||
@ -0,0 +1,10 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="108dp"
|
||||
android:height="108dp"
|
||||
android:viewportWidth="108"
|
||||
android:viewportHeight="108">
|
||||
<path
|
||||
android:fillColor="#FFFFFF"
|
||||
android:pathData="M0,0h108v108h-108z"/>
|
||||
</vector>
|
||||
@ -0,0 +1,10 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="20dp"
|
||||
android:height="20dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
<path
|
||||
android:fillColor="@color/text_hint"
|
||||
android:pathData="M18,8h-1L17,6c0,-2.76 -2.24,-5 -5,-5S7,3.24 7,6v2L6,8c-1.1,0 -2,0.9 -2,2v10c0,1.1 0.9,2 2,2h12c1.1,0 2,-0.9 2,-2L20,10c0,-1.1 -0.9,-2 -2,-2zM12,17c-1.1,0 -2,-0.9 -2,-2s0.9,-2 2,-2 2,0.9 2,2 -0.9,2 -2,2zM15.1,8L8.9,8L8.9,6c0,-1.71 1.39,-3.1 3.1,-3.1 1.71,0 3.1,1.39 3.1,3.1v2z"/>
|
||||
</vector>
|
||||
@ -0,0 +1,10 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="20dp"
|
||||
android:height="20dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
<path
|
||||
android:fillColor="@color/text_hint"
|
||||
android:pathData="M12,12c2.21,0 4,-1.79 4,-4s-1.79,-4 -4,-4 -4,1.79 -4,4 1.79,4 4,4zM12,14c-2.67,0 -8,1.34 -8,4v2h16v-2c0,-2.66 -5.33,-4 -8,-4z"/>
|
||||
</vector>
|
||||
@ -0,0 +1,10 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="18dp"
|
||||
android:height="18dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
<path
|
||||
android:fillColor="@android:color/white"
|
||||
android:pathData="M12,5.9c1.16,0 2.1,0.94 2.1,2.1s-0.94,2.1 -2.1,2.1S9.9,9.16 9.9,8s0.94,-2.1 2.1,-2.1m0,9c2.97,0 6.1,1.46 6.1,2.1v1.1L5.9,18.1v-1.1c0,-0.64 3.13,-2.1 6.1,-2.1M12,4C9.79,4 8,5.79 8,8s1.79,4 4,4 4,-1.79 4,-4 -1.79,-4 -4,-4zM12,13c-2.67,0 -8,1.34 -8,4v3h16v-3c0,-2.66 -5.33,-4 -8,-4z"/>
|
||||
</vector>
|
||||
@ -0,0 +1,10 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="18dp"
|
||||
android:height="18dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
<path
|
||||
android:fillColor="@android:color/white"
|
||||
android:pathData="M15.5,14h-0.79l-0.28,-0.27C15.41,12.59 16,11.11 16,9.5 16,5.91 13.09,3 9.5,3S3,5.91 3,9.5 5.91,16 9.5,16c1.61,0 3.09,-0.59 4.23,-1.57l0.27,0.28v0.79l5,4.99L20.49,19l-4.99,-5zM9.5,14C7.01,14 5,11.99 5,9.5S7.01,5 9.5,5 14,7.01 14,9.5 11.99,14 9.5,14z"/>
|
||||
</vector>
|
||||
@ -0,0 +1,10 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="18dp"
|
||||
android:height="18dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
<path
|
||||
android:fillColor="@android:color/white"
|
||||
android:pathData="M9,16h6v-6h4l-7,-7 -7,7h4zM4,18h16v2L4,20z"/>
|
||||
</vector>
|
||||
@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<solid android:color="@color/bg_input"/>
|
||||
<corners android:radius="12dp"/>
|
||||
<stroke
|
||||
android:width="1dp"
|
||||
android:color="@color/divider"/>
|
||||
</shape>
|
||||
@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<solid android:color="@color/bg_card"/>
|
||||
<corners android:radius="8dp"/>
|
||||
<stroke
|
||||
android:width="1dp"
|
||||
android:color="@color/divider"/>
|
||||
</shape>
|
||||
@ -0,0 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<solid android:color="@color/accent"/>
|
||||
<corners android:radius="12dp"/>
|
||||
</shape>
|
||||
@ -0,0 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<solid android:color="@color/primary"/>
|
||||
<corners android:radius="12dp"/>
|
||||
</shape>
|
||||
@ -0,0 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<solid android:color="@color/success"/>
|
||||
<corners android:radius="12dp"/>
|
||||
</shape>
|
||||
@ -0,0 +1,104 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:background="@color/primary"
|
||||
android:fillViewport="true"
|
||||
android:padding="24dp"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<LinearLayout
|
||||
android:background="@color/bg_card"
|
||||
android:padding="32dp"
|
||||
android:elevation="8dp"
|
||||
android:layout_gravity="center"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
android:layout_margin="16dp">
|
||||
|
||||
<TextView
|
||||
android:text="课程笔记共享系统"
|
||||
android:textSize="28sp"
|
||||
android:textStyle="bold"
|
||||
android:textColor="@color/text_primary"
|
||||
android:layout_marginBottom="8dp"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center_horizontal"/>
|
||||
|
||||
<TextView
|
||||
android:text="知识共享,学习无界"
|
||||
android:textSize="16sp"
|
||||
android:textColor="@color/text_secondary"
|
||||
android:layout_marginBottom="32dp"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center_horizontal"/>
|
||||
|
||||
<TextView
|
||||
android:text="用户名"
|
||||
android:textSize="14sp"
|
||||
android:textColor="@color/text_secondary"
|
||||
android:layout_marginBottom="8dp"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"/>
|
||||
|
||||
<EditText
|
||||
android:id="@+id/usernameInput"
|
||||
android:hint="请输入用户名"
|
||||
android:layout_marginBottom="20dp"
|
||||
android:background="@drawable/input_background"
|
||||
android:padding="16dp"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="56dp"
|
||||
android:inputType="textPersonName"
|
||||
android:drawableLeft="@drawable/ic_person"
|
||||
android:drawablePadding="12dp"
|
||||
android:textSize="16sp"
|
||||
android:drawableTint="@color/text_hint"/>
|
||||
|
||||
<TextView
|
||||
android:text="密码"
|
||||
android:textSize="14sp"
|
||||
android:textColor="@color/text_secondary"
|
||||
android:layout_marginBottom="8dp"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"/>
|
||||
|
||||
<EditText
|
||||
android:id="@+id/passwordInput"
|
||||
android:hint="请输入密码"
|
||||
android:layout_marginBottom="32dp"
|
||||
android:background="@drawable/input_background"
|
||||
android:padding="16dp"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="56dp"
|
||||
android:inputType="textPassword"
|
||||
android:drawableLeft="@drawable/ic_lock"
|
||||
android:drawablePadding="12dp"
|
||||
android:textSize="16sp"
|
||||
android:drawableTint="@color/text_hint"/>
|
||||
|
||||
<Button
|
||||
android:id="@+id/loginBtn"
|
||||
android:text="登录"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="56dp"
|
||||
android:backgroundTint="@color/primary"
|
||||
android:textColor="@android:color/white"
|
||||
android:textSize="16sp"
|
||||
android:textStyle="bold"
|
||||
android:elevation="4dp"
|
||||
android:layout_marginBottom="24dp"/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/registerLink"
|
||||
android:text="还没有账号?立即注册"
|
||||
android:textColor="@color/primary"
|
||||
android:textSize="15sp"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center_horizontal"
|
||||
android:padding="8dp"/>
|
||||
</LinearLayout>
|
||||
</ScrollView>
|
||||
@ -0,0 +1,150 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:background="@color/bg_main"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
<LinearLayout
|
||||
android:orientation="vertical"
|
||||
android:padding="20dp"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<!-- 顶部栏 -->
|
||||
<LinearLayout
|
||||
android:background="@color/primary"
|
||||
android:padding="20dp"
|
||||
android:orientation="horizontal"
|
||||
android:gravity="center_vertical"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:elevation="4dp">
|
||||
<TextView
|
||||
android:id="@+id/welcomeText"
|
||||
android:text="欢迎,张三"
|
||||
android:textColor="@android:color/white"
|
||||
android:textSize="20sp"
|
||||
android:textStyle="bold"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"/>
|
||||
<Button
|
||||
android:id="@+id/logoutBtn"
|
||||
android:text="退出登录"
|
||||
android:textColor="@android:color/white"
|
||||
android:backgroundTint="@color/primary_dark"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="40dp"
|
||||
android:paddingLeft="16dp"
|
||||
android:paddingRight="16dp"
|
||||
android:textSize="14sp"/>
|
||||
</LinearLayout>
|
||||
|
||||
<!-- 搜索栏 -->
|
||||
<LinearLayout
|
||||
android:orientation="horizontal"
|
||||
android:padding="20dp"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<Button
|
||||
android:id="@+id/searchBtn"
|
||||
android:layout_width="11dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginRight="8dp"
|
||||
android:layout_weight="1"
|
||||
android:backgroundTint="@color/primary"
|
||||
android:drawableTop="@drawable/ic_search"
|
||||
android:drawablePadding="4dp"
|
||||
android:text="搜索"
|
||||
android:textColor="@android:color/white"
|
||||
android:textSize="16sp" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/uploadBtn"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginRight="8dp"
|
||||
android:layout_weight="1"
|
||||
android:backgroundTint="@color/accent"
|
||||
android:drawableTop="@drawable/ic_upload"
|
||||
android:drawablePadding="4dp"
|
||||
android:text="上传"
|
||||
android:textColor="@android:color/white"
|
||||
android:textSize="16sp" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/userCenterBtn"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:backgroundTint="@color/primary"
|
||||
android:drawableTop="@drawable/ic_person_outline"
|
||||
android:drawablePadding="4dp"
|
||||
android:text="我的"
|
||||
android:textColor="@android:color/white"
|
||||
android:textSize="16sp" />
|
||||
</LinearLayout>
|
||||
|
||||
<!-- 推荐笔记 -->
|
||||
<LinearLayout
|
||||
android:orientation="vertical"
|
||||
android:padding="4dp"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="24dp">
|
||||
<LinearLayout
|
||||
android:orientation="horizontal"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="16dp">
|
||||
<TextView
|
||||
android:text="为您推荐的笔记"
|
||||
android:textSize="18sp"
|
||||
android:textColor="@color/text_primary"
|
||||
android:textStyle="bold"
|
||||
android:layout_width="0dp"
|
||||
android:layout_weight="1"
|
||||
android:layout_height="wrap_content"/>
|
||||
<Button
|
||||
android:id="@+id/recommendedRefreshBtn"
|
||||
android:text="刷新推荐"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="40dp"
|
||||
android:backgroundTint="@color/primary_light"
|
||||
android:textColor="@color/primary"
|
||||
android:textSize="14sp"/>
|
||||
</LinearLayout>
|
||||
|
||||
<!-- 推荐渲染容器 -->
|
||||
<LinearLayout
|
||||
android:id="@+id/recommendedNotesContainer"
|
||||
android:orientation="vertical"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="16dp"/>
|
||||
</LinearLayout>
|
||||
|
||||
<!-- 笔记列表 -->
|
||||
<LinearLayout
|
||||
android:orientation="vertical"
|
||||
android:padding="4dp"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
<TextView
|
||||
android:text="最新笔记"
|
||||
android:textSize="18sp"
|
||||
android:textColor="@color/text_primary"
|
||||
android:textStyle="bold"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="16dp"/>
|
||||
|
||||
<!-- 动态渲染容器 -->
|
||||
<LinearLayout
|
||||
android:id="@+id/latestNotesContainer"
|
||||
android:orientation="vertical"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"/>
|
||||
</LinearLayout>
|
||||
</LinearLayout>
|
||||
</ScrollView>
|
||||
@ -0,0 +1,261 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:background="@color/bg_main"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
<LinearLayout
|
||||
android:orientation="vertical"
|
||||
android:padding="24dp"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<!-- 顶部导航栏 -->
|
||||
<LinearLayout
|
||||
android:orientation="horizontal"
|
||||
android:gravity="center_vertical"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="24dp">
|
||||
<TextView
|
||||
android:text="笔记详情"
|
||||
android:textStyle="bold"
|
||||
android:textSize="24sp"
|
||||
android:textColor="@color/text_primary"
|
||||
android:layout_width="0dp"
|
||||
android:layout_weight="1"
|
||||
android:layout_height="wrap_content"/>
|
||||
<Button
|
||||
android:id="@+id/backBtn"
|
||||
android:text="返回"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="40dp"
|
||||
android:backgroundTint="@color/primary_dark"
|
||||
android:textColor="@android:color/white"
|
||||
android:textSize="14sp"
|
||||
android:paddingLeft="16dp"
|
||||
android:paddingRight="16dp"/>
|
||||
</LinearLayout>
|
||||
|
||||
<!-- 笔记内容卡片 -->
|
||||
<LinearLayout
|
||||
android:orientation="vertical"
|
||||
android:background="@drawable/card_background"
|
||||
android:padding="24dp"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="20dp">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/noteTitle"
|
||||
android:text="笔记标题"
|
||||
android:textStyle="bold"
|
||||
android:textSize="22sp"
|
||||
android:textColor="@color/text_primary"
|
||||
android:layout_marginBottom="12dp"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/noteMeta"
|
||||
android:text="元信息"
|
||||
android:textColor="@color/text_secondary"
|
||||
android:textSize="14sp"
|
||||
android:layout_marginBottom="20dp"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/noteBody"
|
||||
android:textColor="@color/text_primary"
|
||||
android:textSize="16sp"
|
||||
android:lineSpacingMultiplier="1.6"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"/>
|
||||
</LinearLayout>
|
||||
|
||||
<!-- 编辑模式输入区域 -->
|
||||
<LinearLayout
|
||||
android:orientation="vertical"
|
||||
android:background="@drawable/card_background"
|
||||
android:padding="20dp"
|
||||
android:visibility="gone"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="20dp">
|
||||
|
||||
<TextView
|
||||
android:text="编辑笔记"
|
||||
android:textSize="18sp"
|
||||
android:textColor="@color/text_primary"
|
||||
android:textStyle="bold"
|
||||
android:layout_marginBottom="16dp"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"/>
|
||||
|
||||
<TextView
|
||||
android:text="标题"
|
||||
android:textSize="14sp"
|
||||
android:textColor="@color/text_secondary"
|
||||
android:layout_marginBottom="8dp"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"/>
|
||||
<EditText
|
||||
android:id="@+id/editNoteTitle"
|
||||
android:hint="编辑标题"
|
||||
android:background="@drawable/input_background"
|
||||
android:padding="16dp"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="56dp"
|
||||
android:textSize="16sp"
|
||||
android:layout_marginBottom="16dp"/>
|
||||
|
||||
<TextView
|
||||
android:text="内容"
|
||||
android:textSize="14sp"
|
||||
android:textColor="@color/text_secondary"
|
||||
android:layout_marginBottom="8dp"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"/>
|
||||
<EditText
|
||||
android:id="@+id/editNoteBody"
|
||||
android:hint="编辑内容"
|
||||
android:minLines="8"
|
||||
android:gravity="top"
|
||||
android:background="@drawable/input_background"
|
||||
android:padding="16dp"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:textSize="15sp"
|
||||
android:lineSpacingMultiplier="1.4"/>
|
||||
</LinearLayout>
|
||||
|
||||
<!-- 操作按钮区域 -->
|
||||
<LinearLayout
|
||||
android:orientation="horizontal"
|
||||
android:gravity="center_vertical"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="16dp">
|
||||
|
||||
<Button
|
||||
android:id="@+id/downloadBtn"
|
||||
android:text="📥 下载笔记"
|
||||
android:layout_width="0dp"
|
||||
android:layout_weight="1"
|
||||
android:layout_height="48dp"
|
||||
android:backgroundTint="@color/primary"
|
||||
android:textColor="@android:color/white"
|
||||
android:textSize="15sp"
|
||||
android:layout_marginRight="8dp"/>
|
||||
|
||||
<Button
|
||||
android:id="@+id/likeBtn"
|
||||
android:text="👍 点赞"
|
||||
android:layout_width="0dp"
|
||||
android:layout_weight="1"
|
||||
android:layout_height="48dp"
|
||||
android:backgroundTint="@color/accent"
|
||||
android:textColor="@android:color/white"
|
||||
android:textSize="15sp"
|
||||
android:layout_marginRight="8dp"/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/likeCount"
|
||||
android:text="👍 0"
|
||||
android:textColor="@color/text_primary"
|
||||
android:textSize="16sp"
|
||||
android:gravity="center"
|
||||
android:layout_width="80dp"
|
||||
android:layout_height="48dp"
|
||||
android:background="@drawable/input_background"
|
||||
android:padding="8dp"/>
|
||||
</LinearLayout>
|
||||
|
||||
<!-- 编辑操作按钮区域(仅作者可见) -->
|
||||
<LinearLayout
|
||||
android:id="@+id/editActions"
|
||||
android:orientation="horizontal"
|
||||
android:visibility="gone"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<Button
|
||||
android:id="@+id/startEditBtn"
|
||||
android:text="编辑笔记"
|
||||
android:layout_width="0dp"
|
||||
android:layout_weight="1"
|
||||
android:layout_height="48dp"
|
||||
android:backgroundTint="@color/warning"
|
||||
android:textColor="@android:color/white"
|
||||
android:textSize="15sp"
|
||||
android:layout_marginRight="8dp"/>
|
||||
|
||||
<Button
|
||||
android:id="@+id/saveEditBtn"
|
||||
android:text="保存修改"
|
||||
android:visibility="gone"
|
||||
android:layout_width="0dp"
|
||||
android:layout_weight="1"
|
||||
android:layout_height="48dp"
|
||||
android:backgroundTint="@color/success"
|
||||
android:textColor="@android:color/white"
|
||||
android:textSize="15sp"
|
||||
android:layout_marginRight="8dp"/>
|
||||
|
||||
<Button
|
||||
android:id="@+id/deleteNoteBtn"
|
||||
android:text="删除笔记"
|
||||
android:visibility="gone"
|
||||
android:layout_width="0dp"
|
||||
android:layout_weight="1"
|
||||
android:layout_height="48dp"
|
||||
android:backgroundTint="@color/danger"
|
||||
android:textColor="@android:color/white"
|
||||
android:textSize="15sp"/>
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:orientation="vertical"
|
||||
android:background="@drawable/card_background"
|
||||
android:padding="20dp"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
<TextView
|
||||
android:text="评论"
|
||||
android:textStyle="bold"
|
||||
android:textSize="18sp"
|
||||
android:textColor="@color/text_primary"
|
||||
android:layout_marginBottom="12dp"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"/>
|
||||
<LinearLayout
|
||||
android:id="@+id/commentListContainer"
|
||||
android:orientation="vertical"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="12dp"/>
|
||||
<LinearLayout
|
||||
android:orientation="horizontal"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
<EditText
|
||||
android:id="@+id/commentInput"
|
||||
android:hint="发表你的看法"
|
||||
android:background="@drawable/input_background"
|
||||
android:padding="12dp"
|
||||
android:layout_width="0dp"
|
||||
android:layout_weight="1"
|
||||
android:layout_height="48dp"/>
|
||||
<Button
|
||||
android:id="@+id/commentSubmitBtn"
|
||||
android:text="发布"
|
||||
android:layout_width="100dp"
|
||||
android:layout_height="48dp"
|
||||
android:backgroundTint="@color/success"
|
||||
android:textColor="@android:color/white"
|
||||
android:textSize="14sp"
|
||||
android:layout_marginLeft="8dp"/>
|
||||
</LinearLayout>
|
||||
</LinearLayout>
|
||||
</LinearLayout>
|
||||
</ScrollView>
|
||||
@ -0,0 +1,70 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
<LinearLayout
|
||||
android:orientation="vertical"
|
||||
android:padding="16dp"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<TextView
|
||||
android:text="编辑笔记"
|
||||
android:textStyle="bold"
|
||||
android:textSize="18sp"
|
||||
android:textColor="#333"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"/>
|
||||
|
||||
<EditText
|
||||
android:id="@+id/editNoteTitle"
|
||||
android:hint="标题"
|
||||
android:layout_marginTop="12dp"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="48dp"/>
|
||||
|
||||
<EditText
|
||||
android:id="@+id/editNoteContent"
|
||||
android:hint="内容"
|
||||
android:minLines="6"
|
||||
android:gravity="top"
|
||||
android:layout_marginTop="8dp"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"/>
|
||||
|
||||
<LinearLayout
|
||||
android:orientation="horizontal"
|
||||
android:layout_marginTop="12dp"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<Button
|
||||
android:id="@+id/saveNoteBtn"
|
||||
android:text="保存"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="48dp"
|
||||
android:layout_weight="1"
|
||||
android:backgroundTint="#4CAF50"
|
||||
android:textColor="@android:color/white"/>
|
||||
|
||||
<Space android:layout_width="8dp" android:layout_height="1dp"/>
|
||||
|
||||
<Button
|
||||
android:id="@+id/cancelEditBtn"
|
||||
android:text="取消"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="48dp"
|
||||
android:layout_weight="1"/>
|
||||
</LinearLayout>
|
||||
|
||||
<Button
|
||||
android:id="@+id/deleteNoteBtn"
|
||||
android:text="删除笔记"
|
||||
android:layout_marginTop="8dp"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="48dp"
|
||||
android:backgroundTint="#DC3545"
|
||||
android:textColor="@android:color/white"/>
|
||||
|
||||
</LinearLayout>
|
||||
</ScrollView>
|
||||
@ -0,0 +1,272 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:background="#F5F5F5"
|
||||
android:padding="16dp">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical">
|
||||
|
||||
<!-- 标题 -->
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="上传笔记"
|
||||
android:textSize="24sp"
|
||||
android:textStyle="bold"
|
||||
android:textColor="#333333"
|
||||
android:layout_marginBottom="24dp"
|
||||
android:gravity="center" />
|
||||
|
||||
<!-- 文件选择区域 -->
|
||||
<com.google.android.material.card.MaterialCardView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="16dp"
|
||||
app:cardCornerRadius="8dp"
|
||||
app:cardElevation="2dp">
|
||||
|
||||
<LinearLayout
|
||||
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:text="选择文件"
|
||||
android:textSize="16sp"
|
||||
android:textStyle="bold"
|
||||
android:layout_marginBottom="8dp" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/btn_select_file"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="选择图片或PDF文件"
|
||||
android:drawableLeft="@android:drawable/ic_menu_gallery"
|
||||
android:drawablePadding="8dp"
|
||||
android:backgroundTint="#2196F3"
|
||||
android:textColor="@android:color/white" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tv_selected_file"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="未选择文件"
|
||||
android:textSize="14sp"
|
||||
android:textColor="#666666"
|
||||
android:layout_marginTop="8dp"
|
||||
android:padding="8dp"
|
||||
android:background="#F0F0F0" />
|
||||
|
||||
</LinearLayout>
|
||||
</com.google.android.material.card.MaterialCardView>
|
||||
|
||||
<!-- 笔记信息区域 -->
|
||||
<com.google.android.material.card.MaterialCardView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="16dp"
|
||||
app:cardCornerRadius="8dp"
|
||||
app:cardElevation="2dp">
|
||||
|
||||
<LinearLayout
|
||||
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:text="笔记信息"
|
||||
android:textSize="16sp"
|
||||
android:textStyle="bold"
|
||||
android:layout_marginBottom="12dp" />
|
||||
|
||||
<com.google.android.material.textfield.TextInputLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="12dp"
|
||||
android:hint="笔记标题">
|
||||
|
||||
<com.google.android.material.textfield.TextInputEditText
|
||||
android:id="@+id/et_note_title"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:inputType="text"
|
||||
android:maxLines="1" />
|
||||
</com.google.android.material.textfield.TextInputLayout>
|
||||
|
||||
<com.google.android.material.textfield.TextInputLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:hint="笔记描述">
|
||||
|
||||
<com.google.android.material.textfield.TextInputEditText
|
||||
android:id="@+id/et_note_description"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:inputType="textMultiLine"
|
||||
android:minLines="3"
|
||||
android:maxLines="5" />
|
||||
</com.google.android.material.textfield.TextInputLayout>
|
||||
|
||||
</LinearLayout>
|
||||
</com.google.android.material.card.MaterialCardView>
|
||||
|
||||
<!-- 分类推荐区域 -->
|
||||
<com.google.android.material.card.MaterialCardView
|
||||
android:id="@+id/card_classification"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="16dp"
|
||||
android:visibility="gone"
|
||||
app:cardCornerRadius="8dp"
|
||||
app:cardElevation="2dp">
|
||||
|
||||
<LinearLayout
|
||||
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:text="自动分类推荐"
|
||||
android:textSize="16sp"
|
||||
android:textStyle="bold"
|
||||
android:layout_marginBottom="12dp" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tv_classification_result"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="正在分析..."
|
||||
android:textSize="14sp"
|
||||
android:textColor="#333333"
|
||||
android:layout_marginBottom="8dp" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tv_classification_confidence"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text=""
|
||||
android:textSize="12sp"
|
||||
android:textColor="#666666"
|
||||
android:layout_marginBottom="12dp" />
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<Button
|
||||
android:id="@+id/btn_accept_classification"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:text="接受推荐"
|
||||
android:layout_marginEnd="8dp"
|
||||
android:backgroundTint="#4CAF50"
|
||||
android:textColor="@android:color/white" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/btn_reject_classification"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:text="手动选择"
|
||||
android:backgroundTint="#FF9800"
|
||||
android:textColor="@android:color/white" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</LinearLayout>
|
||||
</com.google.android.material.card.MaterialCardView>
|
||||
|
||||
<!-- 手动选择区域 -->
|
||||
<com.google.android.material.card.MaterialCardView
|
||||
android:id="@+id/card_manual_selection"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="16dp"
|
||||
android:visibility="gone"
|
||||
app:cardCornerRadius="8dp"
|
||||
app:cardElevation="2dp">
|
||||
|
||||
<LinearLayout
|
||||
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:text="手动选择课程和章节"
|
||||
android:textSize="16sp"
|
||||
android:textStyle="bold"
|
||||
android:layout_marginBottom="12dp" />
|
||||
|
||||
<Spinner
|
||||
android:id="@+id/spinner_course"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="12dp" />
|
||||
|
||||
<Spinner
|
||||
android:id="@+id/spinner_chapter"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content" />
|
||||
|
||||
</LinearLayout>
|
||||
</com.google.android.material.card.MaterialCardView>
|
||||
|
||||
<!-- 操作按钮 -->
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal"
|
||||
android:layout_marginTop="24dp">
|
||||
|
||||
<Button
|
||||
android:id="@+id/btn_cancel"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:text="取消"
|
||||
android:layout_marginEnd="8dp"
|
||||
android:backgroundTint="#9E9E9E"
|
||||
android:textColor="@android:color/white" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/btn_upload"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:text="上传笔记"
|
||||
android:layout_marginStart="8dp"
|
||||
android:backgroundTint="#2196F3"
|
||||
android:textColor="@android:color/white" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<!-- 进度条 -->
|
||||
<ProgressBar
|
||||
android:id="@+id/progress_bar"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center"
|
||||
android:visibility="gone"
|
||||
android:layout_marginTop="16dp" />
|
||||
|
||||
</LinearLayout>
|
||||
</ScrollView>
|
||||
@ -0,0 +1,90 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:background="@color/bg_main"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
<LinearLayout
|
||||
android:orientation="vertical"
|
||||
android:padding="24dp"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<!-- 顶部导航栏 -->
|
||||
<LinearLayout
|
||||
android:orientation="horizontal"
|
||||
android:gravity="center_vertical"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="24dp">
|
||||
<TextView
|
||||
android:text="搜索笔记"
|
||||
android:textStyle="bold"
|
||||
android:textSize="24sp"
|
||||
android:textColor="@color/text_primary"
|
||||
android:layout_width="0dp"
|
||||
android:layout_weight="1"
|
||||
android:layout_height="wrap_content"/>
|
||||
<Button
|
||||
android:id="@+id/backBtn"
|
||||
android:text="返回"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="40dp"
|
||||
android:backgroundTint="@color/primary_dark"
|
||||
android:textColor="@android:color/white"
|
||||
android:textSize="14sp"
|
||||
android:paddingLeft="16dp"
|
||||
android:paddingRight="16dp"/>
|
||||
</LinearLayout>
|
||||
|
||||
<!-- 搜索输入区域 -->
|
||||
<LinearLayout
|
||||
android:orientation="horizontal"
|
||||
android:background="@drawable/card_background"
|
||||
android:padding="20dp"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="24dp">
|
||||
|
||||
<EditText
|
||||
android:id="@+id/searchInput"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="56dp"
|
||||
android:layout_marginRight="12dp"
|
||||
android:layout_weight="1"
|
||||
android:background="@drawable/input_background"
|
||||
android:drawableLeft="@drawable/ic_search"
|
||||
android:drawablePadding="12dp"
|
||||
android:drawableTint="@color/text_hint"
|
||||
android:hint="搜索笔记"
|
||||
android:padding="16dp"
|
||||
android:textSize="16sp" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/searchBtn"
|
||||
android:text="搜索"
|
||||
android:layout_width="80dp"
|
||||
android:layout_height="56dp"
|
||||
android:backgroundTint="@color/primary"
|
||||
android:textColor="@android:color/white"
|
||||
android:textSize="16sp"
|
||||
android:textStyle="bold"/>
|
||||
</LinearLayout>
|
||||
|
||||
<!-- 搜索结果统计 -->
|
||||
<TextView
|
||||
android:id="@+id/resultsInfo"
|
||||
android:text=""
|
||||
android:textColor="@color/text_secondary"
|
||||
android:textSize="14sp"
|
||||
android:layout_marginBottom="16dp"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"/>
|
||||
|
||||
<!-- 搜索结果容器 -->
|
||||
<LinearLayout
|
||||
android:id="@+id/resultsContainer"
|
||||
android:orientation="vertical"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"/>
|
||||
</LinearLayout>
|
||||
</ScrollView>
|
||||
@ -0,0 +1,244 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:background="@color/bg_main"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<LinearLayout
|
||||
android:orientation="vertical"
|
||||
android:padding="24dp"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<!-- 顶部标题栏 -->
|
||||
<LinearLayout
|
||||
android:orientation="horizontal"
|
||||
android:gravity="center_vertical"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="24dp">
|
||||
<TextView
|
||||
android:text="上传学习笔记"
|
||||
android:textStyle="bold"
|
||||
android:textSize="24sp"
|
||||
android:textColor="@color/text_primary"
|
||||
android:layout_width="0dp"
|
||||
android:layout_weight="1"
|
||||
android:layout_height="wrap_content"/>
|
||||
<Button
|
||||
android:id="@+id/backBtn"
|
||||
android:text="返回主页"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="40dp"
|
||||
android:backgroundTint="@color/primary_dark"
|
||||
android:textColor="@android:color/white"
|
||||
android:textSize="14sp"
|
||||
android:paddingLeft="16dp"
|
||||
android:paddingRight="16dp"/>
|
||||
</LinearLayout>
|
||||
|
||||
<!-- 文件选择与识别区域 -->
|
||||
<LinearLayout
|
||||
android:orientation="vertical"
|
||||
android:background="@drawable/card_background"
|
||||
android:padding="20dp"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="24dp">
|
||||
|
||||
<TextView
|
||||
android:text="选择笔记文件"
|
||||
android:textSize="16sp"
|
||||
android:textColor="@color/text_primary"
|
||||
android:textStyle="bold"
|
||||
android:layout_marginBottom="16dp"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"/>
|
||||
|
||||
<Button
|
||||
android:id="@+id/selectFileBtn"
|
||||
android:text="选择图片文件"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="56dp"
|
||||
android:backgroundTint="@color/accent"
|
||||
android:textColor="@android:color/white"
|
||||
android:textSize="16sp"
|
||||
android:drawableLeft="@drawable/ic_image"
|
||||
android:drawablePadding="12dp"
|
||||
android:layout_marginBottom="16dp"/>
|
||||
|
||||
<!-- 文件预览(图片时展示) -->
|
||||
<ImageView
|
||||
android:id="@+id/previewImage"
|
||||
android:contentDescription="文件预览"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="200dp"
|
||||
android:scaleType="centerCrop"
|
||||
android:background="@drawable/input_background"
|
||||
android:layout_marginBottom="12dp"
|
||||
android:visibility="gone"/>
|
||||
|
||||
<!-- 文件信息提示(非图片文件时显示) -->
|
||||
<TextView
|
||||
android:id="@+id/fileInfoText"
|
||||
android:text=""
|
||||
android:visibility="gone"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:textColor="@color/text_secondary"
|
||||
android:textSize="14sp"/>
|
||||
|
||||
<!-- 动态文件列表 -->
|
||||
<LinearLayout
|
||||
android:id="@+id/fileListContainer"
|
||||
android:orientation="vertical"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"/>
|
||||
</LinearLayout>
|
||||
|
||||
<!-- 笔记信息填写区域 -->
|
||||
<LinearLayout
|
||||
android:orientation="vertical"
|
||||
android:background="@drawable/card_background"
|
||||
android:padding="20dp"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<TextView
|
||||
android:text="笔记信息"
|
||||
android:textSize="18sp"
|
||||
android:textColor="@color/text_primary"
|
||||
android:textStyle="bold"
|
||||
android:layout_marginBottom="20dp"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"/>
|
||||
|
||||
<!-- 自动分类推荐 -->
|
||||
<TextView
|
||||
android:id="@+id/classificationResultText"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="16dp"
|
||||
android:padding="12dp"
|
||||
android:background="@color/primary_light"
|
||||
android:textColor="@color/primary"
|
||||
android:textSize="14sp"
|
||||
android:text="智能分类推荐将显示在这里"
|
||||
android:visibility="gone" />
|
||||
|
||||
<TextView
|
||||
android:text="笔记标题"
|
||||
android:textSize="14sp"
|
||||
android:textColor="@color/text_secondary"
|
||||
android:layout_marginBottom="8dp"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"/>
|
||||
<EditText
|
||||
android:id="@+id/noteTitle"
|
||||
android:hint="请输入笔记标题"
|
||||
android:background="@drawable/input_background"
|
||||
android:padding="16dp"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="56dp"
|
||||
android:textSize="16sp"
|
||||
android:layout_marginBottom="16dp"/>
|
||||
|
||||
<TextView
|
||||
android:text="科目分类"
|
||||
android:textSize="14sp"
|
||||
android:textColor="@color/text_secondary"
|
||||
android:layout_marginBottom="8dp"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"/>
|
||||
<Spinner
|
||||
android:id="@+id/subjectSpinner"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="56dp"
|
||||
android:background="@drawable/spinner_background"
|
||||
android:padding="16dp"
|
||||
android:layout_marginBottom="16dp"/>
|
||||
|
||||
<TextView
|
||||
android:text="章节"
|
||||
android:textSize="14sp"
|
||||
android:textColor="@color/text_secondary"
|
||||
android:layout_marginBottom="8dp"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"/>
|
||||
<Spinner
|
||||
android:id="@+id/chapterSpinner"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="56dp"
|
||||
android:background="@drawable/spinner_background"
|
||||
android:padding="16dp"
|
||||
android:layout_marginBottom="16dp"/>
|
||||
|
||||
<TextView
|
||||
android:text="笔记内容"
|
||||
android:textSize="14sp"
|
||||
android:textColor="@color/text_secondary"
|
||||
android:layout_marginBottom="8dp"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"/>
|
||||
<EditText
|
||||
android:id="@+id/contentInput"
|
||||
android:hint="粘贴或自动填充OCR识别的全文内容"
|
||||
android:minLines="6"
|
||||
android:gravity="top"
|
||||
android:background="@drawable/input_background"
|
||||
android:padding="16dp"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:textSize="15sp"
|
||||
android:lineSpacingMultiplier="1.4"
|
||||
android:layout_marginBottom="16dp"/>
|
||||
|
||||
<TextView
|
||||
android:text="关键标签"
|
||||
android:textSize="14sp"
|
||||
android:textColor="@color/text_secondary"
|
||||
android:layout_marginBottom="8dp"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"/>
|
||||
<EditText
|
||||
android:id="@+id/tagsInput"
|
||||
android:hint="请输入关键词,用逗号分隔"
|
||||
android:background="@drawable/input_background"
|
||||
android:padding="16dp"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="56dp"
|
||||
android:textSize="16sp"
|
||||
android:layout_marginBottom="20dp"/>
|
||||
|
||||
<!-- 上传进度 -->
|
||||
<ProgressBar
|
||||
android:id="@+id/progressBar"
|
||||
style="@android:style/Widget.ProgressBar.Horizontal"
|
||||
android:max="100"
|
||||
android:visibility="gone"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="8dp"
|
||||
android:layout_marginBottom="8dp"/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/progressText"
|
||||
android:text="上传中... 0%"
|
||||
android:visibility="gone"
|
||||
android:textColor="@color/text_secondary"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="16dp"/>
|
||||
|
||||
<Button
|
||||
android:id="@+id/submitBtn"
|
||||
android:text="发布笔记"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="56dp"
|
||||
android:backgroundTint="@color/success"
|
||||
android:textColor="@android:color/white"
|
||||
android:textSize="16sp"
|
||||
android:textStyle="bold"
|
||||
android:elevation="4dp"/>
|
||||
</LinearLayout>
|
||||
</LinearLayout>
|
||||
</ScrollView>
|
||||
@ -0,0 +1,216 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:background="@color/bg_main"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
<LinearLayout
|
||||
android:orientation="vertical"
|
||||
android:padding="24dp"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<!-- 顶部导航栏 -->
|
||||
<LinearLayout
|
||||
android:orientation="horizontal"
|
||||
android:gravity="center_vertical"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="24dp">
|
||||
<TextView
|
||||
android:text="用户中心"
|
||||
android:textStyle="bold"
|
||||
android:textSize="24sp"
|
||||
android:textColor="@color/text_primary"
|
||||
android:layout_width="0dp"
|
||||
android:layout_weight="1"
|
||||
android:layout_height="wrap_content"/>
|
||||
<Button
|
||||
android:id="@+id/backBtn"
|
||||
android:text="返回主页"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="48dp"
|
||||
android:backgroundTint="@color/primary_dark"
|
||||
android:textColor="@android:color/white"
|
||||
android:textSize="14sp"
|
||||
android:paddingLeft="20dp"
|
||||
android:paddingRight="20dp"
|
||||
android:elevation="2dp"/>
|
||||
</LinearLayout>
|
||||
|
||||
<!-- 概览统计卡片 -->
|
||||
<LinearLayout
|
||||
android:orientation="horizontal"
|
||||
android:weightSum="3"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="24dp">
|
||||
<LinearLayout
|
||||
android:orientation="vertical"
|
||||
android:layout_weight="1"
|
||||
android:padding="20dp"
|
||||
android:background="@drawable/stat_card_primary"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:elevation="4dp"
|
||||
android:layout_margin="4dp">
|
||||
<TextView
|
||||
android:text="上传笔记"
|
||||
android:textColor="@android:color/white"
|
||||
android:textSize="12sp"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"/>
|
||||
<TextView
|
||||
android:id="@+id/statUploaded"
|
||||
android:text="0"
|
||||
android:textStyle="bold"
|
||||
android:textSize="20sp"
|
||||
android:textColor="@android:color/white"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"/>
|
||||
</LinearLayout>
|
||||
<Space android:layout_width="12dp" android:layout_height="1dp"/>
|
||||
<LinearLayout
|
||||
android:orientation="vertical"
|
||||
android:layout_weight="1"
|
||||
android:padding="20dp"
|
||||
android:background="@drawable/stat_card_accent"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:elevation="4dp"
|
||||
android:layout_margin="4dp">
|
||||
<TextView
|
||||
android:text="获得点赞"
|
||||
android:textColor="@android:color/white"
|
||||
android:textSize="12sp"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"/>
|
||||
<TextView
|
||||
android:id="@+id/statLikes"
|
||||
android:text="0"
|
||||
android:textStyle="bold"
|
||||
android:textSize="20sp"
|
||||
android:textColor="@android:color/white"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"/>
|
||||
</LinearLayout>
|
||||
<Space android:layout_width="12dp" android:layout_height="1dp"/>
|
||||
<LinearLayout
|
||||
android:orientation="vertical"
|
||||
android:layout_weight="1"
|
||||
android:padding="20dp"
|
||||
android:background="@drawable/stat_card_success"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:elevation="4dp"
|
||||
android:layout_margin="4dp">
|
||||
<TextView
|
||||
android:text="积分"
|
||||
android:textColor="@android:color/white"
|
||||
android:textSize="12sp"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"/>
|
||||
<TextView
|
||||
android:id="@+id/statPoints"
|
||||
android:text="0"
|
||||
android:textStyle="bold"
|
||||
android:textSize="20sp"
|
||||
android:textColor="@android:color/white"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"/>
|
||||
</LinearLayout>
|
||||
</LinearLayout>
|
||||
|
||||
<!-- 个人信息卡片 -->
|
||||
<LinearLayout
|
||||
android:orientation="vertical"
|
||||
android:background="@drawable/card_background"
|
||||
android:padding="24dp"
|
||||
android:layout_marginBottom="24dp"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
<TextView
|
||||
android:text="个人信息"
|
||||
android:textStyle="bold"
|
||||
android:textSize="18sp"
|
||||
android:textColor="@color/text_primary"
|
||||
android:layout_marginBottom="16dp"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"/>
|
||||
|
||||
<TextView
|
||||
android:text="用户名"
|
||||
android:textSize="14sp"
|
||||
android:textColor="@color/text_secondary"
|
||||
android:layout_marginBottom="12dp"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"/>
|
||||
<EditText
|
||||
android:id="@+id/usernameInput"
|
||||
android:hint="用户名"
|
||||
android:background="@drawable/input_background"
|
||||
android:padding="16dp"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="56dp"
|
||||
android:textSize="16sp"
|
||||
android:layout_marginBottom="16dp"/>
|
||||
|
||||
<TextView
|
||||
android:text="专业信息"
|
||||
android:textSize="14sp"
|
||||
android:textColor="@color/text_secondary"
|
||||
android:layout_marginBottom="12dp"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"/>
|
||||
<TextView
|
||||
android:id="@+id/majorNameTv"
|
||||
android:text="专业:未设置"
|
||||
android:textColor="@color/text_primary"
|
||||
android:textSize="16sp"
|
||||
android:background="@drawable/input_background"
|
||||
android:padding="16dp"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="56dp"
|
||||
android:layout_marginBottom="16dp"/>
|
||||
|
||||
<TextView
|
||||
android:text="年级信息"
|
||||
android:textSize="14sp"
|
||||
android:textColor="@color/text_secondary"
|
||||
android:layout_marginBottom="12dp"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"/>
|
||||
<TextView
|
||||
android:id="@+id/gradeNameTv"
|
||||
android:text="年级:未设置"
|
||||
android:textColor="@color/text_primary"
|
||||
android:textSize="16sp"
|
||||
android:background="@drawable/input_background"
|
||||
android:padding="16dp"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="56dp"/>
|
||||
</LinearLayout>
|
||||
|
||||
<!-- 我的笔记卡片 -->
|
||||
<LinearLayout
|
||||
android:orientation="vertical"
|
||||
android:background="@drawable/card_background"
|
||||
android:padding="24dp"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
<TextView
|
||||
android:text="我的笔记"
|
||||
android:textStyle="bold"
|
||||
android:textSize="18sp"
|
||||
android:textColor="@color/text_primary"
|
||||
android:layout_marginBottom="16dp"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"/>
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/myNotesContainer"
|
||||
android:orientation="vertical"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"/>
|
||||
</LinearLayout>
|
||||
</LinearLayout>
|
||||
</ScrollView>
|
||||
@ -0,0 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<background android:drawable="@drawable/ic_launcher_background" />
|
||||
<foreground android:drawable="@drawable/ic_launcher_foreground" />
|
||||
</adaptive-icon>
|
||||
@ -0,0 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<background android:drawable="@drawable/ic_launcher_background" />
|
||||
<foreground android:drawable="@drawable/ic_launcher_foreground" />
|
||||
</adaptive-icon>
|
||||
@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<background android:drawable="@drawable/ic_launcher_background" />
|
||||
<foreground android:drawable="@drawable/ic_launcher_foreground" />
|
||||
<monochrome android:drawable="@drawable/ic_launcher_foreground" />
|
||||
</adaptive-icon>
|
||||
|
After Width: | Height: | Size: 1.4 KiB |
|
After Width: | Height: | Size: 2.8 KiB |
|
After Width: | Height: | Size: 982 B |
|
After Width: | Height: | Size: 1.7 KiB |
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in new issue