实现了AI功能,待完善

This commit is contained in:
zhangsan 2024-05-08 19:14:44 +08:00
parent 5e32755d48
commit 54a8a8304d
136 changed files with 9881 additions and 118 deletions

1
.idea/gradle.xml generated
View File

@ -11,6 +11,7 @@
<set>
<option value="$PROJECT_DIR$" />
<option value="$PROJECT_DIR$/app" />
<option value="$PROJECT_DIR$/hwtxtreaderlib" />
</set>
</option>
</GradleProjectSettings>

View File

@ -9,14 +9,13 @@ android {
defaultConfig {
applicationId "com.zjgsu.jianshu"
minSdk 21
targetSdk 31
minSdk 19
targetSdkVersion 31
versionCode 1
versionName "1.0"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
multiDexEnabled true
}
buildTypes {
release {
minifyEnabled false
@ -57,4 +56,6 @@ dependencies {
implementation 'junit:junit:4.12'
implementation 'com.jakewharton:butterknife:7.0.1'
implementation 'com.facebook.fresco:fresco:0.9.0+'
implementation project(':hwtxtreaderlib')
implementation 'androidx.multidex:multidex:2.0.1'
}

View File

@ -12,13 +12,9 @@
<uses-permission android:name="android.permission.CHANGE_NETWORK_STATE" />
<uses-permission android:name="android.permission.WAKE_LOCK" /> <!-- 设备休眠 -->
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> <!-- sd卡存储 -->
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.CAMERA" /> <!-- 摄像头 -->
<uses-permission android:name="android.permission.RECORD_AUDIO" /> <!-- 录音 -->
<uses-permission android:name="android.permission.VIBRATE" />
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
@ -28,18 +24,18 @@
android:supportsRtl="true"
android:theme="@style/Theme.Jianshu"
android:usesCleartextTraffic="true">
<activity android:name=".GoodBookActivity"/>
<activity android:name=".HotBookActivity"/>
<activity android:name=".GoodBookActivity" />
<activity android:name=".HotBookActivity" />
<activity android:name=".BookInformationActivity" />
<activity android:name=".PerceptionActivity" />
<activity android:name=".FindpwdActivity"/>
<activity android:name=".LiteratureActivity"/>
<activity android:name=".PsychologyActivity"/>
<activity android:name=".ManagementActivity"/>
<activity android:name=".PhilosophyActivity"/>
<activity android:name=".FindpwdActivity" />
<activity android:name=".LiteratureActivity" />
<activity android:name=".PsychologyActivity" />
<activity android:name=".ManagementActivity" />
<activity android:name=".PhilosophyActivity" />
<!-- <activity android:name=".BookShelfActivity"/>-->
<activity android:name=".ScienceActivity"/>
<activity android:name=".Author_introductionActivity"/>
<activity android:name=".ScienceActivity" />
<activity android:name=".Author_introductionActivity" />
<!-- <activity android:name=".MainActivity"-->
<!-- android:exported="true"-->
<!-- android:label="@string/app_name"-->
@ -50,23 +46,23 @@
<!-- <category android:name="android.intent.category.LAUNCHER" />-->
<!-- </intent-filter>-->
<!-- </activity>-->
<activity android:name=".SendSpecificActivity"></activity>
<activity android:name=".BookIntroActivity"/>
<activity android:name=".SendSpecificActivity" />
<activity android:name=".BookIntroActivity" />
<activity android:name=".MainActivity"
android:configChanges="orientation|keyboard|keyboardHidden|navigation" />
<activity android:name=".RegisterActivity" />
<activity android:name=".BookShelfActivity"/>
<activity android:name=".Activity.Perception_informationActivity"/>
<activity android:name=".Activity.sendPerceptionActivity"/>
<activity android:name=".MyActivity"/>
<activity android:name=".Activity.MyPostActivity"/>
<activity android:name=".ReEmailActivity"/>
<activity android:name=".RePhoneActivity"/>
<activity android:name=".ReMyinfoActivity"/>
<activity android:name=".ReNameActivity"/>
<activity android:name=".ReSexActivity"/>
<activity android:name=".ReSignatureActivity"/>
<activity android:name=".Activity.AccountManagerActivity"/>
<activity android:name=".BookShelfActivity" />
<activity android:name=".Activity.Perception_informationActivity" />
<activity android:name=".Activity.sendPerceptionActivity" />
<activity android:name=".MyActivity" />
<activity android:name=".Activity.MyPostActivity" />
<activity android:name=".ReEmailActivity" />
<activity android:name=".RePhoneActivity" />
<activity android:name=".ReMyinfoActivity" />
<activity android:name=".ReNameActivity" />
<activity android:name=".ReSexActivity" />
<activity android:name=".ReSignatureActivity" />
<activity android:name=".Activity.AccountManagerActivity" />
<activity android:name=".ReAddressActivity"/>
<activity
android:name=".LoginActivity"
@ -77,6 +73,16 @@
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity
android:name="com.bifan.txtreaderlib.ui.HwTxtPlayActivity"
android:exported="true"
android:theme="@style/HwTxtFullScreen">
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<data android:mimeType="text/plain" />
</intent-filter>
</activity>
<provider
android:name="cn.bmob.v3.util.BmobContentProvider"
android:authorities="com.zjgsu.jianshu.BmobContentProvider">

View File

@ -18,14 +18,21 @@ import androidx.appcompat.app.AppCompatActivity
import androidx.core.content.ContextCompat
import androidx.recyclerview.widget.StaggeredGridLayoutManager
import cn.bmob.v3.listener.SaveListener
import com.bifan.txtreaderlib.ui.HwTxtPlayActivity
import com.bumptech.glide.Glide
import com.zjgsu.jianshu.Adapter.GoodperceptionAdapter
import com.zjgsu.jianshu.Bmob.BookShelf
import com.zjgsu.jianshu.Bmob.Book_info_bmob
import com.zjgsu.jianshu.Bmob.Bookcontent_bmob
import com.zjgsu.jianshu.Bmob.Perception_bmob
import kotlinx.android.synthetic.main.activity_book_info.*
import kotlinx.android.synthetic.main.activity_sendspecific.*
import kotlinx.android.synthetic.main.bookinfo_title.*
import okhttp3.*
import java.io.File
import java.io.FileOutputStream
import java.io.IOException
import java.io.InputStream
class BookInformationActivity : AppCompatActivity() {
@ -38,6 +45,7 @@ class BookInformationActivity : AppCompatActivity() {
private lateinit var picurl:String
private var sourceActivityClass: Class<*>? = null
private lateinit var bookInfoTextView: TextView
private lateinit var bookPath:String
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_book_info)
@ -102,7 +110,6 @@ class BookInformationActivity : AppCompatActivity() {
val str: String = String.format("%.2f", tuijian_res)
textView10.text = "简书推荐值 " + str + "%"
}
updateBookRankings(book)
bookinfo_bookname.text = book.name
bookinfo_authorname.text = book.author_name
bookinfo_introduce.text=book.introduce
@ -181,9 +188,8 @@ class BookInformationActivity : AppCompatActivity() {
showBookIntroDialog(bookInfoTextView.text.toString())
}
startToRead.setOnClickListener {
val intent = Intent(this, BookIntroActivity::class.java)
intent.putExtra("Book_name", bookName)
startActivity(intent)
downloadFile(bookName)
}
}
//TODO:以后可以考虑再次点击这个“已加入的按钮”后,弹出一个对话框:是否将该书移除,然后将该书从书架中移除。而不是弹:该书已在您的书架中
@ -268,21 +274,6 @@ class BookInformationActivity : AppCompatActivity() {
// }
// }
private fun updateBookRankings(book: Book_info_bmob, isRecommended: Boolean = false) {
if (isRecommended) {
book.settuijian(book.tuijian + 1)
} else {
book.setyiban(book.yiban + 1)
book.setbuxing(book.buxing + 1)
}
book.update(book.objectId, object : UpdateListener() {
override fun done(e: BmobException?) {
if (e != null) {
Log.e("BookInfo", "Error updating book rankings: ${e.message}")
}
}
})
}
private fun judgeComment(){
val query=BmobQuery<Perception_bmob>()
query.addWhereEqualTo("userid",userId)
@ -313,4 +304,59 @@ class BookInformationActivity : AppCompatActivity() {
.error(R.drawable.fail_load) // 设置加载失败时显示的图片
.into(bookinfo_img) // 将图片加载到指定的 ImageView 中
}
private fun downloadFile(bookName: String) {
val localPath = getExternalFilesDir(null)?.absolutePath + "/$bookName.txt"
val file = File(localPath)
// 检查本地是否已经存在该书籍的 TXT 文件
if (file.exists()) {
// 如果存在,则直接打开该文件
runOnUiThread {
HwTxtPlayActivity.loadTxtFile(this@BookInformationActivity, file.absolutePath)
}
} else {
// 如果不存在,则从网上下载
val querybook = BmobQuery<Bookcontent_bmob>()
querybook.addWhereEqualTo("bookname", bookName)
querybook.findObjects(object : FindListener<Bookcontent_bmob>() {
override fun done(books: MutableList<Bookcontent_bmob>?, e: BmobException?) {
if (e == null && books != null && books.isNotEmpty()) {
val book = books[0]
bookPath = book.bookpath.url
downloadFromUrl(bookPath, localPath)
} else {
Log.e("BookInfo", "Error loading book info: ${e?.message}")
}
}
})
}
}
private fun downloadFromUrl(bookPath: String, localPath: String) {
val client = OkHttpClient()
val request = Request.Builder().url(bookPath).build()
client.newCall(request).enqueue(object : Callback {
override fun onFailure(call: Call, e: IOException) {
Log.e("Download", "Download failed: ${e.message}")
runOnUiThread {
Toast.makeText(this@BookInformationActivity, "Download failed", Toast.LENGTH_SHORT).show()
}
}
override fun onResponse(call: Call, response: Response) {
response.body?.let { body ->
val inputStream: InputStream = body.byteStream()
val file = File(localPath)
val outputStream = FileOutputStream(file)
inputStream.use { input ->
outputStream.use { output ->
input.copyTo(output)
runOnUiThread {
HwTxtPlayActivity.loadTxtFile(this@BookInformationActivity, file.absolutePath)
}
}
}
}
}
})
}
}

View File

@ -86,9 +86,6 @@ class BookShelfActivity : AppCompatActivity() {
bookcity.setOnClickListener {
NavigationHelper.navigateTo(this, MainActivity::class.java)
}
bookshelf.setOnClickListener {
NavigationHelper.navigateTo(this, BookShelfActivity::class.java)
}
reading_comprehension.setOnClickListener {
NavigationHelper.navigateTo(this, PerceptionActivity::class.java)
}

View File

@ -107,9 +107,6 @@ class MyActivity : AppCompatActivity() {
reading_comprehension.setOnClickListener {
NavigationHelper.navigateTo(this, PerceptionActivity::class.java)
}
homepage.setOnClickListener {
NavigationHelper.navigateTo(this, MyActivity::class.java)
}
}
}

View File

@ -67,9 +67,6 @@ class PerceptionActivity : AppCompatActivity() {
bookshelf.setOnClickListener {
NavigationHelper.navigateTo(this, BookShelfActivity::class.java)
}
reading_comprehension.setOnClickListener {
NavigationHelper.navigateTo(this, PerceptionActivity::class.java)
}
homepage.setOnClickListener {
NavigationHelper.navigateTo(this, MyActivity::class.java)
}

View File

@ -42,7 +42,6 @@ class BillboardAdapter(var listOfbookRankList: List<List<Book_rank>>) : Recycler
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
val bookRankList = listOfbookRankList[position]
Log.d("BillboardAdapter", "Size of bookRankList at position $position")
if(position==0) {
holder.bookRank_title.text="热门图书榜"
holder.bookRank_decr1.text = "在读人数:"+bookRankList[0].courtOfReaders.toString()

View File

@ -71,8 +71,6 @@ class PerceptionAdapter(val perceptionList: List<Perception>) :
for (i in list) {
if (p.userid.equals(i.objectId)) {
holder.perception_posetPerson.text = i.nickName
// flag = true //找到
Log.d("perception", "find sucessflly")
//加载图片
Glide.with(holder.itemView.context)
.load(i.face.url) // 确保你的Book对象有正确的图片URL
@ -110,55 +108,6 @@ class PerceptionAdapter(val perceptionList: List<Perception>) :
}
}
})
//加载书籍封面
// query.findObjects(object : FindListener<Perception_bmob>() {
// override fun done(p0: MutableList<Perception_bmob>?, p1: BmobException?) {
// if (p1 == null) {
// if (p0 != null && p0.size > 0) {
// for (p in p0) {
// Log.d("myLog", p.b_name)
// bookName=p.b_name
// holder.perceptionList_bookName.text=bookName
// val queryBook = BmobQuery<Book_info_bmob>()
// queryBook.findObjects(object : FindListener<Book_info_bmob>() {
// override fun done(list: List<Book_info_bmob>, e: BmobException?) {
// if (e == null) {
// for (i in list) {
// if (p.b_name.equals(i.name)) {
// holder.perceptionList_bookAuthor.text=i.author_name
// //加载图片
// object : Thread() {
// override fun run() {
// try {
// val url = URL(i!!.picture?.url)
// val connection: HttpURLConnection =
// url.openConnection() as HttpURLConnection
// connection.setRequestMethod("GET")
// connection.setConnectTimeout(3000)
// val `in`: InputStream = connection.getInputStream()
// val bitmap: Bitmap =
// BitmapFactory.decodeStream(`in`)
// holder.perceptionList_bookPic.setImageBitmap(bitmap)
// } catch (e: Exception) {
// e.printStackTrace()
// }
// }
// }.start()
//
//
// }
// }
// } else {
// Log.d("myLog", "error")
// }
// }
// })
// }
// }
// }
// }
// })
holder.perception_bookInf.setOnClickListener() {
val intent = Intent(holder.itemView.context, BookInformationActivity::class.java)
intent.putExtra("Book_name", bookName)

View File

@ -0,0 +1,9 @@
package com.zjgsu.jianshu.Bmob
import cn.bmob.v3.BmobObject
import cn.bmob.v3.datatype.BmobFile
class Bookcontent_bmob:BmobObject() {
val bookpath:BmobFile=BmobFile()
val bookname:String=""
}

View File

@ -0,0 +1,50 @@
package com.zjgsu.jianshu.Fragment
import android.graphics.Typeface
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.TextView
import androidx.fragment.app.Fragment
import com.zjgsu.jianshu.R
class BookPageFragment : Fragment() {
private var bookContent: String? = null
private var pageIndex: Int = 0
companion object {
private const val ARG_BOOK_CONTENT = "book_content"
private const val ARG_PAGE_INDEX = "page_index"
fun newInstance(bookContent: String, pageIndex: Int): BookPageFragment {
return BookPageFragment().apply {
arguments = Bundle().apply {
putString(ARG_BOOK_CONTENT, bookContent)
putInt(ARG_PAGE_INDEX, pageIndex)
}
}
}
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
arguments?.let {
bookContent = it.getString(ARG_BOOK_CONTENT)
pageIndex = it.getInt(ARG_PAGE_INDEX)
}
}
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
val view = inflater.inflate(R.layout.fragment_content, container, false)
val textViewContent: TextView = view.findViewById(R.id.textViewContent)
// 计算当前页的内容
val startIndex = pageIndex * (10 * 100)
val endIndex = minOf((pageIndex + 1) * (10 * 100), bookContent?.length ?: 0)
val currentPageContent = bookContent?.substring(startIndex, endIndex) ?: ""
textViewContent.text = currentPageContent
return view
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 311 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 585 B

View File

@ -29,8 +29,10 @@
<!-- SwipeRefreshLayout 包裹 NestedScrollView -->
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/constraintLayout"
android:layout_width="match_parent"
android:layout_height="match_parent">
<androidx.swiperefreshlayout.widget.SwipeRefreshLayout
android:id="@+id/swipeRefresh"
android:layout_width="match_parent"
@ -58,7 +60,7 @@
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/horizontal_recycler_view"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
android:layout_height="match_parent" />
<TextView
android:layout_width="match_parent"
@ -83,14 +85,15 @@
<!-- 底部导航栏布局 -->
<include
android:id="@+id/layout_bottom_navigation"
layout="@layout/layout_bottom_navigation"
android:layout_width="match_parent"
android:id="@+id/layout_bottom_navigation"
android:layout_width="0dp"
android:layout_height="wrap_content"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" />
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
</androidx.coordinatorlayout.widget.CoordinatorLayout>
<!-- 左侧导航栏 -->

View File

@ -0,0 +1,17 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_marginTop="20dp"
android:layout_marginBottom="20dp"
android:layout_height="match_parent">
<TextView
android:id="@+id/textViewContent"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:textSize="16sp"
android:lineSpacingExtra="4dp"
android:padding="16dp"
android:scrollbars="vertical"
android:textIsSelectable="true" />
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@ -0,0 +1,2 @@
<?xml version="1.0" encoding="utf-8"?>
<resources></resources>

View File

@ -3,6 +3,7 @@ buildscript {
repositories {
google()
mavenCentral()
maven { url 'https://jitpack.io' }
}
dependencies {
classpath "com.android.tools.build:gradle:7.0.2"
@ -12,8 +13,6 @@ buildscript {
}
}
task clean(type: Delete) {
delete rootProject.buildDir
}

View File

@ -4,3 +4,4 @@ distributionUrl=https\://services.gradle.org/distributions/gradle-7.0.2-bin.zip
distributionPath=wrapper/dists
zipStorePath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME

1
hwtxtreaderlib/.gitignore vendored Normal file
View File

@ -0,0 +1 @@
/build

View File

@ -0,0 +1,33 @@
apply plugin: 'com.android.library'
android {
compileSdkVersion 31
defaultConfig {
minSdkVersion 15
targetSdkVersion 30
versionCode 6
versionName "4.5"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
}
dependencies {
implementation 'androidx.appcompat:appcompat:1.2.0'
implementation files('libs/chardet.jar')
implementation 'com.google.android.material:material:1.3.0'
implementation 'io.github.bmob:android-sdk:3.9.4'
implementation 'io.reactivex.rxjava2:rxjava:2.2.8'
implementation 'io.reactivex.rxjava2:rxandroid:2.1.1'
implementation 'com.squareup.okhttp3:okhttp:4.8.1'
implementation 'com.squareup.okio:okio:2.2.2'
implementation 'com.google.code.gson:gson:2.8.5'
implementation 'com.squareup.retrofit2:retrofit:2.9.0'
implementation 'com.squareup.retrofit2:converter-gson:2.9.0'
}

Binary file not shown.

25
hwtxtreaderlib/proguard-rules.pro vendored Normal file
View File

@ -0,0 +1,25 @@
# Add project specific ProGuard rules here.
# By default, the flags in this file are appended to flags specified
# in D:\developtool\sdk\sdk/tools/proguard/proguard-android.txt
# You can edit the include path and order by changing the proguardFiles
# directive in build.gradle.
#
# For more details, see
# http://developer.android.com/guide/developing/tools/proguard.html
# Add any project specific keep options here:
# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
# public *;
#}
# Uncomment this to preserve the line number information for
# debugging stack traces.
#-keepattributes SourceFile,LineNumberTable
# If you keep the line number information, uncomment this to
# hide the original source file name.
#-renamesourcefileattribute SourceFile

View File

@ -0,0 +1,24 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.bifan.txtreaderlib">
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<application
android:allowBackup="true"
android:label="@string/app_name"
android:supportsRtl="true">
<activity android:name=".ui.ChatWithGptActivity"></activity>
<activity
android:name="com.bifan.txtreaderlib.ui.HwTxtPlayActivity"
android:theme="@style/HwTxtFullScreen">
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<data android:mimeType="text/plain" />
</intent-filter>
</activity>
</application>
</manifest>

View File

@ -0,0 +1 @@
auth_key=sk-No44eOPA2V17ZQmuD897C61b905743B9Aa2d83B6EeF2C38a

Binary file not shown.

View File

@ -0,0 +1,69 @@
package com.bifan.txtreaderlib.adapter;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;
import com.bifan.txtreaderlib.R;
import com.bifan.txtreaderlib.bean.Message;
import java.util.List;
public class MessageAdapter extends RecyclerView.Adapter<MessageAdapter.MessageViewHolder> {
private List<Message> messages;
public MessageAdapter(List<Message> messages) {
this.messages = messages;
}
@Override
public int getItemViewType(int position) {
Message message = messages.get(position);
return message.getIsuser();
}
@NonNull
@Override
public MessageViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
LayoutInflater inflater = LayoutInflater.from(parent.getContext());
View view;
if (viewType == 1) {
view = inflater.inflate(R.layout.item_message_user, parent, false);
} else {
view = inflater.inflate(R.layout.item_message_bot, parent, false);
}
return new MessageViewHolder(view, viewType);
}
@Override
public void onBindViewHolder(@NonNull MessageViewHolder holder, int position) {
Message message = messages.get(position);
if (message.getIsuser() == 1) {
holder.textViewMessage.setText(message.getContent());
} else {
holder.textViewMessage.setText(message.getContent());
}
}
@Override
public int getItemCount() {
return messages.size();
}
static class MessageViewHolder extends RecyclerView.ViewHolder {
TextView textViewMessage;
MessageViewHolder(View itemView, int viewType) {
super(itemView);
if (viewType == 1) { // 假设 1 是用户消息
textViewMessage = itemView.findViewById(R.id.textViewMessageUser);
} else { // 1 即机器人消息
textViewMessage = itemView.findViewById(R.id.textViewMessageBot);
}
}
}
}

View File

@ -0,0 +1,99 @@
package com.bifan.txtreaderlib.bean;
import com.bifan.txtreaderlib.interfaces.IChapter;
/**
* @description
* @author bifan-wei
* @time 2021/11/13 16:05
*/
public class Chapter implements IChapter {
public String Title;
public int Index;//章节位置
public int StartParagraphIndex;
public int EndParagraphIndex;
public int StartCharIndex;//字符开始位置
public int EndCharIndex;//字符结束位置
public int StartIndex;//第一个字符在全文字符的位置
public Chapter(String title, int startParagraphIndex, int endParagraphIndex) {
Title = title;
StartParagraphIndex = startParagraphIndex;
EndParagraphIndex = endParagraphIndex;
}
public Chapter(int startIndex, int index, String title, int startParagraphIndex, int endParagraphIndex, int startCharIndex, int endCharIndex) {
StartIndex = startIndex;
Title = title;
Index = index;
StartParagraphIndex = startParagraphIndex;
EndParagraphIndex = endParagraphIndex;
StartCharIndex = startCharIndex;
EndCharIndex = endCharIndex;
}
public Chapter() {
}
public void setTitle(String title) {
Title = title;
}
@Override
public int getStartIndex() {
return StartIndex;
}
@Override
public int getStartCharIndex() {
return StartCharIndex;
}
@Override
public int getEndCharIndex() {
return EndCharIndex;
}
@Override
public int getStartParagraphIndex() {
return StartParagraphIndex;
}
@Override
public int getEndParagraphIndex() {
return EndParagraphIndex;
}
@Override
public String getTitle() {
return Title;
}
@Override
public void setStartParagraphIndex(int index) {
StartParagraphIndex = index;
}
@Override
public void setEndParagraphIndex(int index) {
EndParagraphIndex = index;
}
@Override
public int getIndex() {
return Index;
}
@Override
public String toString() {
return "Chapter{" +
"Title='" + Title + '\'' +
", Index=" + Index +
", StartParagraphIndex=" + StartParagraphIndex +
", EndParagraphIndex=" + EndParagraphIndex +
", StartCharIndex=" + StartCharIndex +
", EndCharIndex=" + EndCharIndex +
'}';
}
}

View File

@ -0,0 +1,49 @@
package com.bifan.txtreaderlib.bean;
import cn.bmob.v3.BmobObject;
public class ChatMessage_bmob extends BmobObject {
private String content;
private Integer isuser;
private String relatedBook;
private String userid;
public String getContent() {
return content;
}
public void setContent(String content) {
this.content = content;
}
public Integer getIsuser() {
return isuser;
}
public void setIsuser(Integer isuser) {
this.isuser = isuser;
}
public String getRelatedBook() {
return relatedBook;
}
public void setRelatedBook(String relatedBook) {
this.relatedBook = relatedBook;
}
public String getUserid() {
return userid;
}
public void setUserid(String userid) {
this.userid = userid;
}
public ChatMessage_bmob(String content, Integer isuser, String relatedBook, String userid) {
this.content = content;
this.isuser = isuser;
this.relatedBook = relatedBook;
this.userid = userid;
}
}

View File

@ -0,0 +1,71 @@
package com.bifan.txtreaderlib.bean;
import java.util.List;
//curl https://api.openai.com/v1/chat/completions \
// -H "Content-Type: application/json" \
// -H "Authorization: Bearer $OPENAI_API_KEY" \
// -d '{
// "model": "gpt-3.5-turbo",
// "messages": [
// {
// "role": "system",
// "content": "You are a helpful assistant."
// },
// {
// "role": "user",
// "content": "Hello!"
// }
// ]
// }'
public class ChatRequest {
private String model;
private List<Message> messages;
private Integer maxTokens;
private Double temperature;
public ChatRequest(List<Message> messages) {
this.messages = messages;
this.model = "gpt-3.5-turbo"; // 默认模型
this.maxTokens = 150; // 默认的最大 token
this.temperature = 0.7;
}
public void setMaxTokens(Integer maxTokens) {
this.maxTokens = maxTokens;
}
public Integer getMaxTokens() {
return maxTokens;
}
public void setTemperature(Double temperature) {
this.temperature = temperature;
}
public Double getTemperature() {
return temperature;
}
public void setModel(String model) {
this.model = model;
}
public List<Message> getMessages() {
return messages;
}
public String getModel() {
return model;
}
// Message
public static class Message {
String role;
String content;
public Message(String role, String content) {
this.role = role;
this.content = content;
}
}
}

View File

@ -0,0 +1,47 @@
package com.bifan.txtreaderlib.bean;
import java.util.List;
//{
// "id": "chatcmpl-123",
// "object": "chat.completion",
// "created": 1677652288,
// "model": "gpt-3.5-turbo-0125",
// "system_fingerprint": "fp_44709d6fcb",
// "choices": [{
// "index": 0,
// "message": {
// "role": "assistant",
// "content": "\n\nHello there, how may I assist you today?",
// },
// "logprobs": null,
// "finish_reason": "stop"
// }],
// "usage": {
// "prompt_tokens": 9,
// "completion_tokens": 12,
// "total_tokens": 21
// }
// }
public class ChatResponse {
String id;
String object;
long created;
String model;
public List<Choice> choices;
public static class Choice {
int index;
public Message message;
// 你可以根据需要继续添加其他字段如 logprobs, finish_reason
}
public static class Message {
String role;
public String content;
}
// 假设你已经有 getter 方法或者你可以自行添加
}

View File

@ -0,0 +1,37 @@
package com.bifan.txtreaderlib.bean;
import android.graphics.Path;
import android.graphics.Rect;
import android.graphics.RectF;
/**
* created by bifan-wei
*/
public class DefaultLeftSlider extends Slider{
@Override
public float getX(float dx) {
return Right + dx +5;
}
@Override
public float getY(float dy) {
return Top + dy - 5;
}
@Override
public Path getPath(TxtChar txtChar,Path path) {
if (txtChar != null) {
Path p = path;
p.reset();
p.moveTo(txtChar.Left, txtChar.Bottom);
p.lineTo(txtChar.Left, txtChar.Bottom + SliderWidth);
Rect rect = new Rect(txtChar.Left - SliderWidth * 2, txtChar.Bottom, txtChar.Left, txtChar.Bottom + SliderWidth * 2);
p.addArc(new RectF(rect), 0, 270);
p.lineTo(txtChar.Left, txtChar.Bottom);
return p;
} else {
return null;
}
}
}

View File

@ -0,0 +1,37 @@
package com.bifan.txtreaderlib.bean;
import android.graphics.Path;
import android.graphics.Rect;
import android.graphics.RectF;
/**
* created by bifan-wei
*/
public class DefaultRightSlider extends Slider{
@Override
public float getX(float dx) {
return Left + dx - 5;
}
@Override
public float getY(float dy) {
return Top + dy - 5;
}
@Override
public Path getPath(TxtChar txtChar,Path path) {
if (txtChar != null) {
Path p = path;
p.reset();
p.moveTo(txtChar.Right, txtChar.Bottom + SliderWidth);
p.lineTo(txtChar.Right, txtChar.Bottom);
p.lineTo(txtChar.Right + SliderWidth, txtChar.Bottom);
Rect rect = new Rect(txtChar.Right, txtChar.Bottom, txtChar.Right + SliderWidth * 2, txtChar.Bottom + SliderWidth * 2);
p.addArc(new RectF(rect), -90, 270);
return p;
} else {
return null;
}
}
}

View File

@ -0,0 +1,23 @@
package com.bifan.txtreaderlib.bean;
import android.graphics.Color;
/*
* create by bifan-wei
* 2017-11-13
*/
public class EnChar extends TxtChar {
public static int DefaultTextColor = Color.parseColor("#45a1cd");
public EnChar(char aChar) {
super(aChar);
}
@Override
public int getTextColor() {
return DefaultTextColor;
}
@Override
public int getCharType() {
return Char_En;
}
}

View File

@ -0,0 +1,26 @@
package com.bifan.txtreaderlib.bean;
/**
* created by bifan-wei
*/
public class FileReadRecordBean {
public String fileHashName;
public int id;
public String fileName;
public String filePath;
public int paragraphIndex;
public int chartIndex;
@Override
public String toString() {
return "FileReadRecordBean{" +
"fileHashName='" + fileHashName + '\'' +
", id=" + id +
", fileName='" + fileName + '\'' +
", filePath='" + filePath + '\'' +
", paragraphIndex='" + paragraphIndex + '\'' +
", chartIndex='" + chartIndex + '\'' +
'}';
}
}

View File

@ -0,0 +1,27 @@
package com.bifan.txtreaderlib.bean;
public class Message {
private String content;
private Integer isuser;
public String getContent() {
return content;
}
public void setContent(String content) {
this.content = content;
}
public Integer getIsuser() {
return isuser;
}
public void setIsuser(Integer isuser) {
this.isuser = isuser;
}
public Message(String content, Integer isuser) {
this.content = content;
this.isuser = isuser;
}
}

View File

@ -0,0 +1,47 @@
package com.bifan.txtreaderlib.bean;
import android.graphics.Path;
import android.graphics.Rect;
import android.graphics.RectF;
/**
* created by bifan-wei
*/
public class MuiLeftSlider extends Slider {
@Override
public float getX(float dx) {
return Right + dx - 5;
}
@Override
public float getY(float dy) {
return Top + dy - 5;
}
@Override
public Path getPath(TxtChar txtChar, Path path) {
if (txtChar != null) {
int r = SliderWidth;
int leftWidth = (int) (Math.cos(30) * r);
int heightWidth = r * 3 / 2;
Path p = path;
p.reset();
p.moveTo(txtChar.Left, txtChar.Top);
p.lineTo(txtChar.Left - leftWidth, txtChar.Top - heightWidth);
Rect rect = new Rect(
txtChar.Left - r,
txtChar.Top - 3 * r,
txtChar.Left + r,
txtChar.Top - r);
p.addArc(new RectF(rect), 150, 240);
p.lineTo(txtChar.Left, txtChar.Top);
return p;
} else {
return null;
}
}
}

View File

@ -0,0 +1,46 @@
package com.bifan.txtreaderlib.bean;
import android.graphics.Path;
import android.graphics.Rect;
import android.graphics.RectF;
/**
* created by bifan-wei
*/
public class MuiRightSlider extends Slider {
@Override
public float getX(float dx) {
return Left + dx + 5;
}
@Override
public float getY(float dy) {
return Bottom + dy + 5;
}
@Override
public Path getPath(TxtChar txtChar, Path path) {
if (txtChar != null) {
int r = SliderWidth;
int leftWidth = (int) (Math.cos(30) * r);
int heightWidth = r * 3 / 2;
Path p = path;
p.reset();
p.moveTo(txtChar.Right, txtChar.Bottom);
p.lineTo(txtChar.Right + leftWidth, txtChar.Bottom + heightWidth);
Rect rect = new Rect(
txtChar.Right - r,
txtChar.Bottom + r,
txtChar.Right + r,
txtChar.Bottom +3*r);
p.addArc(new RectF(rect), -30, 240);
p.lineTo(txtChar.Right, txtChar.Bottom);
return p;
} else {
return null;
}
}
}

View File

@ -0,0 +1,23 @@
package com.bifan.txtreaderlib.bean;
import android.graphics.Color;
/*
* create by bifan-wei
* 2017-11-13
*/
public class NumChar extends TxtChar {
public static int DefaultTextColor = Color.parseColor("#45a1cf");
public NumChar(char aChar) {
super(aChar);
}
@Override
public int getTextColor() {
return DefaultTextColor;
}
@Override
public int getCharType() {
return Char_Num;
}
}

View File

@ -0,0 +1,236 @@
package com.bifan.txtreaderlib.bean;
import com.bifan.txtreaderlib.interfaces.ICursor;
import com.bifan.txtreaderlib.interfaces.IPage;
import com.bifan.txtreaderlib.interfaces.ITxtLine;
import java.util.ArrayList;
import java.util.List;
/*
* create by bifan-wei
* 2017-11-13
*/
public class Page implements IPage, ICursor<ITxtLine> {
private int CurrentIndex;
private List<ITxtLine> lines = null;
private boolean fullPage = false;
@Override
public int CurrentIndex() {
return CurrentIndex;
}
@Override
public void addLineTo(ITxtLine line, int index) {
if (line == null) {
throw new NullPointerException("line == null on addLine form Page");
}
if (lines == null) {
lines = new ArrayList<>();
}
lines.add(index, line);
}
@Override
public void addLine(ITxtLine line) {
if (line == null) {
throw new NullPointerException("line == null on addLine form Page");
}
if (lines == null) {
lines = new ArrayList<>();
}
lines.add(line);
}
@Override
public void setLines(List<ITxtLine> lines) {
this.lines = lines;
}
@Override
public int getCount() {
return lines == null ? 0 : lines.size();
}
@Override
public void moveToPosition(int index) {
if (HasData()) {
if (index < 0 || index >= getCount()) {
throw new ArrayIndexOutOfBoundsException
(" moveToPosition index OutOfBoundsException from page");
}
CurrentIndex = index;
Current();
}
}
@Override
public TxtChar getFirstChar() {
ITxtLine firstLine = getFirstLine();
if (!firstLine.HasData()) {
return null;
} else {
return firstLine.getFirstChar();
}
}
@Override
public void moveToFirst() {
moveToPosition(0);
}
@Override
public TxtChar getLastChar() {
ITxtLine lastLine = getLastLine();
if (!lastLine.HasData()) {
return null;
} else {
return lastLine.getLastChar();
}
}
@Override
public void moveToLast() {
CurrentIndex = getCount() - 1;
if (CurrentIndex < 0) {
CurrentIndex = 0;
}
moveToPosition(CurrentIndex);
}
@Override
public ITxtLine getFirstLine() {
moveToFirst();
return Current();
}
@Override
public void moveToNext() {
CurrentIndex++;
if (CurrentIndex >= getCount()) {
CurrentIndex = getCount() - 1;
}
if (CurrentIndex < 0) {
CurrentIndex = 0;
}
moveToPosition(CurrentIndex);
}
@Override
public ITxtLine getLastLine() {
moveToLast();
return Current();
}
@Override
public void moveToPrevious() {
CurrentIndex--;
if (CurrentIndex < 0) {
CurrentIndex = 0;
}
moveToPosition(CurrentIndex);
}
@Override
public boolean isFirst() {
return CurrentIndex == 0;
}
@Override
public ICursor<ITxtLine> getLineCursor() {
return this;
}
@Override
public boolean isLast() {
return CurrentIndex == getCount() - 1;
}
@Override
public boolean isFullPage() {
return fullPage;
}
@Override
public void setFullPage(boolean fullPage) {
this.fullPage = fullPage;
}
@Override
public int getLineNum() {
return getCount();
}
private Boolean AfterLast = false;
private Boolean BeforeFirst = false;
@Override
public boolean isBeforeFirst() {
return BeforeFirst;
}
@Override
public boolean isAfterLast() {
return AfterLast;
}
@Override
public Boolean HasData() {
return getCount() > 0;
}
@Override
public ITxtLine Pre() {
CurrentIndex--;
if (CurrentIndex < 0) {
CurrentIndex = 0;
}
moveToPosition(CurrentIndex);
return Current();
}
@Override
public ITxtLine Next() {
CurrentIndex++;
if (CurrentIndex >= getCount()) {
CurrentIndex = getCount() - 1;
}
if (CurrentIndex < 0) {
CurrentIndex = 0;
}
moveToPosition(CurrentIndex);
return Current();
}
@Override
public ITxtLine Current() {
AfterLast = isLast();
BeforeFirst = isFirst();
return lines == null ? null : getLine(CurrentIndex);
}
@Override
public ITxtLine getLine(int index) {
return lines == null ? null : lines.get(index);
}
@Override
public String toString() {
String str = "";
if (HasData()) {
for (ITxtLine l : lines) {
str = str + l.getLineStr() + "\r\n";
}
}
return str;
}
@Override
public List<ITxtLine> getLines() {
return lines;
}
}

View File

@ -0,0 +1,31 @@
package com.bifan.txtreaderlib.bean;
import android.graphics.Path;
/**
* Created by bifan-wei
* on 2017/12/5.
*/
public abstract class Slider {
public Boolean ShowBellow= true;
public int Left;
public int Right;
public int Top;
public int Bottom;
public int SliderWidth;
public abstract float getX(float dx);//手势判断位置x坐标
public abstract float getY(float dy);//手势判断位置y坐标
public abstract Path getPath(TxtChar txtChar,Path path);//获取当前滑动条Path
@Override
public String toString() {
return "Slider{" +
"ShowBellow=" + ShowBellow +
", Left=" + Left +
", Right=" + Right +
", Top=" + Top +
", Bottom=" + Bottom +
'}';
}
}

View File

@ -0,0 +1,72 @@
package com.bifan.txtreaderlib.bean;
import android.graphics.Color;
/*
* create by bifan-wei
* 2017-11-13
*/
public class TxtChar {
public static final int Char_text = 0x01;
public static final int Char_Num = 0x02;
public static final int Char_En = 0x03;
public char Char;
public int ParagraphIndex;
public float CharWidth = 0;
public int CharIndex;
public int TextColor = Color.BLACK;
public int PositionX;
public int PositionY;
public int Left;
public int Right;
public int Bottom;
public int Top;
public TxtChar(char aChar) {
Char = aChar;
}
public int getTextColor() {
return TextColor;
}
public int getCharType() {
return Char_text;
}
public char getValue() {
return Char;
}
public String getValueStr() {
return String.valueOf(getValue());
}
@Override
public String toString() {
return "TxtChar{" +
"Char=" + Char +
", ParagraphIndex=" + ParagraphIndex +
", CharWidth=" + CharWidth +
", CharIndex=" + CharIndex +
", TextColor=" + TextColor +
", PositionX=" + PositionX +
", PositionY=" + PositionY +
", Left=" + Left +
", Right=" + Right +
", Bottom=" + Bottom +
", Top=" + Top +
'}';
}
@Override
public boolean equals(Object obj) {
TxtChar to = (TxtChar) obj;
if (to != null) {
return ParagraphIndex == to.ParagraphIndex && CharIndex == to.CharIndex && Char == to.Char && Top == to.Top;
}
return false;
}
}

View File

@ -0,0 +1,18 @@
package com.bifan.txtreaderlib.bean;
/**
* created by bifan-wei
*/
public class TxtFileMsg {
public String FilePath;
public String FileName;
public String HexName;
public long FileSize;
public int CurrentParagraphIndex;
public int CurrentCharIndex;
public int PreParagraphIndex =-1;
public int PreCharIndex = -1;
public String FileCode;
}

View File

@ -0,0 +1,224 @@
package com.bifan.txtreaderlib.bean;
import com.bifan.txtreaderlib.interfaces.ICursor;
import com.bifan.txtreaderlib.interfaces.ITxtLine;
import java.util.ArrayList;
import java.util.List;
/*
* create by bifan-wei
* 2017-11-13
*/
public class TxtLine implements ITxtLine, ICursor<TxtChar> {
private int CurrentIndex;
private List<TxtChar> chars = null;
private boolean isParagraphEndLine = false;
public TxtLine() {
}
@Override
public int CurrentIndex() {
return CurrentIndex;
}
@Override
public Boolean HasData() {
return getCount() != 0;
}
@Override
public List<TxtChar> getTxtChars() {
return chars;
}
@Override
public void addChar(TxtChar txtChar) {
if (chars == null) {
chars = new ArrayList<>();
}
chars.add(txtChar);
}
@Override
public int getCount() {
return chars == null ? 0 : chars.size();
}
@Override
public void moveToPosition(int index) {
if (index < 0 || index >= getCount()) {
throw new ArrayIndexOutOfBoundsException(" moveToPosition index OutOfBoundsException");
}
CurrentIndex = index;
}
@Override
public int getCharNum() {
return getCount();
}
@Override
public void moveToFirst() {
CurrentIndex = 0;
Current();
}
@Override
public TxtChar getFirstChar() {
CurrentIndex = 0;
return Current();
}
@Override
public void moveToLast() {
CurrentIndex = getCount() - 1;
if (CurrentIndex < 0) {
CurrentIndex = 0;
}
Current();
}
@Override
public TxtChar getLastChar() {
CurrentIndex = getCount() - 1;
if (CurrentIndex < 0) {
CurrentIndex = 0;
}
return Current();
}
@Override
public void moveToNext() {
CurrentIndex++;
if (CurrentIndex >= getCount()) {
CurrentIndex = getCount() - 1;
}
if (CurrentIndex < 0) {
CurrentIndex = 0;
}
moveToPosition(CurrentIndex);
}
@Override
public TxtChar getChar(int index) {
if (index < 0 || index >= getCount()) {
throw new ArrayIndexOutOfBoundsException(" moveToPosition index OutOfBoundsException");
}
return chars == null ? null : chars.get(index);
}
@Override
public void moveToPrevious() {
CurrentIndex--;
if (CurrentIndex < 0) {
CurrentIndex = 0;
}
moveToPosition(CurrentIndex);
}
@Override
public boolean isFirst() {
return CurrentIndex == 0;
}
@Override
public ICursor<TxtChar> getCharCursor() {
return this;
}
@Override
public boolean isLast() {
return CurrentIndex == getCount() - 1;
}
@Override
public String getLineStr() {
String str = "";
for (TxtChar txtChar : chars) {
str = str + txtChar.getValue();
}
return str;
}
@Override
public boolean isBeforeFirst() {
return BeforeFirst;
}
@Override
public char[] getLineChar() {
return (getLineStr() + "").toCharArray();
}
@Override
public boolean isAfterLast() {
return AfterLast;
}
private Boolean AfterLast = false;
private Boolean BeforeFirst = false;
@Override
public TxtChar Pre() {
CurrentIndex--;
if (CurrentIndex < 0) {
CurrentIndex = 0;
}
moveToPosition(CurrentIndex);
return Current();
}
@Override
public TxtChar Next() {
CurrentIndex++;
if (CurrentIndex >= getCount()) {
CurrentIndex = getCount() - 1;
}
if (CurrentIndex < 0) {
CurrentIndex = 0;
}
moveToPosition(CurrentIndex);
return Current();
}
@Override
public TxtChar Current() {
if (isLast()) {
AfterLast = true;
} else {
AfterLast = false;
}
if (isFirst()) {
BeforeFirst = true;
} else {
BeforeFirst = false;
}
return chars == null ? null : getChar(CurrentIndex);
}
@Override
public boolean isParagraphEndLine() {
return isParagraphEndLine;
}
public void setParagraphEndLine(boolean paragraphEndLine) {
isParagraphEndLine = paragraphEndLine;
}
@Override
public void Clear() {
if (chars != null) {
chars.clear();
}
}
@Override
public String toString() {
return "" + getLineStr();
}
}

View File

@ -0,0 +1,10 @@
package com.bifan.txtreaderlib.bean;
/*
* create by bifan-wei
* 2017-11-13
*/
public enum TxtMsg {
InitError,
FileNoExist,
}

View File

@ -0,0 +1,11 @@
package com.bifan.txtreaderlib.constant;
import com.bifan.txtreaderlib.utils.ConfigUtils;
public class Constants {
public static final String AUTH_KEY;
static {
AUTH_KEY = ConfigUtils.getAuthKey();
}
}

View File

@ -0,0 +1,15 @@
package com.bifan.txtreaderlib.interfaces;
import com.bifan.txtreaderlib.bean.ChatRequest;
import com.bifan.txtreaderlib.bean.ChatResponse;
import retrofit2.Call;
import retrofit2.http.Body;
import retrofit2.http.Headers;
import retrofit2.http.POST;
public interface ChatGPTService {
@Headers("Authorization: Bearer sk-uucSf3pIkBZSYHzQ6028C9FcCe4e4d61B457497b4d72C894")
@POST("v1/chat/completions")
Call<ChatResponse> postMessage(@Body ChatRequest request);
}

View File

@ -0,0 +1,19 @@
package com.bifan.txtreaderlib.interfaces;
/**
* created by bifan-wei
*/
public interface ICenterAreaClickListener {
/**
* @param widthPercentInView
* @return 返回是否处理了这个事件如果已经处理了将可能会执行翻页事件
*/
boolean onCenterClick(float widthPercentInView);
/**
* @param widthPercentInView
* @return 返回是否处理了这个事件如果已经处理了将可能会执行翻页事件
*/
boolean onOutSideCenterClick(float widthPercentInView);
}

View File

@ -0,0 +1,27 @@
package com.bifan.txtreaderlib.interfaces;
/**
* Created by HP on 2017/11/15.
*/
public interface IChapter {
int getIndex();
int getStartParagraphIndex();
int getEndParagraphIndex();
int getStartCharIndex();
int getEndCharIndex();
int getStartIndex();
String getTitle();
void setStartParagraphIndex(int index);
void setEndParagraphIndex(int index);
}

View File

@ -0,0 +1,14 @@
package com.bifan.txtreaderlib.interfaces;
/**
* created by bifan-wei
*/
public interface IChapterMatcher {
/**
* @param paragraphData 段落数据
* @param ParagraphIndex 当前段落位置
* @return
*/
IChapter match(String paragraphData, int ParagraphIndex);
}

View File

@ -0,0 +1,14 @@
package com.bifan.txtreaderlib.interfaces;
/**
* Created by HP on 2017/11/15.
*/
public interface ICharpter {
int getParagraphNum();
int getStartParagraphIndex();
int getEndParagraphIndex();
String getTitle();
void setStartParagraphIndex(int index);
void setEndParagraphIndex(int index);
}

View File

@ -0,0 +1,21 @@
package com.bifan.txtreaderlib.interfaces;
/*
* create by bifan-wei
* 2017-11-13
*/
public interface ICursor<T> {
int getCount();
void moveToPosition(int var1);
void moveToFirst();
void moveToLast();
void moveToNext();
void moveToPrevious();
boolean isFirst();
boolean isLast();
boolean isBeforeFirst();
boolean isAfterLast();
T Pre();
T Next();
T Current();
}

View File

@ -0,0 +1,14 @@
package com.bifan.txtreaderlib.interfaces;
import com.bifan.txtreaderlib.bean.TxtMsg;
/*
* create by bifan-wei
* 2017-11-13
*/
public interface ILoadListener {
void onSuccess();
void onFail(TxtMsg txtMsg);
void onMessage(String message);
}

View File

@ -0,0 +1,28 @@
package com.bifan.txtreaderlib.interfaces;
import com.bifan.txtreaderlib.bean.TxtChar;
import java.util.List;
/*
* create by bifan-wei
* 2017-11-13
*/
public interface IPage {
ITxtLine getLine(int index);
void addLine(ITxtLine line);
void addLineTo(ITxtLine line, int index);
void setLines(List<ITxtLine> lines);
TxtChar getFirstChar();
TxtChar getLastChar();
ITxtLine getFirstLine();
ITxtLine getLastLine();
List<ITxtLine> getLines();
ICursor<ITxtLine> getLineCursor();
int getLineNum();
boolean isFullPage();//是否满页了
void setFullPage(boolean fullPage);
int CurrentIndex();
Boolean HasData();
}

View File

@ -0,0 +1,10 @@
package com.bifan.txtreaderlib.interfaces;
/**
* Created by bifan-wei
* on 2017/12/8.
*/
public interface IPageChangeListener {
void onCurrentPage(float progress);
}

View File

@ -0,0 +1,10 @@
package com.bifan.txtreaderlib.interfaces;
/*
* create by bifan-wei
* 2017-11-13
*/
public interface IPageDataPipeline {
IPage getPageStartFromProgress(int paragraphIndex, int charIndex);
IPage getPageEndToProgress(int paragraphIndex, int charIndex);
}

View File

@ -0,0 +1,10 @@
package com.bifan.txtreaderlib.interfaces;
/**
* created by bifan-wei
*/
public interface IPageEdgeListener {
void onCurrentFirstPage();
void onCurrentLastPage();
}

View File

@ -0,0 +1,31 @@
package com.bifan.txtreaderlib.interfaces;
/*
* create by bifan-wei
* 2017-11-13
*/
public interface IParagraphData {
void addParagraph(int ParaIndex, String p);
void addParagraph(String p);
int getParagraphNum();
int getCharNum();
int findParagraphIndexByCharIndex(int CharIndex);
int getParaStartCharIndex(int ParaIndex);
String getParagraphStr(int ParaIndex);
/**
* @param ParaIndex 段落位置
* @param CharIndex 字符位置
* @return 如果段落位置超出范围报越界异常如果字符位置超出段落范围返回空字符
*/
String getParagraphStrFromStart(int ParaIndex,int CharIndex);
/**
* @param ParaIndex
* @param CharIndex
* @return 如果段落位置超出范围报越界异常如果字符位置超出段落范围返回空字符
*/
String getParagraphStrToEnd(int ParaIndex,int CharIndex);
void Clear();
}

View File

@ -0,0 +1,61 @@
package com.bifan.txtreaderlib.interfaces;
import android.graphics.Canvas;
import android.view.MotionEvent;
/**
* Created by bifan-wei
* on 2017/12/1.
*/
public interface IReaderViewDrawer {
void drawPageNextPageShadow(Canvas canvas);
//绘制执行下一页的页边阴影
void drawPageNextBottomPage(Canvas canvas);
//绘制执行下一页时的下面页部分
void drawPageNextTopPage(Canvas canvas);
//绘制执行下一页时的上面页部分
void drawPagePrePageShadow(Canvas canvas);
//绘制执行上一页的页边阴影
void drawPagePreBottomPage(Canvas canvas);
//绘制执行上一页时的下面页部分
void drawPagePreTopPage(Canvas canvas);
//绘制执行上一页时的上面页部分
void startPageStateBackAnimation();
//状态恢复动画
void startPageNextAnimation();
//执行下一页动画
void startPagePreAnimation();
//执行上一页动画
void onTextSelectMoveForward(MotionEvent event);
// 文字向前选中
void onTextSelectMoveBack(MotionEvent event);
//文字向后选中
void onPageMove(MotionEvent event);
//页面移动
void drawNote(Canvas canvas);
//绘制笔记
void drawSelectedText(Canvas canvas);
//文字选中
void computeScroll();
//完成移动
}

View File

@ -0,0 +1,13 @@
package com.bifan.txtreaderlib.interfaces;
import com.bifan.txtreaderlib.bean.TxtChar;
/**
* created by bifan-wei
*/
public interface ISliderListener {
void onShowSlider(TxtChar txtChar);
void onShowSlider(String CurrentSelectedText);
void onReleaseSlider();
}

View File

@ -0,0 +1,19 @@
package com.bifan.txtreaderlib.interfaces;
import android.graphics.Canvas;
import android.graphics.Paint;
import com.bifan.txtreaderlib.bean.TxtChar;
import java.util.List;
/**
* Created by bifan-wei
* on 2017/12/4.
*/
public interface ITextSelectDrawer {
void drawSelectedChar(TxtChar selectedChar, Canvas canvas, Paint paint);
void drawSelectedLines(List<ITxtLine> selectedLines, Canvas canvas, Paint paint);
}

View File

@ -0,0 +1,14 @@
package com.bifan.txtreaderlib.interfaces;
import com.bifan.txtreaderlib.bean.TxtChar;
/**
* Created by bifan-wei
* on 2017/12/5.
*/
public interface ITextSelectListener {
void onTextChanging(TxtChar firstSelectedChar,TxtChar lastSelectedChar);
void onTextChanging(String selectText);
void onTextSelected(String selectText);
}

View File

@ -0,0 +1,27 @@
package com.bifan.txtreaderlib.interfaces;
import com.bifan.txtreaderlib.bean.TxtChar;
import java.util.List;
/*
* create by bifan-wei
* 2017-11-14
*/
public interface ITxtLine {
List<TxtChar> getTxtChars();
int getCharNum();
TxtChar getFirstChar();
TxtChar getLastChar();
TxtChar getChar(int index);
ICursor<TxtChar> getCharCursor();
String getLineStr();
char[] getLineChar();
Boolean HasData();
void addChar(TxtChar txtChar);
void Clear();
int CurrentIndex();
boolean isParagraphEndLine();//判断是否是段落最后一句
void setParagraphEndLine(boolean isParagraphEndLine);
}

View File

@ -0,0 +1,9 @@
package com.bifan.txtreaderlib.interfaces;
/**
* Created by HP on 2017/11/15.
*/
public interface ITxtReaderLoggerListener {
void onLog(String tag, String msg);
}

View File

@ -0,0 +1,11 @@
package com.bifan.txtreaderlib.interfaces;
import com.bifan.txtreaderlib.main.TxtReaderContext;
/*
* create by bifan-wei
* 2017-11-13
*/
public interface ITxtTask {
void Run(ILoadListener callBack, TxtReaderContext readerContext);
}

View File

@ -0,0 +1,62 @@
package com.bifan.txtreaderlib.main;
import android.graphics.Bitmap;
/**
* Created by bifa-wei
* on 2017/11/27.
*/
public class BitmapData {
private final Bitmap[] pages = new Bitmap[3];
private Bitmap BgBitmap;
public void setFirstPage(Bitmap page) {
pages[0] = page;
}
public void setMidPage(Bitmap page) {
pages[1] = page;
}
public void setLastPage(Bitmap page) {
pages[2] = page;
}
public Bitmap FirstPage() {
return pages[0];
}
public Bitmap MidPage() {
return pages[1];
}
public Bitmap LastPage() {
return pages[2];
}
public void setBgBitmap(Bitmap bgBitmap) {
BgBitmap = bgBitmap;
}
public Bitmap getBgBitmap() {
return BgBitmap;
}
public Bitmap[] getPages() {
return pages;
}
public void onDestroy() {
recycle(getBgBitmap());
recycle(FirstPage());
recycle(MidPage());
recycle(LastPage());
}
private void recycle(Bitmap bitmap) {
if (bitmap != null) {
bitmap.recycle();
}
}
}

View File

@ -0,0 +1,222 @@
package com.bifan.txtreaderlib.main;
import android.content.Context;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import androidx.annotation.NonNull;
import android.text.TextUtils;
import com.bifan.txtreaderlib.bean.FileReadRecordBean;
import java.util.ArrayList;
import java.util.List;
public class FileReadRecordDB extends SQLiteOpenHelper {
private static final int Version_1 = 1;
private static final String DB_NAME = "hwTxtReader";
private String TABLE_NAME = "FileReadRecord";
private final String FileHashName = "fileHashName";
private final String SearchId = "searchId";
private final String FilePath = "filePath";
private final String FileName = "fileName";
private final String ParagraphIndex = "paragraphIndex";
private final String ChartIndex = "chartIndex";
private String sql = "create table if not exists " + TABLE_NAME + " (" + SearchId
+ " integer primary key autoincrement," + FileHashName + " varchar(50),"
+ FilePath + " varchar(100), " + FileName + " varchar(100),"
+ ParagraphIndex + " integer, " + ChartIndex + " integer)";
public FileReadRecordDB(Context context) {
this(context, DB_NAME, null, Version_1);
}
private FileReadRecordDB(Context context, String name, SQLiteDatabase.CursorFactory factory, int version) {
super(context, name, factory, version);
}
public void createTable() {
getWritableDatabase().execSQL(sql);
}
@Override
public void onCreate(SQLiteDatabase db) {
}
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
}
/**
* 关闭数据表
*/
public void closeTable() {
close();
}
/**
* 删除数据表
*/
public void delectTable() {
getWritableDatabase().execSQL("DROP TABLE IF EXISTS " + TABLE_NAME);
}
public void insertData(String fileHashName, String path, String fileName, int paragraphIndex, int chartIndex) {
if (!TextUtils.isEmpty(FileHashName)) {
if (IsHasData(FileHashName, fileHashName)) {//已经存在了先删除
deleteRecordsByFileHashName(fileHashName);
}
String sql = " insert into " + TABLE_NAME + " (" + FileHashName + "," + FilePath + "," + FileName + "," + ParagraphIndex + "," + ChartIndex + ") values ('" + fileHashName
+ "','" + path + "','" + fileName + "','" + paragraphIndex + "','" + chartIndex + "')";
getWritableDatabase().execSQL(sql);
}
}
public void insertData(@NonNull FileReadRecordBean recordBean) {
insertData(
recordBean.fileHashName
, recordBean.filePath
, recordBean.fileName
, recordBean.paragraphIndex
, recordBean.chartIndex);
}
/**
* @param fileHashName 文件hash值
* @return 如果没有记录返回null
*/
public FileReadRecordBean getRecordByHashName(String fileHashName) {
if (!TextUtils.isEmpty(fileHashName) && IsHasData(FileHashName, fileHashName)) {
Cursor cursor = getCursorByKeyValue(FileHashName, fileHashName);
if (cursor != null) {
if (cursor.getCount() > 0) {
cursor.moveToFirst();
FileReadRecordBean bean = getRecordBeanFromCursor(cursor);
cursor.close();
return bean;
}
cursor.close();
}
}
return null;
}
/**
* @param userId
* @return 如果userId 为空不分用户返回的是所有下载的数据,否则返回指定用户的下载记录,不会返回空
*/
public List<FileReadRecordBean> getRecord(String userId) {
List<FileReadRecordBean> searchBeans = new ArrayList<>();
Cursor cursor;
if (TextUtils.isEmpty(userId)) {
cursor = getWritableDatabase().rawQuery("select * from " + TABLE_NAME, null);
} else {
cursor = getCursorByKeyValue(FileHashName, userId);
}
if (cursor != null) {
if (cursor.getCount() > 0) {
cursor.moveToFirst();
while (!cursor.isAfterLast()) {
searchBeans.add(getRecordBeanFromCursor(cursor));
cursor.moveToNext();
}
}
cursor.close();
}
return searchBeans;
}
/**
* @return 获取所有记录
*/
public List<FileReadRecordBean> getAllFileRecord() {
return getRecord(null);
}
private FileReadRecordBean getRecordBeanFromCursor(Cursor cursor) {
FileReadRecordBean bean = new FileReadRecordBean();
int userIdIndex = cursor.getColumnIndex(FileHashName);
bean.fileHashName = cursor.getString(userIdIndex);
int searchIdIndex = cursor.getColumnIndex(SearchId);
bean.id = cursor.getInt(searchIdIndex);
int CachePathIndex = cursor.getColumnIndex(FilePath);
bean.filePath = cursor.getString(CachePathIndex);
int fileNameIndex = cursor.getColumnIndex(FileName);
bean.fileName = cursor.getString(fileNameIndex);
int paragraphIndex = cursor.getColumnIndex(ParagraphIndex);
bean.paragraphIndex = cursor.getInt(paragraphIndex);
int chartIndex = cursor.getColumnIndex(ChartIndex);
bean.chartIndex = cursor.getInt(chartIndex);
return bean;
}
/**
* @param recordBean 要删除的记录
*/
public void deleteRecord(FileReadRecordBean recordBean) {
if (IsHasData(SearchId, recordBean.id + "")) {
delete(SearchId, recordBean.id + "");
}
}
/**
* @param fileHashName fileHashName
*/
public void deleteRecordsByFileHashName(String fileHashName) {
if (IsHasData(FileHashName, fileHashName + "")) {
delete(FileHashName, fileHashName + "");
}
}
/**
* @param key
* @param value
* @return 是否有数据
*/
private Boolean IsHasData(String key, String value) {
Cursor cursor = getCursorByKeyValue(key, value);
if (cursor != null) {
if (cursor.getCount() > 0) {
cursor.close();
return true;
}
cursor.close();
}
return false;
}
/**
* @param key
* @param value
*/
private void delete(String key, String value) {
String sql = "delete from " + TABLE_NAME + " where " + key + " = " + "'" + value + "'";
getWritableDatabase().execSQL(sql);
}
/**
* @param key
* @param value
* @return
*/
private Cursor getCursorByKeyValue(String key, String value) {
Cursor cursor = getWritableDatabase()
.rawQuery("select * from " + TABLE_NAME + " where " + key + " = " + "'" + value + "'", null);
return cursor;
}
}

View File

@ -0,0 +1,23 @@
package com.bifan.txtreaderlib.main;
import com.bifan.txtreaderlib.bean.TxtMsg;
import com.bifan.txtreaderlib.interfaces.ILoadListener;
/**
* Created by bifan-wei
* on 2017/12/11.
*/
public class LoadListenerAdapter implements ILoadListener{
@Override
public void onSuccess() {
}
@Override
public void onFail(TxtMsg txtMsg) {
}
@Override
public void onMessage(String message) {
}
}

View File

@ -0,0 +1,258 @@
package com.bifan.txtreaderlib.main;
import android.graphics.Canvas;
import android.graphics.Region;
import android.graphics.drawable.GradientDrawable;
import android.view.MotionEvent;
import android.widget.Scroller;
import com.bifan.txtreaderlib.interfaces.IReaderViewDrawer;
/**
* Created by bifan-wei
* on 2017/12/1.
*/
public class NormalPageDrawer extends PageDrawerBase implements IReaderViewDrawer {
private String tag = "NormalReaderViewDrawer";
private final static int BorderShadowWith = 20;
public NormalPageDrawer(TxtReaderView readerView, TxtReaderContext readerContext, Scroller scroller) {
super(readerView, readerContext, scroller);
}
@Override
public void drawPageNextPageShadow(Canvas canvas) {
//绘制执行下一页的页边阴影
mPath.reset();
int left = (int) getMoveDistance() + getWidth();
int top = 0;
int right = left + BorderShadowWith;
int bottom = getHeight();
if (left > BorderShadowWith) {
getPageNextBorderDrawable().setBounds(left, top, right, bottom);
getPageNextBorderDrawable().draw(canvas);
}
}
@Override
public void drawPageNextBottomPage(Canvas canvas) {
//绘制执行下一页时的下面页部分
/* float x =getWidth()+getMoveDistance();
mPath.reset();
mPath.moveTo(x, 0);
mPath.lineTo(getWidth(), 0);
mPath.lineTo(getWidth(), getHeight());
mPath.lineTo(x, getHeight());
mPath.lineTo(x, 0);
canvas.clipPath(mPath, Region.Op.INTERSECT);
canvas.drawBitmap(getBottomPage(), x, x, null);*/
float x =getWidth()+getMoveDistance();
mPath.reset();
mPath.moveTo(x, 0);
mPath.lineTo(getWidth(), 0);
mPath.lineTo(getWidth(), getHeight());
mPath.lineTo(x, getHeight());
mPath.lineTo(x, 0);
canvas.clipPath(mPath, Region.Op.INTERSECT);
canvas.drawBitmap(getBottomPage(), 0, 0, null);
}
@Override
public void drawPageNextTopPage(Canvas canvas) {
//绘制执行下一页时的上面页部分
mPath.reset();
mPath.moveTo(0, 0);
mPath.lineTo(getWidth() , 0);
mPath.lineTo(getWidth() , getHeight());
mPath.lineTo(0, getHeight());
mPath.lineTo(0, 0);
canvas.clipPath(mPath,Region.Op.INTERSECT);
canvas.drawBitmap(getTopPage(), getMoveDistance()+1, 0, null);
}
@Override
public void drawPagePrePageShadow(Canvas canvas) {
//绘制执行上一页的页边阴影
mPath.reset();
int left = (int) getMoveDistance() - BorderShadowWith;
int top = 0;
int right = (int) getMoveDistance();
int bottom = getHeight();
if (right < getWidth() - BorderShadowWith) {
getPagePreBorderDrawable().setBounds(left, top, right, bottom);
getPagePreBorderDrawable().draw(canvas);
}
}
@Override
public void drawPagePreBottomPage(Canvas canvas) {
//绘制执行上一页时的下面页部分
float x = getMoveDistance();
mPath.reset();
mPath.moveTo(0, 0);
mPath.lineTo(x, 0);
mPath.lineTo(x, getHeight());
mPath.lineTo(0, getHeight());
mPath.lineTo(0, 0);
canvas.clipPath(mPath, Region.Op.INTERSECT);
canvas.drawBitmap(getBottomPage(), 0, 0, null);
}
@Override
public void drawPagePreTopPage(Canvas canvas) {
//绘制执行上一页时的上面页部分
float distance = getMoveDistance();
float x = getWidth();
mPath.reset();
mPath.moveTo(0, 0);
mPath.lineTo(x , 0);
mPath.lineTo(x , getHeight());
mPath.lineTo(0, getHeight());
mPath.lineTo(0, 0);
canvas.clipPath(mPath, Region.Op.INTERSECT);
canvas.drawBitmap(getTopPage(), distance, 0, null);
}
private Boolean onPageStateBackAnimation = false;
@Override
public void startPageStateBackAnimation() {
if (readerView.isPagePre() || readerView.isPageNext()) {
onPageStateBackAnimation = true;
scroller.startScroll((int) readerView.mTouch.x, 0, -(int) getMoveDistance(), 0, PageSwitchTime);
postInvalidate();
}
}
@Override
public void startPageNextAnimation() {
scroller.startScroll(getWidth() + (int) getMoveDistance(), 0, -(getWidth() + (int) getMoveDistance()), 0, PageSwitchTime);
readerView.mDown.x = getWidth();//从getWidth()开始
postInvalidate();
}
@Override
public void startPagePreAnimation() {
scroller.startScroll((int) getMoveDistance(), 0, getWidth() - (int) getMoveDistance(), 0, PageSwitchTime);
readerView.mDown.x = 0;
postInvalidate();
}
@Override
public void onTextSelectMoveForward(MotionEvent event) {
}
@Override
public void onTextSelectMoveBack(MotionEvent event) {
}
@Override
public void onPageMove(MotionEvent event) {
readerView.mTouch.x = event.getX();
readerView.mTouch.y = event.getY();
postInvalidate();
}
private void postInvalidate() {
readerView.postInvalidate();
}
@Override
public void drawNote(Canvas canvas) {
}
@Override
public void drawSelectedText(Canvas canvas) {
if (readerView.CurrentMode == TxtReaderBaseView.Mode.PressSelectText) {
drawPressSelectedText(canvas);
} else if (readerView.CurrentMode == TxtReaderBaseView.Mode.SelectMoveBack) {
drawSelectedLinesText(canvas);
} else if (readerView.CurrentMode == TxtReaderBaseView.Mode.SelectMoveForward) {
drawSelectedLinesText(canvas);
}
}
private void drawSelectedLinesText(Canvas canvas) {
getTextSelectDrawer().drawSelectedLines(readerView.getCurrentSelectTextLine(), canvas, readerContext.getPaintContext().selectTextPaint);
//draw slider
drawSlider(canvas);
}
private void drawPressSelectedText(Canvas canvas) {
getTextSelectDrawer().drawSelectedChar(readerView.FirstSelectedChar, canvas, readerContext.getPaintContext().selectTextPaint);
//draw slider
drawSlider(canvas);
}
private void drawSlider(Canvas canvas) {
if (readerView.getLeftSliderPath() != null && readerView.getRightSliderPath() != null) {
canvas.drawPath(readerView.getLeftSliderPath(), readerContext.getPaintContext().sliderPaint);
canvas.drawPath(readerView.getRightSliderPath(), readerContext.getPaintContext().sliderPaint);
}
}
@Override
public void computeScroll() {
if (scroller.computeScrollOffset()) {
readerView.mTouch.x = scroller.getCurrX();
readerView.invalidate();
checkPageData();
}
}
private synchronized void checkPageData() {
if (onPageStateBackAnimation) {
if ((getMoveDistance() > 0 && getMoveDistance() <= 3) || (getMoveDistance() < 0 && getMoveDistance() >= -3)) {
scroller.abortAnimation();
readerView.releaseTouch();
readerView.invalidate();
onPageStateBackAnimation = false;
}
} else {
if (readerView.mTouch.x == 0) {//执行下一页数据获取
readerView.doPageNextDone();
scroller.abortAnimation();
} else if (readerView.mTouch.x == getWidth()) {//执行上一页数据获取
readerView.doPagePreDone();
scroller.abortAnimation();
}
}
}
private GradientDrawable mPagePreBorderDrawable;
private GradientDrawable mPageNextBorderDrawable;
private GradientDrawable getPagePreBorderDrawable() {
if (mPagePreBorderDrawable == null) {
int[] color = new int[]{0xaa666666, 0x00666666};
mPagePreBorderDrawable = new GradientDrawable(GradientDrawable.Orientation.RIGHT_LEFT, color);
}
return mPagePreBorderDrawable;
}
private GradientDrawable getPageNextBorderDrawable() {
if (mPageNextBorderDrawable == null) {
int[] color = new int[]{0xaa666666, 0x00666666};
mPageNextBorderDrawable = new GradientDrawable(GradientDrawable.Orientation.LEFT_RIGHT, color);
}
return mPageNextBorderDrawable;
}
}

View File

@ -0,0 +1,61 @@
package com.bifan.txtreaderlib.main;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.RectF;
import com.bifan.txtreaderlib.bean.TxtChar;
import com.bifan.txtreaderlib.interfaces.ITextSelectDrawer;
import com.bifan.txtreaderlib.interfaces.ITxtLine;
import com.bifan.txtreaderlib.utils.ELogger;
import java.util.List;
/**
* Created by bifan-wei
* on 2017/12/4.
*/
public class NormalTextSelectDrawer implements ITextSelectDrawer {
private final Path path = new Path();
@Override
public void drawSelectedChar(TxtChar selectedChar, Canvas canvas, Paint paint) {
if (selectedChar != null) {
ELogger.log("onPressSelectText","drawSelectedChar");
path.reset();
path.moveTo(selectedChar.Left, selectedChar.Top);
path.lineTo(selectedChar.Right, selectedChar.Top);
path.lineTo(selectedChar.Right, selectedChar.Bottom);
path.lineTo(selectedChar.Left, selectedChar.Bottom);
path.lineTo(selectedChar.Left, selectedChar.Top);
canvas.drawPath(path, paint);
}
}
@Override
public void drawSelectedLines(List<ITxtLine> selectedLines, Canvas canvas, Paint paint) {
for (ITxtLine line : selectedLines) {
ELogger.log("onPressSelectText",line.getLineStr());
if (line.getTxtChars() != null && line.getTxtChars().size() > 0) {
TxtChar fistChar = line.getTxtChars().get(0);
TxtChar lastChar = line.getTxtChars().get(line.getTxtChars().size() - 1);
float fw = fistChar.CharWidth;
float lw = lastChar.CharWidth;
RectF rect = new RectF(fistChar.Left, fistChar.Top,
lastChar.Right, lastChar.Bottom);
canvas.drawRoundRect(rect, fw / 2,
paint.getTextSize() / 2, paint);
}
}
}
}

View File

@ -0,0 +1,49 @@
package com.bifan.txtreaderlib.main;
import com.bifan.txtreaderlib.interfaces.IPage;
/**
* Created by bifa-wei
* on 2017/11/27.
*/
public class PageData {
private final IPage[] pages = new IPage[3];
public final int[] refreshTag = new int[]{1, 1, 1};//1:需要刷新0不需要刷新
public void setFirstPage(IPage page) {
pages[0] = page;
}
public void setMidPage(IPage page) {
pages[1] = page;
}
public void setLastPage(IPage page) {
pages[2] = page;
}
public IPage FirstPage() {
return pages[0];
}
public IPage MidPage() {
return pages[1];
}
public IPage LastPage() {
return pages[2];
}
public IPage[] getPages() {
return pages;
}
public void onDestroy() {
pages[0]=null;
pages[1]=null;
pages[2]=null;
}
}

View File

@ -0,0 +1,330 @@
package com.bifan.txtreaderlib.main;
import android.graphics.Paint;
import com.bifan.txtreaderlib.bean.EnChar;
import com.bifan.txtreaderlib.bean.NumChar;
import com.bifan.txtreaderlib.bean.Page;
import com.bifan.txtreaderlib.bean.TxtChar;
import com.bifan.txtreaderlib.bean.TxtLine;
import com.bifan.txtreaderlib.interfaces.IPage;
import com.bifan.txtreaderlib.interfaces.IPageDataPipeline;
import com.bifan.txtreaderlib.interfaces.IParagraphData;
import com.bifan.txtreaderlib.interfaces.ITxtLine;
import com.bifan.txtreaderlib.utils.FormatUtil;
import com.bifan.txtreaderlib.utils.TextBreakUtil;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
public class PageDataPipeline implements IPageDataPipeline {
private TxtReaderContext readerContext;
public PageDataPipeline(TxtReaderContext readerContext) {
this.readerContext = readerContext;
}
@Override
public IPage getPageStartFromProgress(int paragraphIndex, int charIndex) {
IParagraphData data = readerContext.getParagraphData();
PageParam param = readerContext.getPageParam();
if(data==null) return null;
int PageLineNum = param.PageLineNum;
int PageHeight = param.PageHeight;
int LineHeight = (int) param.LineHeight;
int CurrentPaIndex = paragraphIndex;
int startIndex = charIndex;
int ParagraphNum = data.getParagraphNum();
float lineWidth = readerContext.getPageParam().LineWidth;
float textPadding = readerContext.getPageParam().TextPadding;
int paragraphMargin = param.ParagraphMargin;
if (CurrentPaIndex >= ParagraphNum || CurrentPaIndex < 0) return null;//超过返回null
IPage page = new Page();
//获取页面数据
while (page.getLineNum() < PageLineNum && CurrentPaIndex < ParagraphNum) {
String paragraphStr = data.getParagraphStr(CurrentPaIndex);
if (paragraphStr != null && paragraphStr.length() > 0) {
List<ITxtLine> lines = getLineFromParagraphStartOnBeginning(paragraphStr, CurrentPaIndex, startIndex, lineWidth, textPadding, readerContext.getPaintContext().textPaint);
for (ITxtLine line : lines) {
page.addLine(line);
if (page.getLineNum() >= PageLineNum) {
page.setFullPage(true);
break;
}
}
startIndex = 0;
}
CurrentPaIndex++;
}
//尝试识别是否需要加段落间距数据
PageHeight = PageHeight - param.PaddingTop - param.PaddingBottom;
int textHeight = readerContext.getTxtConfig().textSize;
if (paragraphMargin > 0 && LineHeight > 0 && PageHeight > LineHeight && page.getLineNum() > 0) {
int height = param.PaddingTop;
List<ITxtLine> lines = new ArrayList<>();
for (int i = 0; i < page.getLineNum(); i++) {
ITxtLine line = page.getLine(i);
height = height + LineHeight;
if (height > PageHeight) {
page.setFullPage(true);
if (height - LineHeight + textHeight <= PageHeight) {
lines.add(line);
}
break;
} else {
lines.add(line);
}
if (line.isParagraphEndLine()) {
// if (height + paragraphMargin < PageHeight) {
height = height + paragraphMargin;//说明还有行数据
// }
}
}
if (height >= PageHeight) {
page.setFullPage(true);
}
page.setLines(lines);
}
return page;
}
@Override
public IPage getPageEndToProgress(int paragraphIndex, int charIndex) {
IParagraphData data = readerContext.getParagraphData();
PageParam param = readerContext.getPageParam();
if(data==null) return null;
int PageLineNum = param.PageLineNum;
int PageHeight = param.PageHeight;
int LineHeight = (int) param.LineHeight;
int CurrentPaIndex = paragraphIndex;
int startIndex = charIndex;
int ParagraphNum = data.getParagraphNum();
float lineWidth = readerContext.getPageParam().LineWidth;
float textPadding = readerContext.getPageParam().TextPadding;
if (charIndex == 0) {//说明上页开始是段落开始位置段落左移
CurrentPaIndex--;//
startIndex = 0;
}
if (CurrentPaIndex >= ParagraphNum || CurrentPaIndex < 0) return null;//超过返回null
IPage page = new Page();
PageHeight = PageHeight - param.PaddingTop - param.PaddingBottom;
int paragraphMargin = param.ParagraphMargin;
while (page.getLineNum() < PageLineNum && CurrentPaIndex >= 0) {
String paragraphStr = data.getParagraphStr(CurrentPaIndex);
if (paragraphStr != null && paragraphStr.length() > 0) {
if (startIndex == 0) {//说明上页开始是段落开始位置
startIndex = paragraphStr.length();
}
List<ITxtLine> lines = getLineFromParagraphOnEnd(paragraphStr, CurrentPaIndex, startIndex, lineWidth, textPadding, readerContext.getPaintContext().textPaint);
if (lines.size() > 0) {
for (int i = lines.size() - 1; i >= 0; i--) {
ITxtLine line = lines.get(i);
page.addLine(line);
if (page.getLineNum() >= PageLineNum) {
page.setFullPage(true);
break;
}
}
}
}
CurrentPaIndex--;
startIndex = 0;
}
if (page.HasData()) {
Collections.reverse(page.getLines());
}
//尝试识别是否需要加段落间距数据
PageHeight = PageHeight - param.PaddingTop - param.PaddingBottom;
if (paragraphMargin > 0 && LineHeight > 0 && PageHeight > LineHeight && page.getLineNum() > 0) {
int height = param.PaddingTop;
int textHeight = readerContext.getTxtConfig().textSize;
List<ITxtLine> lines = new ArrayList<>();
for (int i = page.getLineNum() - 1; i >= 0; i--) {
ITxtLine line = page.getLine(i);
if (i == page.getLineNum() - 1) {//底部那个不添加偏移量
lines.add(line);
height = height + textHeight;
} else {
if (height + LineHeight > PageHeight) {
page.setFullPage(true);
if (height + textHeight <= PageHeight) {
lines.add(line);
}
break;
} else {
lines.add(line);
height = height + LineHeight;
if (line.isParagraphEndLine()) {
// if (height + paragraphMargin < PageHeight) {
height = height + paragraphMargin;//说明还有行数据
// }
}
}
}
}
if (height >= PageHeight) {
page.setFullPage(true);
}
page.setLines(lines);
if (page.HasData()) {
Collections.reverse(page.getLines());
}
}
return page;
}
/**
* @param paragraphData 完整段落数据
* @param paragraphIndex 段落下标
* @param startCharIndex 开始字符位置
* @param lineWidth 行宽度
* @param textPadding 字体间距
* @param paint 画笔
* @return startCharIndex超过范围返回空集合
*/
//开始满行
private List<ITxtLine> getLineFromParagraphStartOnBeginning(String paragraphData, int paragraphIndex, int startCharIndex,
float lineWidth, float textPadding, Paint paint) {
List<ITxtLine> lines = new ArrayList<>();
int startIndex = startCharIndex;
startIndex = startIndex < 0 ? 0 : startIndex;
int paragraphLength = paragraphData.length();
if (startIndex >= paragraphLength) {
return lines;
}
if ( paragraphData.length() > 0) {
String s = paragraphData.substring(startIndex);//截取要的数据
while (s.length() > 0) {// is[0] 为个数 is[1] 为是否满一行
float[] is = TextBreakUtil.BreakText(s, lineWidth, textPadding, paint);
ITxtLine line;
if (is[1] != 1) {//不满一行
line = getLineFromString(s, paragraphIndex, startIndex, is);
if (s.length() + startIndex >= paragraphLength) {
line.setParagraphEndLine(true);
}
lines.add(line);
break;
}
int num = (int) is[0];
String lineStr = s.substring(0, num);
line = getLineFromString(lineStr, paragraphIndex, startIndex, is);
startIndex = startIndex + num;
if (line != null) {
lines.add(line);
}
s = s.substring(num);
}
}
return lines;
}
/**
* @param paragraphData
* @param paragraphIndex
* @param endCharIndex
* @param lineWidth
* @param textPadding
* @param paint
* @return startCharIndex等于0返回空集合超过范围为段落长度数据
*/
//结束满行
private List<ITxtLine> getLineFromParagraphOnEnd(String paragraphData, int paragraphIndex, int endCharIndex,
float lineWidth, float textPadding, Paint paint) {
List<ITxtLine> lines = new ArrayList<>();
int startIndex = 0;
int paragraphLength = paragraphData.length();
if (paragraphLength == 0 || endCharIndex <= 0) {
return lines;
}
endCharIndex = endCharIndex >= paragraphData.length() ? paragraphData.length() : endCharIndex;
if (endCharIndex > 0) {
String s = paragraphData.substring(startIndex, endCharIndex);//截取要的数据
while (s.length() > 0) {// is[0] 为个数 is[1] 为是否满一行
float[] is = TextBreakUtil.BreakText(s, lineWidth, textPadding, paint);
ITxtLine line;
int num = (int) is[0];
if (is[1] != 1) {//不满一行
line = getLineFromString(s, paragraphIndex, startIndex, is);
// ELogger.log("isParagraphEndLine11",endCharIndex+"/"+startIndex+"/"+paragraphLength+"/"+s.length()+"/"+s);
if (endCharIndex == paragraphLength && s.length() + startIndex >= paragraphLength) {
line.setParagraphEndLine(true);
}
lines.add(line);
return lines;
}
String lineStr = s.substring(0, num);
line = getLineFromString(lineStr, paragraphIndex, startIndex, is);
startIndex = startIndex + num;
if (line != null) {
lines.add(line);
}
s = s.substring(lineStr.length());
}
}
return lines;
}
private ITxtLine getLineFromString(String str, int ParagraphIndex, int charIndex, float[] is) {
ITxtLine line = null;
int index = charIndex;
int cIndex = 2;//找到字符宽度
if (str != null && str.length() > 0) {
line = new TxtLine();
char[] cs = str.toCharArray();
for (char c : cs) {
TxtChar Char;
if (FormatUtil.isDigital(c)) {
Char = new NumChar(c);
} else if (FormatUtil.isLetter(c)) {
Char = new EnChar(c);
} else {
Char = new TxtChar(c);
}
Char.ParagraphIndex = ParagraphIndex;
Char.CharIndex = index++;
Char.CharWidth = is[cIndex++];
line.addChar(Char);
}
}
return line;
}
}

View File

@ -0,0 +1,60 @@
package com.bifan.txtreaderlib.main;
import android.graphics.Bitmap;
import android.graphics.Path;
import android.widget.Scroller;
import com.bifan.txtreaderlib.interfaces.ITextSelectDrawer;
/**
* Created by bifan-wei
* on 2017/12/2.
*/
public class PageDrawerBase {
protected int PageSwitchTime = 400;
protected TxtReaderView readerView;
protected TxtReaderContext readerContext;
protected Scroller scroller;
protected Path mPath = new Path();
protected ITextSelectDrawer textSelectDrawer;
public PageDrawerBase(TxtReaderView readerView, TxtReaderContext readerContext, Scroller scroller) {
this.readerView = readerView;
this.readerContext = readerContext;
this.scroller = scroller;
PageSwitchTime = TxtConfig.getPageSwitchDuration(readerContext.context);
}
protected int getWidth() {
return readerView.getWidth();
}
protected float getMoveDistance() {
return readerView.getMoveDistance();
}
protected int getHeight() {
return readerView.getHeight();
}
protected Bitmap getTopPage() {
return readerView.getTopPage();
}
protected Bitmap getBottomPage() {
return readerView.getBottomPage();
}
public ITextSelectDrawer getTextSelectDrawer() {
if (textSelectDrawer == null) {
textSelectDrawer = new NormalTextSelectDrawer();
}
return textSelectDrawer;
}
public void setTextSelectDrawer(ITextSelectDrawer textSelectDrawer) {
this.textSelectDrawer = textSelectDrawer;
}
}

View File

@ -0,0 +1,22 @@
package com.bifan.txtreaderlib.main;
/*
* create by bifan-wei
* 2017-11-13
*/
public class PageParam {
public int PaddingLeft = 20;
public int PaddingBottom = 0;
public int PaddingTop = 20;
public int PaddingRight = 20;
public int ParagraphMargin = 0;
public int VerticalLinePadding = 30;//横行距
public int HorizonalLinePadding = 30;//竖行距
public int LinePadding = 30 ;
public int PageLineNum = -1;
public float LineWidth = 0;
public float LineHeight = 0;
public float TextPadding = 5;
public int PageWidth = 0;
public int PageHeight = 0;
}

View File

@ -0,0 +1,28 @@
package com.bifan.txtreaderlib.main;
import android.graphics.Paint;
/*
* create by bifan-wei
* 2017-11-13
*/
public class PaintContext {
public Paint textPaint;
public Paint selectTextPaint;
public Paint sliderPaint;
public Paint notePaint;
public PaintContext() {
textPaint = new Paint();
selectTextPaint = new Paint();
notePaint = new Paint();
sliderPaint = new Paint();
}
public void onDestroy(){
textPaint = null;
selectTextPaint =null;
notePaint = null;
sliderPaint =null;
}
}

View File

@ -0,0 +1,98 @@
package com.bifan.txtreaderlib.main;
import com.bifan.txtreaderlib.interfaces.IParagraphData;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
/*
* create by bifan-wei
* 2017-11-13
*/
public class ParagraphData implements IParagraphData {
private int CharNum = 0;
private HashMap<Integer, String> paragraph = new HashMap<>();
private List<Integer> charIndex = new ArrayList<>();//每个段落开始字符位置
@Override
public void addParagraph(int ParaIndex, String p) {
if (p != null) {
paragraph.put(ParaIndex, p);
charIndex.add(CharNum);
CharNum += p.length();
}
}
@Override
public void addParagraph(String p) {
addParagraph(paragraph.size(), p);
}
@Override
public int getParagraphNum() {
return paragraph.size();
}
@Override
public int getCharNum() {
return CharNum;
}
@Override
public int findParagraphIndexByCharIndex(int CharIndex) {
int pre = 0;
int next = 0;
int index = 0;
for (Integer v : charIndex) {
if (index == 0) {
pre = v;
} else {
if (CharIndex >= pre && CharIndex < v) {
return index;
} else {
pre = next;
next = v;
}
}
index++;
}
return index;
}
@Override
public int getParaStartCharIndex(int ParaIndex) {
return charIndex.get(ParaIndex);
}
@Override
public String getParagraphStr(int ParaIndex) {
return paragraph.get(ParaIndex);
}
@Override
public String getParagraphStrFromStart(int ParaIndex, int CharIndex) {
String S = getParagraphStr(ParaIndex) + "";
if (CharIndex < 0) CharIndex = 0;
if (CharIndex > S.length()) CharIndex = S.length();
return S.substring(CharIndex);
}
@Override
public String getParagraphStrToEnd(int ParaIndex, int CharIndex) {
String S = getParagraphStr(ParaIndex) + "";
if (CharIndex < 0) CharIndex = 0;
if (CharIndex > S.length()) CharIndex = S.length();
return S.substring(0, CharIndex);
}
@Override
public void Clear() {
charIndex.clear();
paragraph.clear();
CharNum = 0;
}
}

View File

@ -0,0 +1,252 @@
package com.bifan.txtreaderlib.main;
import android.graphics.Canvas;
import android.graphics.Region;
import android.graphics.drawable.GradientDrawable;
import android.view.MotionEvent;
import android.widget.Scroller;
import com.bifan.txtreaderlib.interfaces.IReaderViewDrawer;
/**
* Created by bifan-wei
* on 2017/12/2.
*/
public class SerialPageDrawer extends PageDrawerBase implements IReaderViewDrawer {
private String tag = "SerialPageDrawer";
private final static int BorderShadowWith = 5;
public SerialPageDrawer(TxtReaderView readerView, TxtReaderContext readerContext, Scroller scroller) {
super(readerView, readerContext, scroller);
}
@Override
public void drawPageNextPageShadow(Canvas canvas) {
//绘制执行下一页的页边阴影
mPath.reset();
int left = (int) getMoveDistance() + getWidth();
int top = 0;
int right = left + BorderShadowWith;
int bottom = getHeight();
if (left > BorderShadowWith) {
getPageNextBorderDrawable().setBounds(left, top, right, bottom);
getPageNextBorderDrawable().draw(canvas);
}
}
@Override
public void drawPageNextBottomPage(Canvas canvas) {
//绘制执行下一页时的下面页部分
float startPosition = getWidth() + getMoveDistance();
mPath.reset();
mPath.moveTo(0, 0);
mPath.lineTo(getWidth(), 0);
mPath.lineTo(getWidth(), getHeight());
mPath.lineTo(0, getHeight());
mPath.lineTo(0, 0);
canvas.clipPath(mPath,Region.Op.INTERSECT);
canvas.drawBitmap(getBottomPage(), startPosition, 0, null);
}
@Override
public void drawPageNextTopPage(Canvas canvas) {
//绘制执行下一页时的上面页部分
mPath.reset();
mPath.moveTo(0, 0);
mPath.lineTo(getWidth() , 0);
mPath.lineTo(getWidth() , getHeight());
mPath.lineTo(0, getHeight());
mPath.lineTo(0, 0);
canvas.clipPath(mPath,Region.Op.INTERSECT);
canvas.drawBitmap(getTopPage(), getMoveDistance()+1, 0, null);
}
@Override
public void drawPagePrePageShadow(Canvas canvas) {
//绘制执行上一页的页边阴影
mPath.reset();
int left = (int) getMoveDistance() - BorderShadowWith;
int top = 0;
int right = (int) getMoveDistance();
int bottom = getHeight();
if (right < getWidth() - BorderShadowWith) {
getPagePreBorderDrawable().setBounds(left, top, right, bottom);
getPagePreBorderDrawable().draw(canvas);
}
}
@Override
public void drawPagePreBottomPage(Canvas canvas) {
//绘制执行上一页时的下面页部分
float startPosition = getMoveDistance();
float x = 0;
mPath.reset();
mPath.moveTo(x, 0);
mPath.lineTo(getWidth(), 0);
mPath.lineTo(getWidth(), getHeight());
mPath.lineTo(x, getHeight());
mPath.lineTo(x, 0);
canvas.clipPath(mPath, Region.Op.INTERSECT);
canvas.drawBitmap(getBottomPage(), startPosition-getWidth(), 0, null);
}
@Override
public void drawPagePreTopPage(Canvas canvas) {
//绘制执行上一页时的上面页部分
mPath.reset();
mPath.moveTo(0, 0);
mPath.lineTo(getWidth(), 0);
mPath.lineTo(getWidth(), getHeight());
mPath.lineTo(0, getHeight());
mPath.lineTo(0, 0);
canvas.clipPath(mPath, Region.Op.INTERSECT);
canvas.drawBitmap(getTopPage(), getMoveDistance(), 0, null);
}
private Boolean onPageStateBackAnimation = false;
@Override
public void startPageStateBackAnimation() {
if (readerView.isPagePre() || readerView.isPageNext()) {
onPageStateBackAnimation = true;
scroller.startScroll((int) readerView.mTouch.x, 0, -(int) getMoveDistance(), 0, PageSwitchTime);
postInvalidate();
}
}
@Override
public void startPageNextAnimation() {
scroller.startScroll(getWidth() + (int) getMoveDistance(), 0, -(getWidth() + (int) getMoveDistance()), 0, PageSwitchTime);
readerView.mDown.x = getWidth();//从getWidth()开始
readerView.CurrentMode = TxtReaderBaseView.Mode.PageNextIng;
postInvalidate();
}
@Override
public void startPagePreAnimation() {
scroller.startScroll((int) getMoveDistance(), 0, getWidth() - (int) getMoveDistance(), 0, PageSwitchTime);
readerView.mDown.x = 0;
readerView.CurrentMode = TxtReaderBaseView.Mode.PagePreIng;
postInvalidate();
}
@Override
public void onTextSelectMoveForward(MotionEvent event) {
}
@Override
public void onTextSelectMoveBack(MotionEvent event) {
}
@Override
public void onPageMove(MotionEvent event) {
readerView.mTouch.x = event.getX();
readerView.mTouch.y = event.getY();
postInvalidate();
}
private void postInvalidate() {
readerView.postInvalidate();
}
@Override
public void drawNote(Canvas canvas) {
}
@Override
public void drawSelectedText(Canvas canvas) {
if (readerView.CurrentMode == TxtReaderBaseView.Mode.PressSelectText) {
drawPressSelectedText(canvas);
} else if (readerView.CurrentMode == TxtReaderBaseView.Mode.SelectMoveBack) {
drawSelectedLinesText(canvas);
} else if (readerView.CurrentMode == TxtReaderBaseView.Mode.SelectMoveForward) {
drawSelectedLinesText(canvas);
}
}
private void drawSelectedLinesText(Canvas canvas) {
getTextSelectDrawer().drawSelectedLines(readerView.getCurrentSelectTextLine(), canvas, readerContext.getPaintContext().selectTextPaint);
//draw slider
drawSlider(canvas);
}
private void drawPressSelectedText(Canvas canvas) {
getTextSelectDrawer().drawSelectedChar(readerView.FirstSelectedChar, canvas, readerContext.getPaintContext().selectTextPaint);
//draw slider
drawSlider(canvas);
}
private void drawSlider(Canvas canvas) {
if (readerView.getLeftSliderPath() != null && readerView.getRightSliderPath() != null) {
canvas.drawPath(readerView.getLeftSliderPath(), readerContext.getPaintContext().sliderPaint);
canvas.drawPath(readerView.getRightSliderPath(), readerContext.getPaintContext().sliderPaint);
}
}
@Override
public void computeScroll() {
if (scroller.computeScrollOffset()) {
readerView.mTouch.x = scroller.getCurrX();
readerView.invalidate();
checkPageData();
}
}
private synchronized void checkPageData() {
if (onPageStateBackAnimation) {
if ((getMoveDistance()>0&&getMoveDistance() <= 3) || (getMoveDistance()<0&&getMoveDistance() >= -3) ) {
scroller.abortAnimation();
readerView.releaseTouch();
readerView.invalidate();
onPageStateBackAnimation = false;
}
} else {
if (readerView.mTouch.x == 0)//执行下一页数据获取
{
readerView.doPageNextDone();
scroller.abortAnimation();
} else if (readerView.mTouch.x == getWidth()) {//执行上一页数据获取
readerView.doPagePreDone();
scroller.abortAnimation();
}
}
}
private GradientDrawable mPagePreBorderDrawable;
private GradientDrawable mPageNextBorderDrawable;
private GradientDrawable getPagePreBorderDrawable() {
if (mPagePreBorderDrawable == null) {
int[] color = new int[]{0x55666666, 0x55666666, 0x55666666};
mPagePreBorderDrawable = new GradientDrawable(GradientDrawable.Orientation.RIGHT_LEFT, color);
}
return mPagePreBorderDrawable;
}
private GradientDrawable getPageNextBorderDrawable() {
if (mPageNextBorderDrawable == null) {
int[] color = new int[]{0x55666666, 0x55666666, 0x55666666};
mPageNextBorderDrawable = new GradientDrawable(GradientDrawable.Orientation.LEFT_RIGHT, color);
}
return mPageNextBorderDrawable;
}
}

View File

@ -0,0 +1,253 @@
package com.bifan.txtreaderlib.main;
import android.graphics.Canvas;
import android.graphics.Region;
import android.graphics.drawable.GradientDrawable;
import android.view.MotionEvent;
import android.widget.Scroller;
import com.bifan.txtreaderlib.interfaces.IReaderViewDrawer;
/**
* Created by bifan-wei
* on 2017/12/2.
*/
public class ShearPageDrawer extends PageDrawerBase implements IReaderViewDrawer {
private String tag = "SerialPageDrawer";
private final static int BorderShadowWith = 5;
public ShearPageDrawer(TxtReaderView readerView, TxtReaderContext readerContext, Scroller scroller) {
super(readerView, readerContext, scroller);
}
@Override
public void drawPageNextPageShadow(Canvas canvas) {
//绘制执行下一页的页边阴影
mPath.reset();
int left = (int) getMoveDistance() + getWidth();
int top = 0;
int right = left + BorderShadowWith;
int bottom = getHeight();
if (left > BorderShadowWith) {
getPageNextBorderDrawable().setBounds(left, top, right, bottom);
getPageNextBorderDrawable().draw(canvas);
}
}
@Override
public void drawPageNextBottomPage(Canvas canvas) {
//绘制执行下一页时的下面页部分
float startPosition = getWidth() + getMoveDistance();
mPath.reset();
mPath.moveTo(0, 0);
mPath.lineTo(getWidth(), 0);
mPath.lineTo(getWidth(), getHeight());
mPath.lineTo(0, getHeight());
mPath.lineTo(0, 0);
canvas.clipPath(mPath,Region.Op.INTERSECT);
canvas.drawBitmap(getBottomPage(), startPosition, startPosition, null);
}
@Override
public void drawPageNextTopPage(Canvas canvas) {
//绘制执行下一页时的上面页部分
mPath.reset();
mPath.moveTo(0, 0);
mPath.lineTo(getWidth() , 0);
mPath.lineTo(getWidth() , getHeight());
mPath.lineTo(0, getHeight());
mPath.lineTo(0, 0);
canvas.clipPath(mPath,Region.Op.INTERSECT);
canvas.drawBitmap(getTopPage(), getMoveDistance()+1, 0, null);
}
@Override
public void drawPagePrePageShadow(Canvas canvas) {
//绘制执行上一页的页边阴影
mPath.reset();
int left = (int) getMoveDistance() - BorderShadowWith;
int top = 0;
int right = (int) getMoveDistance();
int bottom = getHeight();
if (right < getWidth() - BorderShadowWith) {
getPagePreBorderDrawable().setBounds(left, top, right, bottom);
getPagePreBorderDrawable().draw(canvas);
}
}
@Override
public void drawPagePreBottomPage(Canvas canvas) {
//绘制执行上一页时的下面页部分
float startPosition = getMoveDistance();
float x = 0;
mPath.reset();
mPath.moveTo(x, 0);
mPath.lineTo(getWidth(), 0);
mPath.lineTo(getWidth(), getHeight());
mPath.lineTo(x, getHeight());
mPath.lineTo(x, 0);
canvas.clipPath(mPath, Region.Op.INTERSECT);
float y = startPosition-getWidth();
canvas.drawBitmap(getBottomPage(), y, y, null);
}
@Override
public void drawPagePreTopPage(Canvas canvas) {
//绘制执行上一页时的上面页部分
mPath.reset();
mPath.moveTo(0, 0);
mPath.lineTo(getWidth(), 0);
mPath.lineTo(getWidth(), getHeight());
mPath.lineTo(0, getHeight());
mPath.lineTo(0, 0);
canvas.clipPath(mPath, Region.Op.INTERSECT);
canvas.drawBitmap(getTopPage(), getMoveDistance(), 0, null);
}
private Boolean onPageStateBackAnimation = false;
@Override
public void startPageStateBackAnimation() {
if (readerView.isPagePre() || readerView.isPageNext()) {
onPageStateBackAnimation = true;
scroller.startScroll((int) readerView.mTouch.x, 0, -(int) getMoveDistance(), 0, PageSwitchTime);
postInvalidate();
}
}
@Override
public void startPageNextAnimation() {
scroller.startScroll(getWidth() + (int) getMoveDistance(), 0, -(getWidth() + (int) getMoveDistance()), 0, PageSwitchTime);
readerView.mDown.x = getWidth();//从getWidth()开始
readerView.CurrentMode = TxtReaderBaseView.Mode.PageNextIng;
postInvalidate();
}
@Override
public void startPagePreAnimation() {
scroller.startScroll((int) getMoveDistance(), 0, getWidth() - (int) getMoveDistance(), 0, PageSwitchTime);
readerView.mDown.x = 0;
readerView.CurrentMode = TxtReaderBaseView.Mode.PagePreIng;
postInvalidate();
}
@Override
public void onTextSelectMoveForward(MotionEvent event) {
}
@Override
public void onTextSelectMoveBack(MotionEvent event) {
}
@Override
public void onPageMove(MotionEvent event) {
readerView.mTouch.x = event.getX();
readerView.mTouch.y = event.getY();
postInvalidate();
}
private void postInvalidate() {
readerView.postInvalidate();
}
@Override
public void drawNote(Canvas canvas) {
}
@Override
public void drawSelectedText(Canvas canvas) {
if (readerView.CurrentMode == TxtReaderBaseView.Mode.PressSelectText) {
drawPressSelectedText(canvas);
} else if (readerView.CurrentMode == TxtReaderBaseView.Mode.SelectMoveBack) {
drawSelectedLinesText(canvas);
} else if (readerView.CurrentMode == TxtReaderBaseView.Mode.SelectMoveForward) {
drawSelectedLinesText(canvas);
}
}
private void drawSelectedLinesText(Canvas canvas) {
getTextSelectDrawer().drawSelectedLines(readerView.getCurrentSelectTextLine(), canvas, readerContext.getPaintContext().selectTextPaint);
//draw slider
drawSlider(canvas);
}
private void drawPressSelectedText(Canvas canvas) {
getTextSelectDrawer().drawSelectedChar(readerView.FirstSelectedChar, canvas, readerContext.getPaintContext().selectTextPaint);
//draw slider
drawSlider(canvas);
}
private void drawSlider(Canvas canvas) {
if (readerView.getLeftSliderPath() != null && readerView.getRightSliderPath() != null) {
canvas.drawPath(readerView.getLeftSliderPath(), readerContext.getPaintContext().sliderPaint);
canvas.drawPath(readerView.getRightSliderPath(), readerContext.getPaintContext().sliderPaint);
}
}
@Override
public void computeScroll() {
if (scroller.computeScrollOffset()) {
readerView.mTouch.x = scroller.getCurrX();
readerView.invalidate();
checkPageData();
}
}
private synchronized void checkPageData() {
if (onPageStateBackAnimation) {
if ((getMoveDistance()>0&&getMoveDistance() <= 3) || (getMoveDistance()<0&&getMoveDistance() >= -3) ) {
scroller.abortAnimation();
readerView.releaseTouch();
readerView.invalidate();
onPageStateBackAnimation = false;
}
} else {
if (readerView.mTouch.x == 0)//执行下一页数据获取
{
readerView.doPageNextDone();
scroller.abortAnimation();
} else if (readerView.mTouch.x == getWidth()) {//执行上一页数据获取
readerView.doPagePreDone();
scroller.abortAnimation();
}
}
}
private GradientDrawable mPagePreBorderDrawable;
private GradientDrawable mPageNextBorderDrawable;
private GradientDrawable getPagePreBorderDrawable() {
if (mPagePreBorderDrawable == null) {
int[] color = new int[]{0x55666666, 0x55666666, 0x55666666};
mPagePreBorderDrawable = new GradientDrawable(GradientDrawable.Orientation.RIGHT_LEFT, color);
}
return mPagePreBorderDrawable;
}
private GradientDrawable getPageNextBorderDrawable() {
if (mPageNextBorderDrawable == null) {
int[] color = new int[]{0x55666666, 0x55666666, 0x55666666};
mPageNextBorderDrawable = new GradientDrawable(GradientDrawable.Orientation.LEFT_RIGHT, color);
}
return mPageNextBorderDrawable;
}
}

View File

@ -0,0 +1,266 @@
package com.bifan.txtreaderlib.main;
import android.content.Context;
import android.content.SharedPreferences;
import android.graphics.Color;
/**
* Created by HP on 2017/11/26.
*/
public class TxtConfig {
public static final String SAVE_NAME = "TxtConfig";
public static final String C_TEXT_SIZE = "TEXT_SIZE ";
public static final String C_TEXT_COLOR = "TEXT_COLOR";
public static final String C_NOTE_TEXT_COLOR = "TEXT_COLOR";
public static final String C_SLIDER_COLOR = "SLIDER_COLOR";
public static final String C_SELECT_TEXT_COLOR = "SELECTED_TEXT_COLOR";
public static final String C_BACKGROUND_COLOR = "BACKGROUND_COLOR";
public static final String C_IS_SHOW_NOTE = "IS_SHOW_NOTE";
public static final String C_CAN_PRESS_SELECT = "CAN_PRESS_SELECT";
public static final String C_BOLD = "BOLD ";
public static final String C_SHOW_SPECIAL_CHAR = "SHOW_SPECIAL_CHAR ";
public static final String C_CENTER_CLICK_AREA = "CENTER_CLICK_AREA";
public static final String C_PAGE_SWITCH_DURATION = "PAGE_SWITCH_DURATION";
public static final String C_PAGE_VERTICAL_MODE = "PAGE_VERTICAL_MODE ";
public static final String C_PAGE_SWITCH_TYPE_MODE = "PAGE_SWITCH_SYPE_MODE ";
public static final int PAGE_SWITCH_MODE_COVER = 1;//in px
public static final int PAGE_SWITCH_MODE_SERIAL = 2;//in px
public static final int PAGE_SWITCH_MODE_SHEAR = 3;//in px
public static int Page_PaddingLeft = 20;//in px
public static int Page_PaddingBottom = 20;//in px
public static int Page_PaddingTop = 20;//in px
public static int Page_PaddingRight = 20;//in px
public static int Page_LinePadding = 30;//in px
public static int Page_Paragraph_margin = 20;//in px,为0没有间距
public int Page_Switch_Mode = PAGE_SWITCH_MODE_COVER;
public static int MAX_TEXT_SIZE = 150;//in px
public static int MIN_TEXT_SIZE = 50;//in px
public static int DEFAULT_SELECT_TEXT_COLOR = Color.parseColor("#44f6950b");
public static int DEFAULT_SLIDER_COLOR = Color.parseColor("#1f4cf5");
public int textSize = MIN_TEXT_SIZE;//字体大小
public int textColor = Color.BLACK;//字体颜色
public int backgroundColor = Color.WHITE;//背景颜色
public int NoteColor = Color.RED;//笔记颜色
public int SelectTextColor = DEFAULT_SELECT_TEXT_COLOR;//选中颜色
public int SliderColor = DEFAULT_SLIDER_COLOR;//滑动条颜色
public static boolean DebugMode = true;//debug模式会进行打印日志默认是开启
public Boolean showNote = true;//是否显示笔记
public Boolean canPressSelect = true;//是否能长按选中
public Boolean VerticalPageMode = false;
public Boolean Bold = false;//是否加粗
public Boolean ShowSpecialChar = true;//是否显示特殊符号对于数字英文可以显示特定颜色
public float CenterClickArea = 0.35f;//0~1,中间点击区域占View宽度的百分比区域为高为宽两倍的矩形如果为1f说明点击翻页将不起效果
public int PageSwitchDuration = 400;//页面滑动时间间隔毫秒建议不要低于200
public static SharedPreferences getS(Context context) {
SharedPreferences share = context.getSharedPreferences(SAVE_NAME, Context.MODE_PRIVATE);
return share;
}
public static int getPageSwitchMode(Context context) {
SharedPreferences share = getS(context);
int PageSwitchMode = share.getInt(C_PAGE_SWITCH_TYPE_MODE, PAGE_SWITCH_MODE_COVER);
if(PageSwitchMode!=PAGE_SWITCH_MODE_COVER
&&PageSwitchMode!= PAGE_SWITCH_MODE_SERIAL
&&PageSwitchMode!= PAGE_SWITCH_MODE_SHEAR){
return PAGE_SWITCH_MODE_COVER;
}
return PageSwitchMode;
}
/**
* @param PageSwitchMode PAGE_SWITCH_MODE_COVERPAGE_SWITCH_MODE_SERIALPAGE_SWITCH__MODE_SHEAR
*/
public static void savePageSwitchMode(Context context,int PageSwitchMode) {
SharedPreferences share = getS(context);
SharedPreferences.Editor editor = share.edit();
editor.putInt(C_PAGE_SWITCH_TYPE_MODE, PageSwitchMode);
editor.apply();
}
/**
* @param context
* @param duration 不能低于100建议200以上
*/
public static void savePageSwitchDuration(Context context, int duration) {
duration = Math.max(duration, 100);
SharedPreferences share = getS(context);
SharedPreferences.Editor editor = share.edit();
editor.putInt(C_PAGE_SWITCH_DURATION, duration);
editor.apply();
}
public static int getPageSwitchDuration(Context context) {
SharedPreferences share = getS(context);
return share.getInt(C_PAGE_SWITCH_DURATION, 400);
}
public static void saveTextSize(Context context, int textSize) {
textSize = Math.max(textSize, MIN_TEXT_SIZE);
textSize = Math.min(textSize, MAX_TEXT_SIZE);
SharedPreferences share = getS(context);
SharedPreferences.Editor editor = share.edit();
editor.putInt(C_TEXT_SIZE, textSize);
editor.apply();
}
public static int getTextSize(Context context) {
SharedPreferences share = getS(context);
return share.getInt(C_TEXT_SIZE, MIN_TEXT_SIZE);
}
public static void saveTextColor(Context context, int textColor) {
SharedPreferences share = getS(context);
SharedPreferences.Editor editor = share.edit();
editor.putInt(C_TEXT_COLOR, textColor);
editor.apply();
editor.commit();
}
public static int getTextColor(Context context) {
SharedPreferences share = getS(context);
return share.getInt(C_TEXT_COLOR, Color.BLACK);
}
public static void saveNoteTextColor(Context context, int textColor) {
SharedPreferences share = getS(context);
SharedPreferences.Editor editor = share.edit();
editor.putInt(C_NOTE_TEXT_COLOR, textColor);
editor.apply();
editor.commit();
}
public static int getNoteTextColor(Context context) {
SharedPreferences share = getS(context);
return share.getInt(C_NOTE_TEXT_COLOR, Color.BLACK);
}
public static void saveSelectTextColor(Context context, int textColor) {
SharedPreferences share = getS(context);
SharedPreferences.Editor editor = share.edit();
editor.putInt(C_SELECT_TEXT_COLOR, textColor);
editor.apply();
editor.commit();
}
public static int getSelectTextColor(Context context) {
SharedPreferences share = getS(context);
return share.getInt(C_SELECT_TEXT_COLOR, DEFAULT_SELECT_TEXT_COLOR);
}
public static void saveBackgroundColor(Context context, int BackgroundColor) {
SharedPreferences share = getS(context);
SharedPreferences.Editor editor = share.edit();
editor.putInt(C_BACKGROUND_COLOR, BackgroundColor);
editor.apply();
editor.commit();
}
public static int getBackgroundColor(Context context) {
SharedPreferences share = getS(context);
return share.getInt(C_BACKGROUND_COLOR, Color.WHITE);
}
public static void saveCenterClickArea(Context context, float CenterClickArea) {
SharedPreferences share = getS(context);
SharedPreferences.Editor editor = share.edit();
editor.putFloat(C_CENTER_CLICK_AREA, CenterClickArea);
editor.apply();
editor.commit();
}
public static float getCenterClickArea(Context context) {
SharedPreferences share = getS(context);
return share.getFloat(C_CENTER_CLICK_AREA, 0.3f);
}
public static void saveSliderColor(Context context, float CenterClickArea) {
SharedPreferences share = getS(context);
SharedPreferences.Editor editor = share.edit();
editor.putFloat(C_SLIDER_COLOR, CenterClickArea);
editor.apply();
editor.commit();
}
public static int getSliderColor(Context context) {
SharedPreferences share = getS(context);
return share.getInt(C_SLIDER_COLOR, DEFAULT_SLIDER_COLOR);
}
public static void saveIsShowNote(Context context, Boolean IsShowNote) {
SharedPreferences share = getS(context);
SharedPreferences.Editor editor = share.edit();
editor.putBoolean(C_IS_SHOW_NOTE, IsShowNote);
editor.apply();
editor.commit();
}
public static Boolean getIsShowNote(Context context) {
SharedPreferences share = getS(context);
return share.getBoolean(C_IS_SHOW_NOTE, true);
}
public static void saveCanPressSelect(Context context, Boolean CanPressSelect) {
SharedPreferences share = getS(context);
SharedPreferences.Editor editor = share.edit();
editor.putBoolean(C_CAN_PRESS_SELECT, CanPressSelect);
editor.apply();
editor.commit();
}
public static Boolean getCanPressSelect(Context context) {
SharedPreferences share = getS(context);
return share.getBoolean(C_CAN_PRESS_SELECT, true);
}
public static void saveIsBold(Context context, Boolean bold) {
SharedPreferences share = getS(context);
SharedPreferences.Editor editor = share.edit();
editor.putBoolean(C_BOLD, bold);
editor.apply();
editor.commit();
}
public static Boolean isBold(Context context) {
SharedPreferences share = getS(context);
return share.getBoolean(C_BOLD, false);
}
public static void saveIsShowSpecialChar(Context context, Boolean showSpecialChar) {
SharedPreferences share = getS(context);
SharedPreferences.Editor editor = share.edit();
editor.putBoolean(C_SHOW_SPECIAL_CHAR, showSpecialChar);
editor.apply();
editor.commit();
}
public static Boolean IsShowSpecialChar(Context context) {
SharedPreferences share = getS(context);
return share.getBoolean(C_SHOW_SPECIAL_CHAR, true);
}
public static void saveIsOnVerticalPageMode(Context context, Boolean IsOnVerticalPageMode) {
SharedPreferences share = getS(context);
SharedPreferences.Editor editor = share.edit();
editor.putBoolean(C_PAGE_VERTICAL_MODE, IsOnVerticalPageMode);
editor.apply();
editor.commit();
}
public static Boolean IsOnVerticalPageMode(Context context) {
SharedPreferences share = getS(context);
return share.getBoolean(C_PAGE_VERTICAL_MODE, false);
}
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,156 @@
package com.bifan.txtreaderlib.main;
import android.content.Context;
import com.bifan.txtreaderlib.bean.TxtFileMsg;
import com.bifan.txtreaderlib.interfaces.IChapter;
import com.bifan.txtreaderlib.interfaces.IChapterMatcher;
import com.bifan.txtreaderlib.interfaces.IPageDataPipeline;
import com.bifan.txtreaderlib.interfaces.IParagraphData;
import java.util.List;
/*
* create by bifan-wei
* 2017-11-13
*/
public class TxtReaderContext {
public Context context;
private IParagraphData paragraphData;
private PageParam pageParam;
private TxtFileMsg fileMsg;
private List<IChapter> chapters;
private IPageDataPipeline pageDataPipeline;
private PaintContext paintContext;
private TxtConfig txtConfig;
private Boolean InitDone = false;
private IChapterMatcher chapterMatcher;
private final BitmapData bitmapData = new BitmapData();
private final PageData pageData = new PageData();
public TxtReaderContext(Context context) {
this.context = context;
}
public TxtFileMsg getFileMsg() {
return fileMsg;
}
public void setFileMsg(TxtFileMsg fileMsg) {
this.fileMsg = fileMsg;
}
public void setParagraphData(IParagraphData paragraphData) {
this.paragraphData = paragraphData;
}
public IParagraphData getParagraphData() {
return paragraphData;
}
public PageParam getPageParam() {
return pageParam;
}
public void setPageParam(PageParam pageParam) {
this.pageParam = pageParam;
}
public Context getContext() {
return context;
}
public List<IChapter> getChapters() {
return chapters;
}
public void setContext(Context context) {
this.context = context;
}
public void setChapters(List<IChapter> chapters) {
this.chapters = chapters;
}
public IChapterMatcher getChapterMatcher() {
return chapterMatcher;
}
public void setChapterMatcher(IChapterMatcher chapterMatcher) {
this.chapterMatcher = chapterMatcher;
}
public PageData getPageData() {
return pageData;
}
public IPageDataPipeline getPageDataPipeline() {
if (pageDataPipeline == null) {
if (getTxtConfig().VerticalPageMode) {
pageDataPipeline = new VerticalPageDataPipeline(this);
}else{
pageDataPipeline = new PageDataPipeline(this);
}
}
return pageDataPipeline;
}
public PaintContext getPaintContext() {
if (paintContext == null) {
paintContext = new PaintContext();
}
return paintContext;
}
public void setPaintContext(PaintContext paintContext) {
this.paintContext = paintContext;
}
public void setPageDataPipeline(IPageDataPipeline pageDataPipeline) {
this.pageDataPipeline = pageDataPipeline;
}
public TxtConfig getTxtConfig() {
if (txtConfig == null) {
txtConfig = new TxtConfig();
}
return txtConfig;
}
public void setTxtConfig(TxtConfig txtConfig) {
this.txtConfig = txtConfig;
}
public BitmapData getBitmapData() {
return bitmapData;
}
public Boolean InitDone() {
return InitDone;
}
public void setInitDone(Boolean initDone) {
InitDone = initDone;
}
public void Clear() {
if (paragraphData != null) {
paragraphData.Clear();
paragraphData = null;
}
if (paintContext != null) {
paintContext.onDestroy();
paintContext = null;
}
if (chapters != null) {
chapters.clear();
chapters = null;
}
bitmapData.onDestroy();
pageData.onDestroy();
chapterMatcher = null;
}
}

View File

@ -0,0 +1,616 @@
package com.bifan.txtreaderlib.main;
import android.content.Context;
import android.graphics.Canvas;
import android.util.AttributeSet;
import android.view.MotionEvent;
import com.bifan.txtreaderlib.bean.FileReadRecordBean;
import com.bifan.txtreaderlib.bean.TxtChar;
import com.bifan.txtreaderlib.bean.TxtFileMsg;
import com.bifan.txtreaderlib.interfaces.IChapter;
import com.bifan.txtreaderlib.interfaces.ILoadListener;
import com.bifan.txtreaderlib.interfaces.IPage;
import com.bifan.txtreaderlib.interfaces.IReaderViewDrawer;
import com.bifan.txtreaderlib.interfaces.ITextSelectListener;
import com.bifan.txtreaderlib.interfaces.ITxtTask;
import com.bifan.txtreaderlib.tasks.DrawPrepareTask;
import com.bifan.txtreaderlib.tasks.TxtConfigInitTask;
import com.bifan.txtreaderlib.tasks.TxtPageLoadTask;
import com.bifan.txtreaderlib.utils.ELogger;
import com.bifan.txtreaderlib.utils.FileUtil;
import com.bifan.txtreaderlib.utils.TxtBitmapUtil;
import java.io.File;
import java.util.List;
import androidx.annotation.Nullable;
/**
* Created by bifa-wei
* on 2017/11/28.
*/
public class TxtReaderView extends TxtReaderBaseView {
private String tag = "TxtReaderView";
public TxtReaderView(Context context) {
super(context);
}
public TxtReaderView(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
}
private IReaderViewDrawer drawer = null;
@Override
protected void init() {
super.init();
}
@Override
protected void drawLineText(Canvas canvas) {
if (isPagePre() || isPageNext()) {
if (isPagePre()) {
//当前是第一页只显示第一页
if (isFirstPage()) {
if (getTopPage() != null) {
canvas.drawBitmap(getTopPage(), 0, 0, null);
}
} else {
//绘制上一页
if (getTopPage() != null) {
drawPagePreTopPage(canvas);
}
//绘制下一页
if (getBottomPage() != null) {
drawPagePreBottomPage(canvas);
}
//绘制阴影线
drawPagePrePageShadow(canvas);
}
} else {
if (getTopPage() != null) {
drawPageNextTopPage(canvas);
}
if (getBottomPage() != null) {
drawPageNextBottomPage(canvas);
}
drawPageNextPageShadow(canvas);
}
} else {
//说明没有触碰移动屏幕
if (getTopPage() != null) {
canvas.drawBitmap(getTopPage(), 0, 0, null);
}
}
}
private void drawPageNextPageShadow(Canvas canvas) {
getDrawer().drawPageNextPageShadow(canvas);
}
private void drawPageNextBottomPage(Canvas canvas) {
//绘制执行下一页时的下面页部分
getDrawer().drawPageNextBottomPage(canvas);
}
private void drawPageNextTopPage(Canvas canvas) {
//绘制执行下一页时的上面页部分
getDrawer().drawPageNextTopPage(canvas);
}
private void drawPagePrePageShadow(Canvas canvas) {
//绘制执行上一页的页边阴影
getDrawer().drawPagePrePageShadow(canvas);
}
private void drawPagePreBottomPage(Canvas canvas) {
//绘制执行上一页时的下面页部分
getDrawer().drawPagePreBottomPage(canvas);
}
private void drawPagePreTopPage(Canvas canvas) {
//绘制执行上一页时的上面页部分
getDrawer().drawPagePreTopPage(canvas);
}
@Override
protected void startPageStateBackAnimation() {
getDrawer().startPageStateBackAnimation();
}
@Override
protected void startPageNextAnimation() {
getDrawer().startPageNextAnimation();
}
@Override
protected void startPagePreAnimation() {
getDrawer().startPagePreAnimation();
}
@Override
protected void onPageMove(MotionEvent event) {
mTouch.x = event.getX();
mTouch.y = event.getY();
checkMoveState();
if (getMoveDistance() > 0 && isFirstPage()) {
ELogger.log(tag, "是第一页了");
return;
}
if (getMoveDistance() < 0 && isLastPage()) {
ELogger.log(tag, "是最后一页了");
return;
}
invalidate();
}
protected void onActionUp(MotionEvent event) {
super.onActionUp(event);
if (CurrentMode == Mode.SelectMoveBack) {
if (textSelectListener != null) {
textSelectListener.onTextSelected(getCurrentSelectedText());
}
} else if (CurrentMode == Mode.SelectMoveForward) {
if (textSelectListener != null) {
textSelectListener.onTextSelected(getCurrentSelectedText());
}
}
}
@Override
protected void drawNote(Canvas canvas) {
getDrawer().drawNote(canvas);
}
@Override
protected void drawSelectedText(Canvas canvas) {
getDrawer().drawSelectedText(canvas);
}
@Override
protected void onTextSelectMoveForward(MotionEvent event) {
getDrawer().onTextSelectMoveForward(event);
if (textSelectListener != null) {
textSelectListener.onTextChanging(FirstSelectedChar, LastSelectedChar);
textSelectListener.onTextChanging(getCurrentSelectedText());
}
}
@Override
protected void onTextSelectMoveBack(MotionEvent event) {
getDrawer().onTextSelectMoveBack(event);
if (textSelectListener != null) {
textSelectListener.onTextChanging(FirstSelectedChar, LastSelectedChar);
textSelectListener.onTextChanging(getCurrentSelectedText());
}
}
@Override
public void computeScroll() {
super.computeScroll();
getDrawer().computeScroll();
}
private IReaderViewDrawer getDrawer() {
if (drawer == null) {
int pageSwitchMode = readerContext.getTxtConfig().Page_Switch_Mode;
switch (pageSwitchMode) {
case TxtConfig.PAGE_SWITCH_MODE_SERIAL:
drawer = new SerialPageDrawer(this, readerContext, mScroller);
break;
case TxtConfig.PAGE_SWITCH_MODE_SHEAR:
drawer = new ShearPageDrawer(this, readerContext, mScroller);
break;
default:
drawer = new NormalPageDrawer(this, readerContext, mScroller);
}
}
return drawer;
}
private ITextSelectListener textSelectListener;
public void setOnTextSelectListener(ITextSelectListener textSelectListener) {
this.textSelectListener = textSelectListener;
}
public TxtReaderContext getTxtReaderContext() {
return readerContext;
}
private void Reload() {
saveProgress();
readerContext.getPageData().refreshTag[0] = 1;
readerContext.getPageData().refreshTag[1] = 1;
readerContext.getPageData().refreshTag[2] = 1;
TxtConfigInitTask configInitTask = new TxtConfigInitTask();
configInitTask.Run(actionLoadListener, readerContext);
}
/**
* 设置字体大小
*
* @param textSize min 25 max 70 in px
*/
public void setTextSize(int textSize) {
readerContext.getTxtConfig().saveTextSize(getContext(), textSize);
if (getWidth() > 0) {
Reload();
}
}
/**
* 获取字体大小 in px
*
* @return
*/
public int getTextSize() {
return readerContext.getTxtConfig().getTextSize(getContext());
}
/**
* 获取背景颜色
*
* @return
*/
public int getBackgroundColor() {
return readerContext.getTxtConfig().getBackgroundColor(getContext());
}
/**
* 设置样式
*
* @param backgroundColor
* @param textColor
*/
public void setStyle(int backgroundColor, int textColor) {
saveProgress();
TxtConfig.saveTextColor(getContext(), textColor);
TxtConfig.saveBackgroundColor(getContext(), backgroundColor);
if (getWidth() > 0) {
readerContext.getTxtConfig().textColor = textColor;
readerContext.getTxtConfig().backgroundColor = backgroundColor;
if (readerContext.getBitmapData().getBgBitmap() != null) {
readerContext.getBitmapData().getBgBitmap().recycle();
}
int width = readerContext.getPageParam().PageWidth;
int height = readerContext.getPageParam().PageHeight;
readerContext.getBitmapData().setBgBitmap(TxtBitmapUtil.createBitmap(backgroundColor, width, height));
refreshCurrentView();
}
}
/**
* 从指定进度加载
*
* @param progress 0~100
*/
public void loadFromProgress(float progress) {
if (readerContext == null || readerContext.getParagraphData() == null) return;
if (progress < 0) progress = 0;
if (progress > 100) progress = 100;
float p = progress / 100;
int charNum = readerContext.getParagraphData().getCharNum();
int charIndex = (int) (p * charNum);
int paragraphNum = readerContext.getParagraphData().getParagraphNum();
int paragraphIndex = readerContext.getParagraphData().findParagraphIndexByCharIndex(charIndex);
if (progress == 100 || paragraphIndex >= paragraphNum) {
paragraphIndex = paragraphNum - 1;
}
if (paragraphIndex < 0) {
paragraphIndex = 0;
}
ELogger.log(tag, "loadFromProgress ,progress:" + progress + "/paragraphIndex:" + paragraphIndex + "/paragraphNum:" + paragraphNum);
loadFromProgress(paragraphIndex, 0);
}
/**
* 根据字符的准确位置跳转进度
*
* @param charIndex
* @param paragraphIndex
*/
public void loadFromProgress(final int paragraphIndex, final int charIndex) {
refreshTag(1, 1, 1);
TxtPageLoadTask txtPageLoadTask = new TxtPageLoadTask(paragraphIndex, charIndex);
txtPageLoadTask.Run(new LoadListenerAdapter() {
@Override
public void onSuccess() {
if (readerContext != null) {
checkMoveState();
postInvalidate();
post(new Runnable() {
@Override
public void run() {
onProgressCallBack(getProgress(paragraphIndex, charIndex));
tryFetchFirstPage();
}
});
}
}
}, readerContext);
}
private void tryFetchFirstPage() {
IPage midPage = readerContext.getPageData().MidPage();
IPage firstPage = null;
onPageProgress(midPage);
if (midPage != null && midPage.HasData()) {
if (midPage.getFirstChar().ParagraphIndex == 0 && midPage.getFirstChar().CharIndex == 0) {
} else {
firstPage = readerContext.getPageDataPipeline().getPageEndToProgress(midPage.getFirstChar().ParagraphIndex, midPage.getFirstChar().CharIndex);
}
}
if (firstPage != null && firstPage.HasData()) {
if (!firstPage.isFullPage()) {
//说明是开始数据重新刷新界面
refreshTag(1, 1, 1);
loadFromProgress(0, 0);
} else {
refreshTag(1, 0, 0);
readerContext.getPageData().setFirstPage(firstPage);
ITxtTask task = new DrawPrepareTask();
task.Run(actionLoadListener, readerContext);//获取上一页数据
}
}
}
/**
* 获取当前页章节
*
* @return 没有章节返回null, 当前页没有数据返回null
*/
public IChapter getCurrentChapter() {
List<IChapter> chapters = readerContext.getChapters();
IPage midPage = readerContext.getPageData().MidPage();
if (chapters == null || chapters.size() == 0 || midPage == null || !midPage.HasData()) {
return null;
}
IChapter lastChapter = readerContext.getChapters().get(readerContext.getChapters().size() - 1);
int currentP = midPage.getFirstChar().ParagraphIndex;
int lastP = lastChapter.getStartParagraphIndex();
int pre = 0;
int next = 0;
int index = 1;
for (int i = 0; i < chapters.size(); i++) {
int startP = chapters.get(i).getStartParagraphIndex();
if (i == 0) {
pre = startP;
} else {
next = startP;
if (currentP >= pre && currentP < next) {
index = i;
break;
} else {
pre = next;
}
}
}
if (currentP >= lastP) {
return lastChapter;
} else {
return chapters.get(index - 1);
}
}
/**
* 跳转到上一章
*
* @return 是否成功
*/
public Boolean jumpToPreChapter() {
IChapter currentChapter = getCurrentChapter();
List<IChapter> chapters = getChapters();
if (chapters == null || currentChapter == null) {
ELogger.log(tag, "jumpToPreChapter false chapters == null or currentChapter == null");
return false;
}
int index = currentChapter.getIndex();
if (index == 0 || chapters.size() == 0) {
ELogger.log(tag, "jumpToPreChapter false index == 0 or chapters.size() == 0");
return false;
}
refreshTag(1, 1, 1);
IChapter preChapter = chapters.get(index - 1);
loadFromProgress(preChapter.getStartParagraphIndex(), 0);
return true;
}
/**
* 跳转到下一章
*
* @return 是否成功
*/
public Boolean jumpToNextChapter() {
IChapter currentChapter = getCurrentChapter();
List<IChapter> chapters = getChapters();
if (chapters == null || currentChapter == null) {
ELogger.log(tag, "jumpToNextChapter false chapters == null or currentChapter == null");
return false;
}
int index = currentChapter.getIndex();
if (index >= chapters.size() - 1 || chapters.size() == 0) {
ELogger.log(tag, "jumpToNextChapter false < chapters.size() - 1 or chapters.size() == 0");
return false;
}
refreshTag(1, 1, 1);
IChapter nextChapter = chapters.get(index + 1);
loadFromProgress(nextChapter.getStartParagraphIndex(), 0);
return true;
}
/**
* 保存当前进度
*/
public void saveProgress() {
IPage currentPage = readerContext.getPageData().MidPage();
if (currentPage != null && currentPage.HasData() && readerContext.getFileMsg() != null) {
TxtChar firstChar = currentPage.getFirstChar();
readerContext.getFileMsg().PreParagraphIndex = firstChar.ParagraphIndex;
readerContext.getFileMsg().PreCharIndex = firstChar.CharIndex;
readerContext.getFileMsg().CurrentParagraphIndex = firstChar.ParagraphIndex;
readerContext.getFileMsg().CurrentCharIndex = firstChar.CharIndex;
}
}
/**
* @param isBold 字体否加粗
*/
public void setTextBold(boolean isBold) {
TxtConfig.saveIsBold(getContext(), isBold);
getTxtReaderContext().getTxtConfig().Bold = isBold;
refreshCurrentView();
}
/**
* 平移切换页面
*/
public void setPageSwitchByTranslate() {
TxtConfig.savePageSwitchMode(getContext(), TxtConfig.PAGE_SWITCH_MODE_SERIAL);
getTxtReaderContext().getTxtConfig().Page_Switch_Mode = TxtConfig.PAGE_SWITCH_MODE_SERIAL;
drawer = new SerialPageDrawer(this, readerContext, mScroller);
}
/**
* 剪切切换页面
*/
public void setPageSwitchByShear() {
TxtConfig.savePageSwitchMode(getContext(), TxtConfig.PAGE_SWITCH_MODE_SHEAR);
getTxtReaderContext().getTxtConfig().Page_Switch_Mode = TxtConfig.PAGE_SWITCH_MODE_SHEAR;
drawer = new ShearPageDrawer(this, readerContext, mScroller);
}
/**
* 滑盖切换页面
*/
public void setPageSwitchByCover() {
TxtConfig.savePageSwitchMode(getContext(), TxtConfig.PAGE_SWITCH_MODE_COVER);
getTxtReaderContext().getTxtConfig().Page_Switch_Mode = TxtConfig.PAGE_SWITCH_MODE_COVER;
drawer = new NormalPageDrawer(this, readerContext, mScroller);
}
/**
* 保存当前进度到数据库建议退出时调用
*/
public void saveCurrentProgress() {
TxtFileMsg fileMsg = getTxtReaderContext().getFileMsg();
if (getTxtReaderContext().InitDone() && fileMsg != null) {
String path = fileMsg.FilePath;
if (path != null && new File(path).exists()) {
//说明当前是有数据的
IPage midPage = getTxtReaderContext().getPageData().MidPage();
if (midPage != null && midPage.HasData()) {
FileReadRecordDB readRecordDB = new FileReadRecordDB(readerContext.getContext());
readRecordDB.createTable();
FileReadRecordBean r = new FileReadRecordBean();
r.fileName = fileMsg.FileName;
r.filePath = fileMsg.FilePath;
try {
r.fileHashName = FileUtil.getMD5Checksum(path);
} catch (Exception e) {
ELogger.log(tag, "saveCurrentProgress Exception:" + e.toString());
readRecordDB.closeTable();
return;
}
r.paragraphIndex = midPage.getFirstChar().ParagraphIndex;
r.chartIndex = midPage.getFirstChar().CharIndex;
readRecordDB.insertData(r);
readRecordDB.closeTable();
} else {
ELogger.log(tag, "saveCurrentProgress midPage is false empty");
}
}
}
}
private void refreshCurrentView() {
if (getWidth() > 0) {
refreshTag(1, 1, 1);
ITxtTask task = new DrawPrepareTask();
task.Run(actionLoadListener, readerContext);
}
}
/**
* @return 获取章节数据
*/
public List<IChapter> getChapters() {
return readerContext.getChapters();
}
/**
* @param progress 获取指定进度的章节信息
* @return
*/
public IChapter getChapterFromProgress(int progress) {
List<IChapter> chapters = getChapters();
if (chapters != null && chapters.size() > 0) {
int paragraphNum = getTxtReaderContext().getParagraphData().getParagraphNum();
int terminalParagraphIndex = progress * paragraphNum / 100;
if (terminalParagraphIndex == 0) {
return chapters.get(0);
}
for (IChapter chapter : chapters) {
int startIndex = chapter.getStartParagraphIndex();
int endIndex = chapter.getEndParagraphIndex();
ELogger.log("getChapterFromProgress", startIndex + "," + endIndex);
if (terminalParagraphIndex >= startIndex && terminalParagraphIndex < endIndex) {
return chapter;
}
}
}
return null;
}
private ILoadListener actionLoadListener = new LoadListenerAdapter() {
@Override
public void onSuccess() {
checkMoveState();
postInvalidate();
post(new Runnable() {
@Override
public void run() {
onPageProgress(readerContext.getPageData().MidPage());
}
});
}
};
}

View File

@ -0,0 +1,298 @@
package com.bifan.txtreaderlib.main;
import android.graphics.Paint;
import com.bifan.txtreaderlib.bean.EnChar;
import com.bifan.txtreaderlib.bean.NumChar;
import com.bifan.txtreaderlib.bean.Page;
import com.bifan.txtreaderlib.bean.TxtChar;
import com.bifan.txtreaderlib.bean.TxtLine;
import com.bifan.txtreaderlib.interfaces.IPage;
import com.bifan.txtreaderlib.interfaces.IPageDataPipeline;
import com.bifan.txtreaderlib.interfaces.IParagraphData;
import com.bifan.txtreaderlib.interfaces.ITxtLine;
import com.bifan.txtreaderlib.utils.FormatUtil;
import com.bifan.txtreaderlib.utils.TextBreakUtil;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
/**
* created by bifan-wei
*/
public class VerticalPageDataPipeline implements IPageDataPipeline{
private TxtReaderContext readerContext;
public VerticalPageDataPipeline(TxtReaderContext readerContext) {
this.readerContext = readerContext;
}
@Override
public IPage getPageStartFromProgress(int paragraphIndex, int charIndex) {
IParagraphData data = readerContext.getParagraphData();
PageParam param = readerContext.getPageParam();
if (data == null) return null;
int PageLineNum = param.PageLineNum;
int PageWidth = param.PageWidth;
int LineWidth = (int) param.LineWidth;
float lineHeight = readerContext.getPageParam().LineHeight;
float textPadding = readerContext.getPageParam().TextPadding;
int CurrentPaIndex = paragraphIndex;
int startIndex = charIndex;
int ParagraphNum = data.getParagraphNum();
int paragraphMargin = param.ParagraphMargin;
if (CurrentPaIndex >= ParagraphNum || CurrentPaIndex < 0) return null;//超过返回null
IPage page = new Page();
//获取页面数据
while (page.getLineNum() < PageLineNum && CurrentPaIndex < ParagraphNum) {
String paragraphStr = data.getParagraphStr(CurrentPaIndex);
if (paragraphStr != null && paragraphStr.length() > 0) {
List<ITxtLine> lines = getLineFromParagraphStartOnBeginning(paragraphStr, CurrentPaIndex, startIndex, lineHeight, textPadding, readerContext.getPaintContext().textPaint);
for (ITxtLine line : lines) {
page.addLine(line);
if (page.getLineNum() >= PageLineNum) {
page.setFullPage(true);
break;
}
}
startIndex = 0;
}
CurrentPaIndex++;
}
//尝试识别是否需要加段落间距数据
int textWidth = readerContext.getTxtConfig().textSize;
if (paragraphMargin > 0 && LineWidth > 0 && PageWidth > LineWidth && page.getLineNum() > 0) {
int width = param.PaddingLeft;
List<ITxtLine> lines = new ArrayList<>();
for (int i = 0; i < page.getLineNum(); i++) {
ITxtLine line = page.getLine(i);
width = width + LineWidth;
if (width > PageWidth) {
page.setFullPage(true);
if (width - LineWidth + textWidth <= PageWidth) {
lines.add(line);
}
break;
} else {
lines.add(line);
}
if (line.isParagraphEndLine()) {
width = width + paragraphMargin;//说明还有行数据
}
}
if (width >= PageWidth) {
page.setFullPage(true);
}
page.setLines(lines);
}
return page;
}
@Override
public IPage getPageEndToProgress(int paragraphIndex, int charIndex) {
IParagraphData data = readerContext.getParagraphData();
PageParam param = readerContext.getPageParam();
if(data==null) return null;
int PageLineNum = param.PageLineNum;
int PageWidth = param.PageWidth;
int LineHeight = (int) param.LineHeight;
int LineWidth = (int) param.LineWidth;
int CurrentPaIndex = paragraphIndex;
int startIndex = charIndex;
int ParagraphNum = data.getParagraphNum();
float textPadding = readerContext.getPageParam().TextPadding;
if (charIndex == 0) {//说明上页开始是段落开始位置段落左移
CurrentPaIndex--;//
startIndex = 0;
}
if (CurrentPaIndex >= ParagraphNum || CurrentPaIndex < 0) return null;//超过返回null
IPage page = new Page();
int paragraphMargin = param.ParagraphMargin;
while (page.getLineNum() < PageLineNum && CurrentPaIndex >= 0) {
String paragraphStr = data.getParagraphStr(CurrentPaIndex);
if (paragraphStr != null && paragraphStr.length() > 0) {
if (startIndex == 0) {//说明上页开始是段落开始位置
startIndex = paragraphStr.length();
}
List<ITxtLine> lines = getLineFromParagraphOnEnd(paragraphStr, CurrentPaIndex, startIndex, LineHeight, textPadding, readerContext.getPaintContext().textPaint);
if (lines.size() > 0) {
for (int i = lines.size() - 1; i >= 0; i--) {
ITxtLine line = lines.get(i);
page.addLine(line);
if (page.getLineNum() >= PageLineNum) {
page.setFullPage(true);
break;
}
}
}
}
CurrentPaIndex--;
startIndex = 0;
}
if (page.HasData()) {
Collections.reverse(page.getLines());
}
if (paragraphMargin > 0 && LineHeight > 0 && PageWidth > LineHeight && page.getLineNum() > 0) {
int width = param.PaddingLeft;
int textWidth = readerContext.getTxtConfig().textSize;
List<ITxtLine> lines = new ArrayList<>();
for (int i = page.getLineNum() - 1; i >= 0; i--) {
ITxtLine line = page.getLine(i);
if (i == page.getLineNum() - 1) {//底部那个不添加偏移量
lines.add(line);
width = width + textWidth;
} else {
if (width + LineWidth > PageWidth) {
page.setFullPage(true);
if (width + textWidth <= PageWidth) {
lines.add(line);
}
break;
} else {
lines.add(line);
width = width + LineWidth;
if (line.isParagraphEndLine()) {
width = width + paragraphMargin;//说明还有行数据
}
}
}
}
if (width >= PageWidth) {
page.setFullPage(true);
}
page.setLines(lines);
if (page.HasData()) {
Collections.reverse(page.getLines());
}
}
return page;
}
private List<ITxtLine> getLineFromParagraphOnEnd(String paragraphData, int paragraphIndex, int endCharIndex,
float lineHeight, float textPadding, Paint paint) {
List<ITxtLine> lines = new ArrayList<>();
int startIndex = 0;
int paragraphLength = paragraphData.length();
if (paragraphLength == 0 || endCharIndex <= 0) {
return lines;
}
endCharIndex = endCharIndex >= paragraphData.length() ? paragraphData.length() : endCharIndex;
if (endCharIndex > 0) {
String s = paragraphData.substring(startIndex, endCharIndex);//截取要的数据
while (s.length() > 0) {// is[0] 为个数 is[1] 为是否满一行
float[] is = TextBreakUtil.BreakTextVertical(s, lineHeight, textPadding, paint);
ITxtLine line;
int num = (int) is[0];
if (is[1] != 1) {//不满一行
line = getLineFromString(s, paragraphIndex, startIndex, is);
if (endCharIndex == paragraphLength && s.length() + startIndex >= paragraphLength) {
line.setParagraphEndLine(true);
}
lines.add(line);
return lines;
}
String lineStr = s.substring(0, num);
line = getLineFromString(lineStr, paragraphIndex, startIndex, is);
startIndex = startIndex + num;
if (line != null) {
lines.add(line);
}
s = s.substring(lineStr.length());
}
}
return lines;
}
/**
* @param paragraphData
* @param paragraphIndex
* @param startCharIndex
* @param lineHeight
* @param textPadding
* @param paint
* @return
*/
private List<ITxtLine> getLineFromParagraphStartOnBeginning(String paragraphData, int paragraphIndex, int startCharIndex,
float lineHeight, float textPadding, Paint paint) {
List<ITxtLine> lines = new ArrayList<>();
int startIndex = startCharIndex;
startIndex = startIndex < 0 ? 0 : startIndex;
int paragraphLength = paragraphData.length();
if ( startIndex >= paragraphLength) {
return lines;
}
if ( paragraphData.length() > 0) {
String s = paragraphData.substring(startIndex);//截取要的数据
while (s.length() > 0) {// is[0] 为个数 is[1] 为是否满一行
float[] is = TextBreakUtil.BreakTextVertical(s, lineHeight, textPadding, paint);
ITxtLine line;
if (is[1] != 1) {//不满一行
line = getLineFromString(s, paragraphIndex, startIndex, is);
if (s.length() + startIndex >= paragraphLength) {
line.setParagraphEndLine(true);
}
lines.add(line);
break;
}
int num = (int) is[0];
String lineStr = s.substring(0, num);
line = getLineFromString(lineStr, paragraphIndex, startIndex, is);
startIndex = startIndex + num;
if (line != null) {
lines.add(line);
}
s = s.substring(num);
}
}
return lines;
}
private ITxtLine getLineFromString(String str, int ParagraphIndex, int charIndex, float[] is) {
ITxtLine line = null;
int index = charIndex;
int cIndex = 2;//找到字符宽度
if (str != null && str.length() > 0) {
line = new TxtLine();
char[] cs = str.toCharArray();
for (char c : cs) {
TxtChar Char;
if (FormatUtil.isDigital(c)) {
Char = new NumChar(c);
} else if (FormatUtil.isLetter(c)) {
Char = new EnChar(c);
} else {
Char = new TxtChar(c);
}
Char.ParagraphIndex = ParagraphIndex;
Char.CharIndex = index++;
Char.CharWidth = is[cIndex++];
line.addChar(Char);
}
}
return line;
}
}

View File

@ -0,0 +1,62 @@
package com.bifan.txtreaderlib.tasks;
import android.graphics.Bitmap;
import com.bifan.txtreaderlib.interfaces.ILoadListener;
import com.bifan.txtreaderlib.interfaces.IPage;
import com.bifan.txtreaderlib.interfaces.ITxtTask;
import com.bifan.txtreaderlib.main.TxtReaderContext;
import com.bifan.txtreaderlib.utils.ELogger;
import com.bifan.txtreaderlib.utils.TxtBitmapUtil;
/**
* Created by bifan-wei
* on 2017/11/27.
*/
public class BitmapProduceTask implements ITxtTask {
private String tag = "BitmapProduceTask";
@Override
public void Run(ILoadListener callBack, TxtReaderContext readerContext) {
ELogger.log(tag, "produce bitmap");
callBack.onMessage("start to produce bitmap");
int[] rs = readerContext.getPageData().refreshTag;
IPage[] pages = readerContext.getPageData().getPages();
Bitmap[] bitmaps = readerContext.getBitmapData().getPages();
int index = 0;
for (int neeRefresh : rs) {
IPage page = pages[index];
if (neeRefresh == 1) {
ELogger.log(tag, "page " + index + " neeRefresh");
Bitmap bitmap ;
if(readerContext.getTxtConfig().VerticalPageMode){
bitmap = TxtBitmapUtil.createVerticalPage(
readerContext.getBitmapData().getBgBitmap(),
readerContext.getPaintContext(),
readerContext.getPageParam(),
readerContext.getTxtConfig(), page);
}else {
bitmap = TxtBitmapUtil.createHorizontalPage(
readerContext.getBitmapData().getBgBitmap(),
readerContext.getPaintContext(),
readerContext.getPageParam(),
readerContext.getTxtConfig(), page);
}
bitmaps[index] = bitmap;
} else {
ELogger.log(tag, "page " + index + " no neeRefresh");
//no neeRefresh ,do not change
}
index++;
}
ELogger.log(tag, "already done ,call back success");
callBack.onMessage("already done ,call back success");
readerContext.setInitDone(true);
//already done ,call back success
callBack.onSuccess();
}
}

View File

@ -0,0 +1,32 @@
package com.bifan.txtreaderlib.tasks;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
/**
* author: bifan-wei
* 2021/11/13.
*/
public class DestroyableTask {
private final ExecutorService mExecutorService = Executors.newSingleThreadExecutor();
private final IShutdownCall mShutdownCall;
public DestroyableTask(IShutdownCall shutdownCall) {
this.mShutdownCall = shutdownCall;
}
public void excuse(Runnable runnable) {
mExecutorService.submit(runnable);
}
public void destroy() {
if (mShutdownCall != null) {
mShutdownCall.clearIfNeedBeforeShutdown();
}
mExecutorService.shutdownNow();
}
public interface IShutdownCall {
void clearIfNeedBeforeShutdown();
}
}

View File

@ -0,0 +1,34 @@
package com.bifan.txtreaderlib.tasks;
import android.content.Context;
import android.graphics.Color;
import com.bifan.txtreaderlib.interfaces.ILoadListener;
import com.bifan.txtreaderlib.interfaces.ITxtTask;
import com.bifan.txtreaderlib.main.PaintContext;
import com.bifan.txtreaderlib.main.TxtConfig;
import com.bifan.txtreaderlib.main.TxtReaderContext;
import com.bifan.txtreaderlib.utils.ELogger;
/**
* Created by bifan-wei
* on 2017/11/27.
*/
public class DrawPrepareTask implements ITxtTask {
private String tag = "DrawPrepareTask";
@Override
public void Run(ILoadListener callBack, TxtReaderContext readerContext) {
callBack.onMessage("start do DrawPrepare");
ELogger.log(tag, "do DrawPrepare");
initPainContext(readerContext.context,readerContext.getPaintContext(), readerContext.getTxtConfig());
readerContext.getPaintContext().textPaint.setColor(Color.WHITE);
ITxtTask txtTask = new BitmapProduceTask();
txtTask.Run(callBack, readerContext);
}
private void initPainContext(Context context,PaintContext paintContext, TxtConfig txtConfig) {
TxtConfigInitTask.initPainContext(context,paintContext, txtConfig);
}
}

View File

@ -0,0 +1,156 @@
package com.bifan.txtreaderlib.tasks;
import com.bifan.txtreaderlib.bean.Chapter;
import com.bifan.txtreaderlib.bean.TxtMsg;
import com.bifan.txtreaderlib.interfaces.IChapter;
import com.bifan.txtreaderlib.interfaces.IChapterMatcher;
import com.bifan.txtreaderlib.interfaces.ILoadListener;
import com.bifan.txtreaderlib.interfaces.IParagraphData;
import com.bifan.txtreaderlib.interfaces.ITxtTask;
import com.bifan.txtreaderlib.main.ParagraphData;
import com.bifan.txtreaderlib.main.TxtReaderContext;
import com.bifan.txtreaderlib.utils.ELogger;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* @author bifan-wei
* @description
* @time 2021/11/13 16:52
*/
public class FileDataLoadTask implements ITxtTask {
private static final String ChapterPatternStr = "(^.{0,3}\\s*第)(.{1,9})[章节卷集部篇回](\\s*)";
private String tag = "FileDataLoadTask";
private IChapterMatcher chapterMatcher;
private boolean stop = false;
public void onStop() {
stop = true;
}
@Override
public void Run(ILoadListener callBack, TxtReaderContext readerContext) {
stop = false;
IParagraphData paragraphData = new ParagraphData();
chapterMatcher = readerContext.getChapterMatcher();
List<IChapter> chapter = new ArrayList<>();
callBack.onMessage("start read file data");
Boolean readSuccess = ReadData(readerContext.getFileMsg().FilePath, readerContext.getFileMsg().FileCode, paragraphData, chapter);
if (readSuccess) {
ELogger.log(tag, "ReadData readSuccess");
callBack.onMessage(" read file data success");
readerContext.setParagraphData(paragraphData);
readerContext.setChapters(chapter);
ITxtTask txtTask = new TxtConfigInitTask();
txtTask.Run(callBack, readerContext);
} else {
callBack.onFail(TxtMsg.InitError);
callBack.onMessage("ReadData fail on FileDataLoadTask");
}
stop = true;
}
private Boolean ReadData(String filePath, String Charset, IParagraphData paragraphData, List<IChapter> chapters) {
File file = new File(filePath);
BufferedReader bufferedReader = null;
ELogger.log(tag, "start to ReadData");
ELogger.log(tag, "--file Charset:" + Charset);
try {
bufferedReader = new BufferedReader(new InputStreamReader(new FileInputStream(file), Charset));
try {
String data;
int index = 0;
int chapterIndex = 0;
while (!stop && (data = bufferedReader.readLine()) != null) {
if (data.length() > 0) {
IChapter chapter = compileChapter(data, paragraphData.getCharNum(), index, chapterIndex);
paragraphData.addParagraph(data);
if (chapter != null) {
chapterIndex++;
chapters.add(chapter);
}
index++;
}
}
initChapterEndIndex(chapters, paragraphData.getParagraphNum());
return !stop;
} catch (IOException e) {
ELogger.log(tag, "IOException:" + e.toString());
}
} catch (UnsupportedEncodingException e) {
ELogger.log(tag, "UnsupportedEncodingException:" + e.toString());
} catch (FileNotFoundException e) {
ELogger.log(tag, "FileNotFoundException:" + e.toString());
} finally {
if (bufferedReader != null) {
try {
bufferedReader.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
return false;
}
private void initChapterEndIndex(List<IChapter> chapters, int paragraphNum) {
if (chapters != null && chapters.size() > 0) {
for (int i = 0, sum = chapters.size(); i < sum; i++) {
int nextIndex = i + 1;
IChapter chapter = chapters.get(i);
if (nextIndex < sum) {
int startIndex = chapter.getStartParagraphIndex();
int endIndex = chapters.get(nextIndex).getEndParagraphIndex() - 1;
if (endIndex < startIndex) {
endIndex = startIndex;
}
chapter.setEndParagraphIndex(endIndex);
} else {
int endIndex = paragraphNum - 1;
endIndex = Math.max(endIndex, 0);
chapter.setEndParagraphIndex(endIndex);
}
}
}
}
/**
* @param data 文本数据
* @param chapterStartIndex 开始字符在全文中的位置
* @param ParagraphIndex 段落位置
* @param chapterIndex 章节位置
* @return 没有识别到章节数据返回null
*/
private IChapter compileChapter(String data, int chapterStartIndex, int ParagraphIndex, int chapterIndex) {
if (chapterMatcher == null) {
if (data.trim().startsWith("") || data.contains("")) {
Pattern p = Pattern.compile(ChapterPatternStr);
Matcher matcher = p.matcher(data);
if (matcher.find()) {
int startIndex = 0;
int endIndex = data.length();
return new Chapter(chapterStartIndex, chapterIndex, data, ParagraphIndex, ParagraphIndex, startIndex, endIndex);
}
}
return null;
} else {
return chapterMatcher.match(data, ParagraphIndex);
}
}
}

View File

@ -0,0 +1,33 @@
package com.bifan.txtreaderlib.tasks;
import com.bifan.txtreaderlib.interfaces.IChapter;
import com.bifan.txtreaderlib.interfaces.ILoadListener;
import com.bifan.txtreaderlib.interfaces.IParagraphData;
import com.bifan.txtreaderlib.interfaces.ITxtTask;
import com.bifan.txtreaderlib.main.ParagraphData;
import com.bifan.txtreaderlib.main.TxtReaderContext;
import com.bifan.txtreaderlib.utils.ELogger;
import java.util.ArrayList;
import java.util.List;
/**
* Created by bifan-wei
* on 2018/1/28.
*/
public class TextLoader {
private final String tag = "FileDataLoadTask";
public void load(String text, TxtReaderContext readerContext, ILoadListener callBack) {
IParagraphData paragraphData = new ParagraphData();
List<IChapter> chapter = new ArrayList<>();
callBack.onMessage("start read text");
ELogger.log(tag, "start read text");
paragraphData.addParagraph(text + "");
readerContext.setParagraphData(paragraphData);
readerContext.setChapters(chapter);
ITxtTask txtTask = new TxtConfigInitTask();
txtTask.Run(callBack, readerContext);
}
}

View File

@ -0,0 +1,134 @@
package com.bifan.txtreaderlib.tasks;
import android.content.Context;
import android.content.res.AssetManager;
import android.graphics.Paint;
import android.graphics.Typeface;
import com.bifan.txtreaderlib.interfaces.ILoadListener;
import com.bifan.txtreaderlib.interfaces.ITxtTask;
import com.bifan.txtreaderlib.main.PageParam;
import com.bifan.txtreaderlib.main.PaintContext;
import com.bifan.txtreaderlib.main.TxtConfig;
import com.bifan.txtreaderlib.main.TxtReaderContext;
import com.bifan.txtreaderlib.utils.ELogger;
import com.bifan.txtreaderlib.utils.TxtBitmapUtil;
/**
* Created by HP on 2017/11/26.
*/
public class TxtConfigInitTask implements ITxtTask {
private String tag = "TxtConfigInitTask";
@Override
public void Run(ILoadListener callBack, TxtReaderContext readerContext) {
ELogger.log(tag, "do TxtConfigInit");
callBack.onMessage("start init settings in TxtConfigInitTask");
TxtConfig config = readerContext.getTxtConfig();
initTxtConfig(readerContext, config);
//if not null ,do recycle
if (readerContext.getBitmapData().getBgBitmap() != null) {
readerContext.getBitmapData().getBgBitmap().recycle();
}
int width = readerContext.getPageParam().PageWidth;
int height = readerContext.getPageParam().PageHeight;
//init the bg bitmap
readerContext.getBitmapData().setBgBitmap(TxtBitmapUtil.createBitmap(config.backgroundColor, width, height));
//initPageParam
initPageParam(readerContext);
//start draw prepare
//get preRead Progress
int startParagraphIndex = 0;
int startCharIndex = 0;
if (readerContext.getFileMsg() != null) {
startParagraphIndex = readerContext.getFileMsg().PreParagraphIndex;
startCharIndex = readerContext.getFileMsg().PreCharIndex;
}
//init Context
initPainContext(readerContext.context, readerContext.getPaintContext(), readerContext.getTxtConfig());
ITxtTask txtTask = new TxtPageLoadTask(startParagraphIndex, startCharIndex);
txtTask.Run(callBack, readerContext);
}
/**
* get pre or default config
*
* @param readerContext
* @param config
*/
private void initTxtConfig(TxtReaderContext readerContext, TxtConfig config) {
config.showNote = TxtConfig.getIsShowNote(readerContext.context);
config.canPressSelect = TxtConfig.getCanPressSelect(readerContext.context);
config.textColor = TxtConfig.getTextColor(readerContext.context);
config.textSize = TxtConfig.getTextSize(readerContext.context);
config.backgroundColor = TxtConfig.getBackgroundColor(readerContext.context);
config.NoteColor = TxtConfig.getNoteTextColor(readerContext.context);
config.SelectTextColor = TxtConfig.getSelectTextColor(readerContext.context);
config.SliderColor = TxtConfig.getSliderColor(readerContext.context);
config.Bold = TxtConfig.isBold(readerContext.context);
config.Page_Switch_Mode = TxtConfig.getPageSwitchMode(readerContext.context);
config.ShowSpecialChar = TxtConfig.IsShowSpecialChar(readerContext.context);
config.VerticalPageMode = TxtConfig.IsOnVerticalPageMode(readerContext.context);
config.PageSwitchDuration = TxtConfig.getPageSwitchDuration(readerContext.context);
}
/**
* @param paintContext
* @param txtConfig
*/
public static void initPainContext(Context context, PaintContext paintContext, TxtConfig txtConfig) {
paintContext.textPaint.setTextSize(txtConfig.textSize);
paintContext.textPaint.setFakeBoldText(txtConfig.Bold);
paintContext.textPaint.setTextAlign(Paint.Align.LEFT);
paintContext.textPaint.setColor(txtConfig.textColor);
paintContext.textPaint.setAntiAlias(true);
paintContext.notePaint.setTextSize(txtConfig.textSize);
paintContext.notePaint.setColor(txtConfig.NoteColor);
paintContext.notePaint.setTextAlign(Paint.Align.LEFT);
paintContext.notePaint.setAntiAlias(true);
paintContext.selectTextPaint.setTextSize(txtConfig.textSize);
paintContext.selectTextPaint.setColor(txtConfig.SelectTextColor);
paintContext.selectTextPaint.setTextAlign(Paint.Align.LEFT);
paintContext.selectTextPaint.setAntiAlias(true);
paintContext.sliderPaint.setColor(txtConfig.SliderColor);
paintContext.sliderPaint.setAntiAlias(true);
paintContext.textPaint.setFakeBoldText(txtConfig.Bold);
paintContext.textPaint.setTypeface(null);
if (txtConfig.VerticalPageMode) {
AssetManager mgr = context.getAssets();
Typeface tf = Typeface.createFromAsset(mgr, "fonts/text_style.TTF");
paintContext.textPaint.setTypeface(tf);
}
}
private void initPageParam(TxtReaderContext readerContext) {
PageParam param = readerContext.getPageParam();
int lineHeight = readerContext.getTxtConfig().textSize + param.LinePadding;
param.LineHeight = lineHeight;
if (!readerContext.getTxtConfig().VerticalPageMode) {
param.LineWidth = param.PageWidth - param.PaddingLeft - param.PaddingRight;
param.LineHeight = lineHeight;
param.PageLineNum = (param.PageHeight - param.PaddingTop - param.PaddingBottom - readerContext.getTxtConfig().textSize - 2) / lineHeight + 1;
} else {
param.LineWidth = lineHeight;
param.LineHeight = param.PageHeight - param.PaddingTop - param.PaddingBottom;
param.PageLineNum = (param.PageWidth - param.PaddingLeft - param.PaddingRight - readerContext.getTxtConfig().textSize - 2) / lineHeight + 1;
}
param.PaddingLeft = TxtConfig.Page_PaddingLeft;
param.LinePadding = TxtConfig.Page_LinePadding;
param.PaddingRight = TxtConfig.Page_PaddingRight;
param.PaddingTop = TxtConfig.Page_PaddingTop;
param.PaddingBottom = TxtConfig.Page_PaddingBottom;
param.ParagraphMargin = TxtConfig.Page_Paragraph_margin;
}
}

View File

@ -0,0 +1,82 @@
package com.bifan.txtreaderlib.tasks;
import com.bifan.txtreaderlib.bean.FileReadRecordBean;
import com.bifan.txtreaderlib.bean.TxtFileMsg;
import com.bifan.txtreaderlib.bean.TxtMsg;
import com.bifan.txtreaderlib.interfaces.ILoadListener;
import com.bifan.txtreaderlib.interfaces.ITxtTask;
import com.bifan.txtreaderlib.main.FileReadRecordDB;
import com.bifan.txtreaderlib.main.TxtReaderContext;
import com.bifan.txtreaderlib.utils.ELogger;
import com.bifan.txtreaderlib.utils.FileCharsetDetector;
import com.bifan.txtreaderlib.utils.FileUtil;
import java.io.File;
/**
* @description
* @author bifan-wei
* @time 2021/11/13 16:53
*/
public class TxtFileLoader {
private String tag = "TxtFileLoader";
private FileDataLoadTask mFileDataLoadTask;
public void load(String filePath, TxtReaderContext readerContext, ILoadListener loadListener) {
load(filePath, null, readerContext, loadListener);
}
public void load(String filePath, String fileName, TxtReaderContext readerContext, ILoadListener loadListener) {
onStop();
if (!FileUtil.FileExist(filePath)) {
loadListener.onFail(TxtMsg.FileNoExist);
return;
}
loadListener.onMessage("initFile start");
initFile(filePath, fileName, readerContext);
ELogger.log(tag, "initFile done");
loadListener.onMessage("initFile done");
mFileDataLoadTask = new FileDataLoadTask();
mFileDataLoadTask.Run(loadListener, readerContext);
}
private void initFile(String filePath, String fileName, TxtReaderContext readerContext) {
File file = new File(filePath);
TxtFileMsg fileMsg = new TxtFileMsg();
fileMsg.FileSize = file.getTotalSpace();
fileMsg.FilePath = filePath;
fileMsg.FileCode = new FileCharsetDetector().getCharset(new File(filePath));
fileMsg.CurrentParagraphIndex = 0;
fileMsg.PreParagraphIndex = 0;
fileMsg.PreCharIndex = 0;
if (fileName == null || fileName.trim().length() == 0) {
fileName = file.getName();
}
fileMsg.FileName = fileName;
//获取之前的播放进度记录
FileReadRecordDB readRecordDB = new FileReadRecordDB(readerContext.getContext());
readRecordDB.createTable();
try {
FileReadRecordBean r = readRecordDB.getRecordByHashName(FileUtil.getMD5Checksum(filePath));
if (r != null) {
fileMsg.PreParagraphIndex = r.paragraphIndex;
fileMsg.PreCharIndex = r.chartIndex;
}
} catch (Exception e) {
e.printStackTrace();
}
readRecordDB.createTable();
readerContext.setFileMsg(fileMsg);
}
public void onStop() {
if (mFileDataLoadTask != null) {
mFileDataLoadTask.onStop();
}
}
}

View File

@ -0,0 +1,82 @@
package com.bifan.txtreaderlib.tasks;
import com.bifan.txtreaderlib.interfaces.ILoadListener;
import com.bifan.txtreaderlib.interfaces.IPage;
import com.bifan.txtreaderlib.interfaces.ITxtTask;
import com.bifan.txtreaderlib.main.TxtReaderContext;
import com.bifan.txtreaderlib.utils.ELogger;
/**
* Created by bifan-wei
* on 2017/11/27.
*/
public class TxtPageLoadTask implements ITxtTask {
private String tag = "TxtPageLoadTask";
private final int startParagraphIndex;
private final int startCharIndex;
public TxtPageLoadTask(int startParagraphIndex, int startCharIndex) {
this.startParagraphIndex = startParagraphIndex;
this.startCharIndex = startCharIndex;
}
@Override
public void Run(ILoadListener callBack, TxtReaderContext readerContext) {
callBack.onMessage("start load pageData");
IPage firstPage = null;
IPage midPage;
IPage nextPage = null;
midPage = readerContext.getPageDataPipeline().getPageStartFromProgress(startParagraphIndex, startCharIndex);
if (midPage != null && midPage.HasData()) {
firstPage = readerContext.getPageDataPipeline().getPageEndToProgress(midPage.getFirstChar().ParagraphIndex, midPage.getFirstChar().CharIndex - 1);
}
if (midPage != null && midPage.isFullPage()) {
nextPage = readerContext.getPageDataPipeline().getPageStartFromProgress(midPage.getLastChar().ParagraphIndex, midPage.getLastChar().CharIndex + 1);
}
ELogger.log(tag, "获取进度数据完成");
ELogger.log(tag, "startParagraphIndex/ startCharIndex+" + startParagraphIndex + "/" + startCharIndex);
//show the data
if (firstPage != null) {
ELogger.log(tag, "firstPage:" + firstPage.toString());
if (!firstPage.HasData()) {
firstPage = null;
}
} else {
ELogger.log(tag, "firstPage is null");
}
if (midPage != null) {
ELogger.log(tag, "midPage:" + midPage.toString());
if (!midPage.HasData()) {
midPage = null;
}
} else {
ELogger.log(tag, "midPage is null");
}
if (nextPage != null) {
ELogger.log(tag, "nextPage:" + nextPage.toString());
if (!nextPage.HasData()) {
nextPage = null;
}
} else {
ELogger.log(tag, "nextPage is null");
}
readerContext.getPageData().setFirstPage(firstPage);
readerContext.getPageData().setMidPage(midPage);
readerContext.getPageData().setLastPage(nextPage);
ITxtTask txtTask = new DrawPrepareTask();
txtTask.Run(callBack, readerContext);
}
}

View File

@ -0,0 +1,157 @@
package com.bifan.txtreaderlib.ui;
import android.content.Context;
import android.graphics.Color;
import android.graphics.drawable.ColorDrawable;
import android.util.DisplayMetrics;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.WindowManager;
import android.widget.BaseAdapter;
import android.widget.ListView;
import android.widget.PopupWindow;
import android.widget.TextView;
import com.bifan.txtreaderlib.R;
import com.bifan.txtreaderlib.interfaces.IChapter;
import java.util.List;
/**
* Created by bifan-wei
* on 2017/12/12.
*/
public class ChapterList extends PopupWindow {
private Context mContext;
private ListView mRootView;
private MyAdapter mAdapter;
private List<IChapter> mChapters;
private int CurrentIndex = -1;
private int AllCharNum ;
private int ViewHeight;
public ChapterList(Context mContext, int ViewHeight, List<IChapter> mChapters, int allCharNum) {
super(mContext);
this.mContext = mContext;
this.ViewHeight = ViewHeight;
this.mChapters = mChapters;
this.AllCharNum = allCharNum;
initRootView();
}
public int getAllCharNum() {
return AllCharNum;
}
public void setCurrentIndex(int currentIndex) {
CurrentIndex = currentIndex;
}
public void notifyDataSetChanged() {
mAdapter.notifyDataSetChanged();
}
public ListView getListView() {
return mRootView;
}
public BaseAdapter getAdapter() {
return mAdapter;
}
protected void initRootView() {
WindowManager m = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE);
DisplayMetrics metrics = new DisplayMetrics();
m.getDefaultDisplay().getMetrics(metrics);
int ViewHeight = this.ViewHeight;
int ViewWidth = metrics.widthPixels;
mRootView = new ListView(mContext);
ViewGroup.LayoutParams params = new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);
mRootView.setLayoutParams(params);
this.setContentView(mRootView);
this.setWidth(ViewWidth);
this.setHeight(ViewHeight);
this.setFocusable(true);
this.setOutsideTouchable(true);
this.setAnimationStyle(R.style.HwTxtChapterMenuAnimation);
this.setBackgroundDrawable(new ColorDrawable(Color.parseColor("#1c1c1a")));
mAdapter = new MyAdapter();
mRootView.setAdapter(mAdapter);
}
private class MyAdapter extends BaseAdapter {
@Override
public int getCount() {
return mChapters == null ? 0 : mChapters.size();
}
@Override
public Object getItem(int i) {
return mChapters.get(i);
}
@Override
public long getItemId(int i) {
return 0;
}
private final int ColorNegative = Color.parseColor("#aeaca2");
private final int ColorSelected = Color.parseColor("#fa4613");
@Override
public View getView(int i, View view, ViewGroup viewGroup) {
Holder holder;
if (view == null) {
holder = new Holder();
view = LayoutInflater.from(mContext).inflate(R.layout.adapter_chapterlist, null);
holder.index = view.findViewById(R.id.adapter_chapterList_index);
holder.title = view.findViewById(R.id.adapter_chapterList_title);
holder.progress = view.findViewById(R.id.adapter_chapterList_progress);
view.setTag(holder);
} else {
holder = (Holder) view.getTag();
}
IChapter chapter = mChapters.get(i);
if (CurrentIndex == i) {
holder.title.setTextColor(ColorSelected);
holder.progress.setTextColor(Color.WHITE);
holder.progress.setText("当前");
} else {
holder.title.setTextColor(Color.WHITE);
holder.progress.setTextColor(ColorNegative);
float p = 0;
if (AllCharNum > 0) {
p = (float) chapter.getStartIndex() / (float) AllCharNum;
if (p > 1) {
p = 1;
}
}
holder.progress.setText((int) (p * 100) + "%");
}
holder.index.setText(i + 1 + "");
holder.title.setText((chapter.getTitle() + "").trim());
return view;
}
private class Holder {
TextView index;
TextView title;
TextView progress;
}
}
public void onDestroy() {
mContext = null;
mRootView = null;
mAdapter = null;
if (mChapters != null) {
mChapters.clear();
mChapters = null;
}
}
}

View File

@ -0,0 +1,237 @@
package com.bifan.txtreaderlib.ui;
import android.content.Context;
import android.content.SharedPreferences;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import com.bifan.txtreaderlib.R;
import com.bifan.txtreaderlib.adapter.MessageAdapter;
import com.bifan.txtreaderlib.bean.ChatMessage_bmob;
import com.bifan.txtreaderlib.bean.ChatRequest;
import com.bifan.txtreaderlib.bean.ChatResponse;
import com.bifan.txtreaderlib.bean.Message;
import com.bifan.txtreaderlib.interfaces.ChatGPTService;
import org.json.JSONObject;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;
import cn.bmob.v3.Bmob;
import cn.bmob.v3.BmobQuery;
import cn.bmob.v3.exception.BmobException;
import cn.bmob.v3.listener.FindListener;
import cn.bmob.v3.listener.SaveListener;
import okhttp3.OkHttpClient;
import retrofit2.Call;
import retrofit2.Callback;
import retrofit2.Response;
import retrofit2.Retrofit;
import retrofit2.converter.gson.GsonConverterFactory;
public class ChatWithGptActivity extends AppCompatActivity {
private RecyclerView recyclerView;
private MessageAdapter messageAdapter;
private EditText editTextMessage;
private TextView chatbot_title;
private Button buttonSend;
private String bookName = "";
private String userid="";
private ArrayList<Message> messageList = new ArrayList<>();
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_chatwithgpt);
Bmob.initialize(this, "8f9f1d1ea520b0ce4f84a6fa83a5f754");
if (getIntent() != null && getIntent().hasExtra("book_name")) {
bookName = getIntent().getStringExtra("book_name");
}
SharedPreferences sharedPreferences = getSharedPreferences("userinf", Context.MODE_PRIVATE);
String userIdFromPrefs = sharedPreferences.getString("user_id", "");
userid = userIdFromPrefs != null ? userIdFromPrefs : "";
recyclerView = findViewById(R.id.chatRecyclerView);
editTextMessage = findViewById(R.id.inputEditText);
buttonSend = findViewById(R.id.sendButton);
chatbot_title=findViewById(R.id.chatbot_title);
messageAdapter = new MessageAdapter(messageList);
recyclerView.setLayoutManager(new LinearLayoutManager(this));
recyclerView.setAdapter(messageAdapter);
init();
buttonSend.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
String text = editTextMessage.getText().toString();
if (!text.isEmpty()) {
messageList.add(new Message(text, 1)); // User message
messageAdapter.notifyItemInserted(messageList.size() - 1);
recyclerView.scrollToPosition(messageList.size() - 1);
editTextMessage.setText("");
sendMessageToChatGPT(text);
ChatMessage_bmob chatMessage_bmob=new ChatMessage_bmob(text,1,bookName,userid);
chatMessage_bmob.save(new SaveListener<String>() {
@Override
public void done(String objectId,BmobException e) {
if(e==null){
// toast("添加数据成功返回objectId为"+objectId);
}else{
//toast("创建数据失败:" + e.getMessage());
}
}
});
}
}
});
}
private void init(){
chatbot_title.setText(bookName);
BmobQuery<ChatMessage_bmob> bmobQuery = new BmobQuery<>();
bmobQuery.addWhereEqualTo("relatedBook", bookName);
bmobQuery.addWhereEqualTo("userid", userid);
bmobQuery.findObjects(new FindListener<ChatMessage_bmob>() {
@Override
public void done(List<ChatMessage_bmob> messages, BmobException e) {
if (e == null && messages != null) {
if (!messages.isEmpty()) {
for (ChatMessage_bmob item : messages) {
if (item.getContent() == null) {
String content = "出现未知错误!";
messageList.add(new Message(content, item.getIsuser()));
} else {
messageList.add(new Message(item.getContent(), item.getIsuser()));
}
}
} else {
// 如果没有消息添加一条 bot 的欢迎消息
String welcomeMessage = "你好!我是你的阅读助手。我可以帮助你解答关于书籍的各种问题,比如内容概述、作者信息、主要主题和角色分析等。请随时向我提问,现在,你有什么关于《" + bookName + "》的问题吗?";
messageList.add(new Message(welcomeMessage, 0)); // 假设 0 表示系统或助手消息
ChatMessage_bmob chatMessage_bmob=new ChatMessage_bmob(welcomeMessage,0,bookName,userid);
chatMessage_bmob.save(new SaveListener<String>() {
@Override
public void done(String objectId,BmobException e) {
if(e==null){
// toast("添加数据成功返回objectId为"+objectId);
}else{
//toast("创建数据失败:" + e.getMessage());
}
}
});
}
messageAdapter.notifyDataSetChanged();
} else {
Log.e("BookInfo", "Error loading book info: " + (e != null ? e.getMessage() : ""));
}
}
});
}
/**
* 从数据库加载历史对话并构建消息列表
*/
private List<ChatRequest.Message> loadMessagesFromDatabase(String bookName, String userId) {
List<ChatRequest.Message> messages = new ArrayList<>();
BmobQuery<ChatMessage_bmob> bmobQuery = new BmobQuery<>();
bmobQuery.addWhereEqualTo("relatedBook", bookName);
bmobQuery.addWhereEqualTo("userid", userId);
bmobQuery.order("createdAt");
bmobQuery.setLimit(10); //设置上下文最多10条历史对话
bmobQuery.findObjects(new FindListener<ChatMessage_bmob>() {
@Override
public void done(List<ChatMessage_bmob> chatMessages, BmobException e) {
if (e == null && chatMessages != null && !chatMessages.isEmpty()) {
for (ChatMessage_bmob item : chatMessages) {
// 根据消息来源判断角色假设 0 表示用户1 表示助手
String role = item.getIsuser() == 0 ? "user" : "assistant";
messages.add(new ChatRequest.Message(role, item.getContent()));
Log.d("test",item.getContent());
}
} else {
Log.e("BookInfo", "Error loading book info: " + (e != null ? e.getMessage() : "No error details."));
}
}
});
return messages;
}
private void sendMessageToChatGPT(String messageText) {
OkHttpClient client = new OkHttpClient.Builder()
.connectTimeout(60, TimeUnit.SECONDS)
.readTimeout(60, TimeUnit.SECONDS)
.writeTimeout(60, TimeUnit.SECONDS)
.build();
Retrofit retrofit = new Retrofit.Builder()
.client(client)
.baseUrl("https://api.openai-ch.top/")
.addConverterFactory(GsonConverterFactory.create())
.build();
ChatGPTService service = retrofit.create(ChatGPTService.class);
List<ChatRequest.Message> messages = new ArrayList<>();
String prompt = "你好!我是你的阅读助手。我可以帮助你解答关于书籍的各种问题,比如内容概述、作者信息、主要主题和角色分析等。请随时向我提问,我会用中文简体来回答你。现在,你有什么关于《" + bookName + "》的问题吗?";
messages.add(new ChatRequest.Message("system", prompt));
// messages.add(new ChatRequest.Message("system", "You are a reading assistant. The user will ask you questions about the book '" + bookName + "'."));
messages.addAll(loadMessagesFromDatabase(bookName, userid));
messages.add(new ChatRequest.Message("user", messageText)); // 添加用户消息
ChatRequest request = new ChatRequest(messages); // 使用消息列表创建请求
request.setModel("gpt-4-turbo"); // 根据需要选择不同的模型
request.setMaxTokens(200); // 设置最大 token 根据需要调整
request.setTemperature(0.5); // 设置适当的温度
service.postMessage(request).enqueue(new Callback<ChatResponse>() {
@Override
public void onResponse(Call<ChatResponse> call, Response<ChatResponse> response) {
if (response.isSuccessful() && response.body() != null) {
ChatResponse chatResponse = response.body();
if (chatResponse.choices != null && !chatResponse.choices.isEmpty()) {
ChatResponse.Message message = chatResponse.choices.get(0).message;
String reply = message.content; // 这是从 GPT-3.5 响应中提取的内容
runOnUiThread(() -> {
messageList.add(new Message(reply, 0));
messageAdapter.notifyItemInserted(messageList.size() - 1);
recyclerView.scrollToPosition(messageList.size() - 1);
ChatMessage_bmob chatMessage_bmob = new ChatMessage_bmob(reply, 0, bookName, userid);
chatMessage_bmob.save(new SaveListener<String>() {
@Override
public void done(String objectId, BmobException e) {
if (e == null) {
// toast("添加数据成功返回objectId为" + objectId);
} else {
// toast("创建数据失败:" + e.getMessage());
}
}
});
});
}
} else {
try {
String errorBody = response.errorBody() != null ? response.errorBody().string() : "Unknown error";
Log.e("ChatGPT", "Error: " + errorBody);
} catch (IOException e) {
Log.e("ChatGPT", "Error reading error body", e);
}
}
}
@Override
public void onFailure(Call<ChatResponse> call, Throwable t) {
Log.e("ChatGPT", "Failure: " + t.getMessage());
}
});
}
}

View File

@ -0,0 +1,74 @@
package com.bifan.txtreaderlib.ui;
import android.annotation.SuppressLint;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.util.AttributeSet;
import android.view.View;
import com.bifan.txtreaderlib.R;
public class CircleView extends View {
private Paint mPaint;
private float Radius = 0.0F;
private int CoverColor = Color.parseColor("#66ffffff");
public CircleView(Context context) {
super(context);
this.init();
}
public CircleView(Context context, AttributeSet attrs) {
super(context, attrs);
getAttrs(attrs);
this.init();
}
public CircleView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
getAttrs(attrs);
this.init();
}
private void getAttrs(AttributeSet attrs) {
TypedArray ta = getContext().obtainStyledAttributes(attrs, R.styleable.CircleView);
Radius = ta.getDimension(R.styleable.CircleView_CircleRadius, Radius);
CoverColor = ta.getColor(R.styleable.CircleView_BgColor, CoverColor);
ta.recycle();
}
private void init() {
this.mPaint = new Paint();
this.mPaint.setAntiAlias(true);
this.mPaint.setColor(this.CoverColor);
}
public void setCoverColor(int CoverColor) {
this.CoverColor = CoverColor;
this.mPaint.setColor(CoverColor);
this.postInvalidate();
}
@SuppressLint({"DrawAllocation"})
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
int vWidth = this.getWidth();
int vHeight = this.getHeight();
int radius = Math.max(vHeight, vWidth) / 2;
if (Radius > 0) {
radius = (int) Radius;
}
float Cx = (float) (vWidth / 2);
float Cy = (float) (vHeight / 2);
this.mPaint.setColor(Color.WHITE);
canvas.drawCircle(Cx, Cy, (float) radius, this.mPaint);
this.mPaint.setColor(CoverColor);
canvas.drawCircle(Cx, Cy, (float) radius-3, this.mPaint);
}
}

View File

@ -0,0 +1,808 @@
package com.bifan.txtreaderlib.ui;
import android.content.ClipboardManager;
import android.content.Context;
import android.content.Intent;
import android.graphics.Color;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import androidx.annotation.Nullable;
import androidx.core.content.ContextCompat;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.content.res.ResourcesCompat;
import android.text.TextUtils;
import android.util.DisplayMetrics;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
import android.view.WindowManager;
import android.widget.SeekBar;
import android.widget.TextView;
import android.widget.Toast;
import com.bifan.txtreaderlib.R;
import com.bifan.txtreaderlib.bean.TxtChar;
import com.bifan.txtreaderlib.bean.TxtMsg;
import com.bifan.txtreaderlib.interfaces.ICenterAreaClickListener;
import com.bifan.txtreaderlib.interfaces.IChapter;
import com.bifan.txtreaderlib.interfaces.ILoadListener;
import com.bifan.txtreaderlib.interfaces.IPageChangeListener;
import com.bifan.txtreaderlib.interfaces.ISliderListener;
import com.bifan.txtreaderlib.interfaces.ITextSelectListener;
import com.bifan.txtreaderlib.main.TxtConfig;
import com.bifan.txtreaderlib.main.TxtReaderView;
import com.bifan.txtreaderlib.utils.ELogger;
import com.bifan.txtreaderlib.utils.FileProvider;
import com.google.android.material.floatingactionbutton.FloatingActionButton;
import java.io.File;
/**
* Created by bifan-wei
* on 2017/12/8.
*/
public class HwTxtPlayActivity extends AppCompatActivity {
protected Handler mHandler;
protected boolean FileExist = false;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(getContentViewLayout());
FileExist = getIntentData();
init();
loadFile();
registerListener();
}
protected int getContentViewLayout() {
return R.layout.activity_hwtxtpaly;
}
protected boolean getIntentData() {
// Get the intent that started this activity
Uri uri = getIntent().getData();
if (uri != null) {
ELogger.log("getIntentData", "" + uri);
} else {
ELogger.log("getIntentData", "uri is null");
}
if (uri != null) {
try {
String path = FileProvider.getFileAbsolutePath(this, uri);
if (!TextUtils.isEmpty(path)) {
if (path.contains("/storage/")) {
path = path.substring(path.indexOf("/storage/"));
}
ELogger.log("getIntentData", "path:" + path);
File file = new File(path);
if (file.exists()) {
FilePath = path;
FileName = file.getName();
return true;
} else {
toast("文件不存在");
return false;
}
}
return false;
} catch (Exception e) {
toast("文件出错了");
}
}
FilePath = getIntent().getStringExtra("FilePath");
FileName = getIntent().getStringExtra("FileName");
ContentStr = getIntent().getStringExtra("ContentStr");
if (ContentStr == null) {
return FilePath != null && new File(FilePath).exists();
} else {
return true;
}
}
/**
* @param context 上下文
* @param FilePath 文本文件路径
*/
public static void loadTxtFile(Context context, String FilePath) {
loadTxtFile(context, FilePath, null);
}
/**
* @param context 上下文
* @param str 文本文内容
*/
public static void loadStr(Context context, String str) {
loadTxtStr(context, str, null);
}
/**
* @param context 上下文
* @param str 文本显示内容
* @param FileName 显示的书籍或者文件名称
*/
public static void loadTxtStr(Context context, String str, String FileName) {
Intent intent = new Intent();
intent.putExtra("ContentStr", str);
intent.putExtra("FileName", FileName);
intent.setClass(context, HwTxtPlayActivity.class);
context.startActivity(intent);
}
/**
* @param context 上下文
* @param FilePath 文本文件路径
* @param FileName 显示的书籍或者文件名称
*/
public static void loadTxtFile(Context context, String FilePath, String FileName) {
Intent intent = new Intent();
intent.putExtra("FilePath", FilePath);
intent.putExtra("FileName", FileName);
intent.setClass(context, HwTxtPlayActivity.class);
context.startActivity(intent);
}
protected View mTopDecoration, mBottomDecoration;
protected View mChapterMsgView;
protected TextView mChapterMsgName;
protected TextView mChapterMsgProgress;
protected TextView mChapterNameText;
protected TextView mChapterMenuText;
protected TextView mProgressText;
protected TextView mSettingText;
protected TextView mSelectedText;
protected TxtReaderView mTxtReaderView;
protected View mTopMenu;
protected View mBottomMenu;
protected View mCoverView;
protected View ClipboardView;
protected String CurrentSelectedText;
protected ChapterList mChapterListPop;
protected MenuHolder mMenuHolder = new MenuHolder();
protected FloatingActionButton fab;
protected void init() {
mHandler = new Handler();
mChapterMsgView = findViewById(R.id.activity_hwTxtPlay_chapter_msg);
mChapterMsgName = findViewById(R.id.chapter_name);
mChapterMsgProgress = findViewById(R.id.chapter_progress);
mTopDecoration = findViewById(R.id.activity_hwTxtPlay_top);
mBottomDecoration = findViewById(R.id.activity_hwTxtPlay_bottom);
mTxtReaderView = findViewById(R.id.activity_hwTxtPlay_readerView);
mChapterNameText = findViewById(R.id.activity_hwTxtPlay_chapterName);
mChapterMenuText = findViewById(R.id.activity_hwTxtPlay_chapter_menuText);
mProgressText = findViewById(R.id.activity_hwTxtPlay_progress_text);
mSettingText = findViewById(R.id.activity_hwTxtPlay_setting_text); //设置
mTopMenu = findViewById(R.id.activity_hwTxtPlay_menu_top);
mBottomMenu = findViewById(R.id.activity_hwTxtPlay_menu_bottom);
mCoverView = findViewById(R.id.activity_hwTxtPlay_cover);
ClipboardView = findViewById(R.id.activity_hwTxtPlay_ClipBoar);
mSelectedText = findViewById(R.id.activity_hwTxtPlay_selected_text);
mMenuHolder.mTitle = findViewById(R.id.txtReader_menu_title);
mMenuHolder.mPreChapter = findViewById(R.id.txtReadr_menu_chapter_pre);
mMenuHolder.mNextChapter = findViewById(R.id.txtReadr_menu_chapter_next);
mMenuHolder.mSeekBar = findViewById(R.id.txtReadr_menu_seekbar);
mMenuHolder.mTextSizeDel = findViewById(R.id.txtRead_menu_textsize_del);
mMenuHolder.mTextSize = findViewById(R.id.txtRead_menu_textSize);
mMenuHolder.mTextSizeAdd = findViewById(R.id.txtRead_menu_textSize_add);
mMenuHolder.mBoldSelectedLayout = findViewById(R.id.txtRead_menu_textSetting1_bold);
mMenuHolder.mCoverSelectedLayout = findViewById(R.id.txtRead_menu_textSetting2_cover);
mMenuHolder.mShearSelectedLayout = findViewById(R.id.txtRead_menu_textSetting2_shear);
mMenuHolder.mTranslateSelectedLayout = findViewById(R.id.txtRead_menu_textSetting2_translate);
mMenuHolder.mStyle1 = findViewById(R.id.txtReader_menu_style1);
mMenuHolder.mStyle2 = findViewById(R.id.txtReader_menu_style2);
mMenuHolder.mStyle3 = findViewById(R.id.txtRead_menu_style3);
mMenuHolder.mStyle4 = findViewById(R.id.txtReader_menu_style4);
mMenuHolder.mStyle5 = findViewById(R.id.txtReader_menu_style5);
fab = findViewById(R.id.chatbot);
}
private final int[] StyleTextColors = new int[]{
Color.parseColor("#4a453a"),
Color.parseColor("#505550"),
Color.parseColor("#453e33"),
Color.parseColor("#8f8e88"),
Color.parseColor("#27576c")
};
protected String ContentStr = null;
protected String FilePath = null;
protected String FileName = null;
protected void loadFile() {
TxtConfig.savePageSwitchDuration(this, 400);
if (ContentStr == null) {
if (TextUtils.isEmpty(FilePath) || !(new File(FilePath).exists())) {
toast("文件不存在");
return;
}
}
mHandler.postDelayed(() -> {
//延迟加载避免闪一下的情况出现
if (ContentStr == null) {
loadOurFile();
} else {
loadStr();
}
}, 300);
}
/**
*
*/
protected void loadOurFile() {
mTxtReaderView.loadTxtFile(FilePath, new ILoadListener() {
@Override
public void onSuccess() {
if (!hasExisted) {
onLoadDataSuccess();
}
}
@Override
public void onFail(final TxtMsg txtMsg) {
if (!hasExisted) {
runOnUiThread(() -> onLoadDataFail(txtMsg));
}
}
@Override
public void onMessage(String message) {
//加载过程信息
}
});
}
/**
* @param txtMsg txtMsg
*/
protected void onLoadDataFail(TxtMsg txtMsg) {
//加载失败信息
toast(String.valueOf(txtMsg));
}
/**
*
*/
protected void onLoadDataSuccess() {
if (TextUtils.isEmpty(FileName)) {//没有显示的名称获取文件名显示
FileName = mTxtReaderView.getTxtReaderContext().getFileMsg().FileName;
}
setBookName(FileName);
initWhenLoadDone();
}
private void loadStr() {
String testText = ContentStr;
mTxtReaderView.loadText(testText, new ILoadListener() {
@Override
public void onSuccess() {
setBookName("test with str");
initWhenLoadDone();
}
@Override
public void onFail(TxtMsg txtMsg) {
//加载失败信息
toast(txtMsg + "");
}
@Override
public void onMessage(String message) {
//加载过程信息
}
});
}
protected void initWhenLoadDone() {
if (mTxtReaderView.getTxtReaderContext().getFileMsg() != null) {
FileName = mTxtReaderView.getTxtReaderContext().getFileMsg().FileName;
}
mMenuHolder.mTextSize.setText(String.valueOf(mTxtReaderView.getTextSize()));
mTopDecoration.setBackgroundColor(mTxtReaderView.getBackgroundColor());
mBottomDecoration.setBackgroundColor(mTxtReaderView.getBackgroundColor());
//mTxtReaderView.setLeftSlider(new MuiLeftSlider());//修改左滑动条
//mTxtReaderView.setRightSlider(new MuiRightSlider());//修改右滑动条
//字体初始化
onTextSettingUi(mTxtReaderView.getTxtReaderContext().getTxtConfig().Bold);
//翻页初始化
onPageSwitchSettingUi(mTxtReaderView.getTxtReaderContext().getTxtConfig().Page_Switch_Mode);
//保存的翻页模式
int pageSwitchMode = mTxtReaderView.getTxtReaderContext().getTxtConfig().Page_Switch_Mode;
if (pageSwitchMode == TxtConfig.PAGE_SWITCH_MODE_SERIAL) {
mTxtReaderView.setPageSwitchByTranslate();
} else if (pageSwitchMode == TxtConfig.PAGE_SWITCH_MODE_COVER) {
mTxtReaderView.setPageSwitchByCover();
} else if (pageSwitchMode == TxtConfig.PAGE_SWITCH_MODE_SHEAR) {
mTxtReaderView.setPageSwitchByShear();
}
//章节初始化
if (mTxtReaderView.getChapters() != null && mTxtReaderView.getChapters().size() > 0) {
WindowManager m = (WindowManager) getSystemService(Context.WINDOW_SERVICE);
DisplayMetrics metrics = new DisplayMetrics();
m.getDefaultDisplay().getMetrics(metrics);
int ViewHeight = metrics.heightPixels - mTopDecoration.getHeight();
mChapterListPop = new ChapterList(this, ViewHeight, mTxtReaderView.getChapters(), mTxtReaderView.getTxtReaderContext().getParagraphData().getCharNum());
mChapterListPop.setOnDismissListener(this::hideNavigationBarStatusBar);
mChapterListPop.getListView().setOnItemClickListener((adapterView, view, i, l) -> {
IChapter chapter = (IChapter) mChapterListPop.getAdapter().getItem(i);
mChapterListPop.dismiss();
mTxtReaderView.loadFromProgress(chapter.getStartParagraphIndex(), 0);
});
} else {
Gone(mChapterMenuText);
}
}
protected void registerListener() {
mSettingText.setOnClickListener(view -> Show(mTopMenu, mBottomMenu, mCoverView));
setMenuListener();
setSeekBarListener();
setCenterClickListener();
setPageChangeListener();
setOnTextSelectListener();
setStyleChangeListener();
setExtraListener();
setChatbotListener();
}
private void setChatbotListener() {
fab.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
// Define what happens when the fab is clicked
// Toast.makeText(HwTxtPlayActivity.this, "Floating Action Button Clicked!", Toast.LENGTH_SHORT).show();
String bookName = FileName.substring(0, FileName.lastIndexOf("."));
Intent intent = new Intent(HwTxtPlayActivity.this,ChatWithGptActivity.class);
intent.putExtra("book_name", bookName);
startActivity(intent);
}
});
}
//底部菜单中的按钮监听上一章下一章字体减小放大粗体翻页的滑盖平移剪切
private void setExtraListener() {
mMenuHolder.mPreChapter.setOnClickListener(new ChapterChangeClickListener(true));
mMenuHolder.mNextChapter.setOnClickListener(new ChapterChangeClickListener(false));
mMenuHolder.mTextSizeAdd.setOnClickListener(new TextChangeClickListener(true));
mMenuHolder.mTextSizeDel.setOnClickListener(new TextChangeClickListener(false));
mMenuHolder.mBoldSelectedLayout.setOnClickListener(new TextSettingClickListener());
mMenuHolder.mCoverSelectedLayout.setOnClickListener(new SwitchSettingClickListener(TxtConfig.PAGE_SWITCH_MODE_COVER));
mMenuHolder.mTranslateSelectedLayout.setOnClickListener(new SwitchSettingClickListener(TxtConfig.PAGE_SWITCH_MODE_SERIAL));
mMenuHolder.mShearSelectedLayout.setOnClickListener(new SwitchSettingClickListener(TxtConfig.PAGE_SWITCH_MODE_SHEAR));
}
//背景色修改
protected void setStyleChangeListener() {
mMenuHolder.mStyle1.setOnClickListener(new StyleChangeClickListener(ContextCompat.getColor(this, R.color.hwTxtReader_styleColor1), StyleTextColors[0]));
mMenuHolder.mStyle2.setOnClickListener(new StyleChangeClickListener(ContextCompat.getColor(this, R.color.hwTxtReader_styleColor2), StyleTextColors[1]));
mMenuHolder.mStyle3.setOnClickListener(new StyleChangeClickListener(ContextCompat.getColor(this, R.color.hwTxtReader_styleColor3), StyleTextColors[2]));
mMenuHolder.mStyle4.setOnClickListener(new StyleChangeClickListener(ContextCompat.getColor(this, R.color.hwTxtReader_styleColor4), StyleTextColors[3]));
mMenuHolder.mStyle5.setOnClickListener(new StyleChangeClickListener(ContextCompat.getColor(this, R.color.hwTxtReader_styleColor5), StyleTextColors[4]));
}
protected void setOnTextSelectListener() {
mTxtReaderView.setOnTextSelectListener(new ITextSelectListener() {
@Override
public void onTextChanging(TxtChar firstSelectedChar, TxtChar lastSelectedChar) {
//firstSelectedChar.Top
// firstSelectedChar.Bottom
// 这里可以根据 firstSelectedChar与lastSelectedChar的top与bottom的位置
//计算显示你要显示的弹窗位置如果需要的话
}
@Override
public void onTextChanging(String selectText) {
onCurrentSelectedText(selectText);
}
@Override
public void onTextSelected(String selectText) {
onCurrentSelectedText(selectText);
}
});
mTxtReaderView.setOnSliderListener(new ISliderListener() {
@Override
public void onShowSlider(TxtChar txtChar) {
//TxtChar 为当前长按选中的字符
// 这里可以根据 txtChar的top与bottom的位置
//计算显示你要显示的弹窗位置如果需要的话
}
@Override
public void onShowSlider(String currentSelectedText) {
onCurrentSelectedText(currentSelectedText);
Show(ClipboardView);
}
@Override
public void onReleaseSlider() {
Gone(ClipboardView);
}
});
}
protected void setPageChangeListener() {
mTxtReaderView.setPageChangeListener(progress -> {
int p = (int) (progress * 1000);
mProgressText.setText(((float) p / 10) + "%");
mMenuHolder.mSeekBar.setProgress((int) (progress * 100));
IChapter currentChapter = mTxtReaderView.getCurrentChapter();
if (currentChapter != null) {
mChapterNameText.setText((currentChapter.getTitle() + "").trim());
} else {
mChapterNameText.setText("无章节");
}
});
}
protected void setCenterClickListener() {
mTxtReaderView.setOnCenterAreaClickListener(new ICenterAreaClickListener() {
@Override
public boolean onCenterClick(float widthPercentInView) { //点击中心区域才会弹出
mSettingText.performClick();
fab.setVisibility(View.VISIBLE); // Make FloatingActionButton visible
return true;
}
@Override
public boolean onOutSideCenterClick(float widthPercentInView) {
if (mBottomMenu.getVisibility() == View.VISIBLE) {
mSettingText.performClick();
fab.setVisibility(View.GONE); // 隐藏 FloatingActionButton
return true;
}
return false;
}
});
}
// 设置菜单监听器
protected void setMenuListener() {
mTopMenu.setOnTouchListener((view, motionEvent) -> true);
mBottomMenu.setOnTouchListener((view, motionEvent) -> true);
mCoverView.setOnTouchListener((view, motionEvent) -> {
Gone(mTopMenu, mBottomMenu, mCoverView, mChapterMsgView);
fab.setVisibility(View.GONE); // 同时隐藏 FloatingActionButton
return true;
});
//为章节信息文本设置点击监听器,当点击时会显示或隐藏章节列表弹窗
View.OnClickListener chapterMsgShowClick = v -> {
if (mChapterListPop != null) {
if (!mChapterListPop.isShowing()) {
mChapterListPop.showAsDropDown(mTopDecoration);
mHandler.postDelayed(() -> {
IChapter currentChapter = mTxtReaderView.getCurrentChapter();
if (currentChapter != null) {
mChapterListPop.setCurrentIndex(currentChapter.getIndex());
mChapterListPop.notifyDataSetChanged();
}
}, 300);
} else {
mChapterListPop.dismiss();
}
}
};
mChapterNameText.setOnClickListener(chapterMsgShowClick);
mChapterMenuText.setOnClickListener(chapterMsgShowClick);
mTopMenu.setOnClickListener(view -> {
if (mChapterListPop.isShowing()) {
mChapterListPop.dismiss();
}
});
}
// 设置进度条监听器
protected void setSeekBarListener() {
mMenuHolder.mSeekBar.setOnTouchListener((view, motionEvent) -> {
if (motionEvent.getAction() == MotionEvent.ACTION_UP) {
mTxtReaderView.loadFromProgress(mMenuHolder.mSeekBar.getProgress());
Gone(mChapterMsgView);
}
return false;
});
mMenuHolder.mSeekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
@Override
public void onProgressChanged(SeekBar seekBar, final int progress, boolean fromUser) {
if (fromUser) {
mHandler.post(() -> onShowChapterMsg(progress));
}
}
@Override
public void onStartTrackingTouch(SeekBar seekBar) {
}
@Override
public void onStopTrackingTouch(SeekBar seekBar) {
Gone(mChapterMsgView);
}
});
}
private void onShowChapterMsg(int progress) {
if (mTxtReaderView != null && mChapterListPop != null) {
IChapter chapter = mTxtReaderView.getChapterFromProgress(progress);
if (chapter != null) {
float p = (float) chapter.getStartIndex() / (float) mChapterListPop.getAllCharNum();
if (p > 1) {
p = 1;
}
Show(mChapterMsgView);
mChapterMsgName.setText(chapter.getTitle());
mChapterMsgProgress.setText((int) (p * 100) + "%");
}
}
}
private void onCurrentSelectedText(String SelectedText) {
String selectTextShow = String.format(getString(R.string.select_char_num), (SelectedText + "").length());
mSelectedText.setText(selectTextShow);
CurrentSelectedText = SelectedText;
}
private void onTextSettingUi(Boolean isBold) {
int rs = isBold ? R.drawable.ic_bold_selected : R.drawable.ic_bold_normal;
mMenuHolder.mBoldSelectedLayout.setBackgroundDrawable(ResourcesCompat.getDrawable(getResources(), rs, null));
}
private void onPageSwitchSettingUi(int pageSwitchMode) {
if (pageSwitchMode == TxtConfig.PAGE_SWITCH_MODE_SERIAL) {
mMenuHolder.mTranslateSelectedLayout.setBackgroundResource(R.drawable.shape_menu_textsetting_selected);
mMenuHolder.mCoverSelectedLayout.setBackgroundResource(R.drawable.shape_menu_textsetting_unselected);
mMenuHolder.mShearSelectedLayout.setBackgroundResource(R.drawable.shape_menu_textsetting_unselected);
} else if (pageSwitchMode == TxtConfig.PAGE_SWITCH_MODE_COVER) {
mMenuHolder.mTranslateSelectedLayout.setBackgroundResource(R.drawable.shape_menu_textsetting_unselected);
mMenuHolder.mCoverSelectedLayout.setBackgroundResource(R.drawable.shape_menu_textsetting_selected);
mMenuHolder.mShearSelectedLayout.setBackgroundResource(R.drawable.shape_menu_textsetting_unselected);
} else if (pageSwitchMode == TxtConfig.PAGE_SWITCH_MODE_SHEAR) {
mMenuHolder.mTranslateSelectedLayout.setBackgroundResource(R.drawable.shape_menu_textsetting_unselected);
mMenuHolder.mCoverSelectedLayout.setBackgroundResource(R.drawable.shape_menu_textsetting_unselected);
mMenuHolder.mShearSelectedLayout.setBackgroundResource(R.drawable.shape_menu_textsetting_selected);
}
}
private class TextSettingClickListener implements View.OnClickListener {
public TextSettingClickListener() {
}
@Override
public void onClick(View view) {
if (FileExist) {
Boolean Bold = mTxtReaderView.getTxtReaderContext().getTxtConfig().Bold;
mTxtReaderView.setTextBold(!Bold);
onTextSettingUi(!Bold);
}
}
}
//设置滑盖 平移 剪切的翻页模式
private class SwitchSettingClickListener implements View.OnClickListener {
private final int pageSwitchMode;
public SwitchSettingClickListener(int pageSwitchMode) {
this.pageSwitchMode = pageSwitchMode;
}
@Override
public void onClick(View view) {
if (FileExist) {
if (pageSwitchMode == TxtConfig.PAGE_SWITCH_MODE_COVER) {
mTxtReaderView.setPageSwitchByCover();
} else if (pageSwitchMode == TxtConfig.PAGE_SWITCH_MODE_SERIAL) {
mTxtReaderView.setPageSwitchByTranslate();
}
if (pageSwitchMode == TxtConfig.PAGE_SWITCH_MODE_SHEAR) {
mTxtReaderView.setPageSwitchByShear();
}
onPageSwitchSettingUi(pageSwitchMode);
}
}
}
private class ChapterChangeClickListener implements View.OnClickListener {
private final Boolean Pre;
public ChapterChangeClickListener(Boolean pre) {
Pre = pre;
}
@Override
public void onClick(View view) {
if (Pre) {
mTxtReaderView.jumpToPreChapter();
} else {
mTxtReaderView.jumpToNextChapter();
}
}
}
private class TextChangeClickListener implements View.OnClickListener {
private final Boolean Add;
public TextChangeClickListener(Boolean pre) {
Add = pre;
}
@Override
public void onClick(View view) {
if (FileExist) {
int textSize = mTxtReaderView.getTextSize();
if (Add) {
if (textSize + 2 <= TxtConfig.MAX_TEXT_SIZE) {
mTxtReaderView.setTextSize(textSize + 2);
mMenuHolder.mTextSize.setText(textSize + 2 + "");
}
} else {
if (textSize - 2 >= TxtConfig.MIN_TEXT_SIZE) {
mTxtReaderView.setTextSize(textSize - 2);
mMenuHolder.mTextSize.setText(textSize - 2 + "");
}
}
}
}
}
private class StyleChangeClickListener implements View.OnClickListener {
private final int BgColor;
private final int TextColor;
public StyleChangeClickListener(int bgColor, int textColor) {
BgColor = bgColor;
TextColor = textColor;
}
@Override
public void onClick(View view) {
if (FileExist) {
mTxtReaderView.setStyle(BgColor, TextColor);
mTopDecoration.setBackgroundColor(BgColor);
mBottomDecoration.setBackgroundColor(BgColor);
}
}
}
protected void setBookName(String name) {
mMenuHolder.mTitle.setText(name + "");
}
protected void Show(View... views) {
for (View v : views) {
v.setVisibility(View.VISIBLE);
}
}
protected void Gone(View... views) {
for (View v : views) {
v.setVisibility(View.GONE);
}
}
private Toast t;
protected void toast(final String msg) {
if (t != null) {
t.cancel();
}
t = Toast.makeText(HwTxtPlayActivity.this, msg, Toast.LENGTH_SHORT);
t.show();
}
protected static class MenuHolder {
public TextView mTitle;
public TextView mPreChapter;
public TextView mNextChapter;
public SeekBar mSeekBar;
public View mTextSizeDel;
public View mTextSizeAdd;
public TextView mTextSize;
public View mBoldSelectedLayout;
public View mCoverSelectedLayout;
public View mShearSelectedLayout;
public View mTranslateSelectedLayout;
public View mStyle1;
public View mStyle2;
public View mStyle3;
public View mStyle4;
public View mStyle5;
}
@Override
protected void onResume() {
super.onResume();
hideNavigationBarStatusBar();
}
private void hideNavigationBarStatusBar() {
if (Build.VERSION.SDK_INT >= 19) {
View decorView = getWindow().getDecorView();
decorView.setSystemUiVisibility(
View.SYSTEM_UI_FLAG_LAYOUT_STABLE
| View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
| View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
| View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
| View.SYSTEM_UI_FLAG_FULLSCREEN
| View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY);
}
}
@Override
protected void onDestroy() {
super.onDestroy();
exist();
}
public void BackClick(View view) {
finish();
}
public void onCopyText(View view) {
if (!TextUtils.isEmpty(CurrentSelectedText)) {
toast("已经复制到粘贴板");
ClipboardManager cm = (ClipboardManager) getSystemService(Context.CLIPBOARD_SERVICE);
cm.setText(CurrentSelectedText + "");
}
onCurrentSelectedText("");
mTxtReaderView.releaseSelectedState();
Gone(ClipboardView);
}
@Override
public void onBackPressed() {
super.onBackPressed();
finish();
}
@Override
public void finish() {
super.finish();
exist();
}
protected boolean hasExisted = false;
protected void exist() {
if (!hasExisted) {
ContentStr = null;
hasExisted = true;
if (mHandler != null) {
mHandler.removeCallbacksAndMessages(null);
mHandler = null;
}
if (mTxtReaderView != null) {
mTxtReaderView.saveCurrentProgress();
mTxtReaderView.onDestroy();
}
if (mTxtReaderView != null) {
mTxtReaderView.getTxtReaderContext().Clear();
mTxtReaderView = null;
}
if (mChapterListPop != null) {
if (mChapterListPop.isShowing()) {
mChapterListPop.dismiss();
}
mChapterListPop.onDestroy();
mChapterListPop = null;
}
mMenuHolder = null;
}
}
}

View File

@ -0,0 +1,21 @@
package com.bifan.txtreaderlib.utils;
import java.io.IOException;
import java.io.InputStream;
import java.util.Properties;
public class ConfigUtils {
private static final String CONFIG_FILE = "config.properties";
public static String getAuthKey() {
try {
Properties properties = new Properties();
InputStream inputStream = ConfigUtils.class.getClassLoader().getResourceAsStream(CONFIG_FILE);
properties.load(inputStream);
return properties.getProperty("auth_key");
} catch (IOException e) {
e.printStackTrace();
return null;
}
}
}

View File

@ -0,0 +1,29 @@
package com.bifan.txtreaderlib.utils;
import android.content.Context;
public class DisPlayUtil {
public static int px2dip(Context context, float pxValue) {
final float scale = context.getResources().getDisplayMetrics().density;
return (int) (pxValue / scale + 0.5f);
}
public static int dip2px(Context context, float dipValue) {
final float scale = context.getResources().getDisplayMetrics().density;
return (int) (dipValue * scale + 0.5f);
}
public static int px2sp(Context context, float pxValue) {
final float fontScale = context.getResources().getDisplayMetrics().scaledDensity;
return (int) (pxValue / fontScale + 0.5f);
}
public static int sp2px(Context context, float spValue) {
final float fontScale = context.getResources().getDisplayMetrics().scaledDensity;
return (int) (spValue * fontScale + 0.5f);
}
}

View File

@ -0,0 +1,26 @@
package com.bifan.txtreaderlib.utils;
import android.util.Log;
import com.bifan.txtreaderlib.interfaces.ITxtReaderLoggerListener;
import com.bifan.txtreaderlib.main.TxtConfig;
/**
* Created by HP on 2017/11/15.
*/
public class ELogger {
private static ITxtReaderLoggerListener l;
public static void setLoggerListener(ITxtReaderLoggerListener l) {
ELogger.l = l;
}
public static void log(String tag, String msg) {
if(TxtConfig.DebugMode) {
Log.e(tag, msg + "");
if (l != null) {
l.onLog(tag, msg + "");
}
}
}
}

View File

@ -0,0 +1,121 @@
package com.bifan.txtreaderlib.utils;
import android.text.TextUtils;
import org.mozilla.intl.chardet.nsDetector;
import org.mozilla.intl.chardet.nsICharsetDetectionObserver;
import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
public class FileCharsetDetector {
private boolean found = false;
private String encoding = null;
public String getCharset(File file) {
String charset = "GBK";
byte[] first3Bytes = new byte[3];
try {
boolean checked = false;
BufferedInputStream bis = new BufferedInputStream(new FileInputStream(file));
bis.mark(0);
int read = bis.read(first3Bytes, 0, 3);
if (read == -1)
return charset;
if (first3Bytes[0] == (byte) 0xFF && first3Bytes[1] == (byte) 0xFE) {
charset = "UTF-16LE";
checked = true;
} else if (first3Bytes[0] == (byte) 0xFE && first3Bytes[1] == (byte) 0xFF) {
charset = "UTF-16BE";
checked = true;
} else if (first3Bytes[0] == (byte) 0xEF && first3Bytes[1] == (byte) 0xBB
&& first3Bytes[2] == (byte) 0xBF) {
charset = "UTF-8";
checked = true;
}
if (!checked) {
charset = guessFileEncoding(file, new nsDetector());
if (!TextUtils.isEmpty(charset)) {
if (charset.equals("Big5")) {
charset = "GBK";
}
}
}
bis.reset();
bis.close();
} catch (Exception e) {
e.printStackTrace();
}
return charset;
}
/**
* 获取文件的编码
* @param file
* @param det
* @return
* @throws FileNotFoundException
* @throws IOException
*/
private String guessFileEncoding(File file, nsDetector det) throws IOException {
// Set an observer...
// The Notify() will be called when a matching charset is found.
det.Init(new nsICharsetDetectionObserver() {
@Override
public void Notify(String charset) {
encoding = charset;
found = true;
}
});
BufferedInputStream imp = new BufferedInputStream(new FileInputStream(file));
byte[] buf = new byte[1024];
int len;
boolean done;
boolean isAscii = false;
while ((len = imp.read(buf, 0, buf.length)) != -1) {
// Check if the stream is only ascii.
isAscii = det.isAscii(buf, len);
if (isAscii) {
break;
}
// DoIt if non-ascii and not done yet.
done = det.DoIt(buf, len, false);
if (done) {
break;
}
}
imp.close();
det.DataEnd();
if (isAscii) {
encoding = "ASCII";
found = true;
}
if (!found) {
String[] prob = det.getProbableCharsets();
// 这里将可能的字符集组合起来返回
for (int i = 0; i < prob.length; i++) {
if (i == 0) {
encoding = prob[i];
} else {
encoding += "," + prob[i];
}
}
if (prob.length > 0) {
// 在没有发现情况下,也可以只取第一个可能的编码,这里返回的是一个可能的序列
return encoding;
} else {
return "GBK";
}
}
return encoding;
}
}

View File

@ -0,0 +1,233 @@
package com.bifan.txtreaderlib.utils;
import android.content.ContentResolver;
import android.content.ContentUris;
import android.content.Context;
import android.database.Cursor;
import android.net.Uri;
import android.os.Build;
import android.os.Environment;
import android.os.FileUtils;
import android.provider.DocumentsContract;
import android.provider.MediaStore;
import android.provider.OpenableColumns;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import androidx.annotation.RequiresApi;
/**
* Created by bifan-wei
* on 2021/11/6.
*/
public class FileProvider {
/**
* 根据Uri获取文件绝对路径解决Android4.4以上版本Uri转换 兼容Android 10
*
* @param context
* @param uri
*/
public static String getFileAbsolutePath(Context context, Uri uri) {
if (context == null || uri == null) {
return null;
}
if (android.os.Build.VERSION.SDK_INT < android.os.Build.VERSION_CODES.KITKAT) {
return getRealFilePath(context, uri);
}
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.KITKAT && android.os.Build.VERSION.SDK_INT < Build.VERSION_CODES.Q && DocumentsContract.isDocumentUri(context, uri)) {
if (isExternalStorageDocument(uri)) {
String docId = DocumentsContract.getDocumentId(uri);
String[] split = docId.split(":");
String type = split[0];
if ("primary".equalsIgnoreCase(type)) {
return Environment.getExternalStorageDirectory() + "/" + split[1];
}
} else if (isDownloadsDocument(uri)) {
String id = DocumentsContract.getDocumentId(uri);
Uri contentUri = ContentUris.withAppendedId(Uri.parse("content://downloads/public_downloads"), Long.valueOf(id));
return getDataColumn(context, contentUri, null, null);
} else if (isMediaDocument(uri)) {
String docId = DocumentsContract.getDocumentId(uri);
String[] split = docId.split(":");
String type = split[0];
Uri contentUri = null;
if ("image".equals(type)) {
contentUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
} else if ("video".equals(type)) {
contentUri = MediaStore.Video.Media.EXTERNAL_CONTENT_URI;
} else if ("audio".equals(type)) {
contentUri = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI;
}
String selection = MediaStore.Images.Media._ID + "=?";
String[] selectionArgs = new String[]{split[1]};
return getDataColumn(context, contentUri, selection, selectionArgs);
}
} // MediaStore (and general)
if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q){
return uriToFileApiQ(context,uri);
}
else if ("content".equalsIgnoreCase(uri.getScheme())) {
// Return the remote address
if (isGooglePhotosUri(uri)) {
return uri.getLastPathSegment();
}
return getDataColumn(context, uri, null, null);
}
// File
else if ("file".equalsIgnoreCase(uri.getScheme())) {
return uri.getPath();
}
return null;
}
//此方法 只能用于4.4以下的版本
private static String getRealFilePath(final Context context, final Uri uri) {
if (null == uri) {
return null;
}
final String scheme = uri.getScheme();
String data = null;
if (scheme == null) {
data = uri.getPath();
} else if (ContentResolver.SCHEME_FILE.equals(scheme)) {
data = uri.getPath();
} else if (ContentResolver.SCHEME_CONTENT.equals(scheme)) {
String[] projection = {MediaStore.Images.ImageColumns.DATA};
Cursor cursor = context.getContentResolver().query(uri, projection, null, null, null);
// Cursor cursor = context.getContentResolver().query(uri, new String[]{MediaStore.Images.ImageColumns.DATA}, null, null, null);
if (null != cursor) {
if (cursor.moveToFirst()) {
int index = cursor.getColumnIndex(MediaStore.Images.ImageColumns.DATA);
if (index > -1) {
data = cursor.getString(index);
}
}
cursor.close();
}
}
return data;
}
/**
* @param uri The Uri to check.
* @return Whether the Uri authority is ExternalStorageProvider.
*/
private static boolean isExternalStorageDocument(Uri uri) {
return "com.android.externalstorage.documents".equals(uri.getAuthority());
}
/**
* @param uri The Uri to check.
* @return Whether the Uri authority is DownloadsProvider.
*/
private static boolean isDownloadsDocument(Uri uri) {
return "com.android.providers.downloads.documents".equals(uri.getAuthority());
}
private static String getDataColumn(Context context, Uri uri, String selection, String[] selectionArgs) {
Cursor cursor = null;
String column = MediaStore.Images.Media.DATA;
String[] projection = {column};
try {
cursor = context.getContentResolver().query(uri, projection, selection, selectionArgs, null);
if (cursor != null && cursor.moveToFirst()) {
int index = cursor.getColumnIndexOrThrow(column);
return cursor.getString(index);
}
} finally {
if (cursor != null) {
cursor.close();
}
}
return null;
}
/**
* @param uri The Uri to check.
* @return Whether the Uri authority is MediaProvider.
*/
private static boolean isMediaDocument(Uri uri) {
return "com.android.providers.media.documents".equals(uri.getAuthority());
}
/**
* @param uri The Uri to check.
* @return Whether the Uri authority is Google Photos.
*/
private static boolean isGooglePhotosUri(Uri uri) {
return "com.google.android.apps.photos.content".equals(uri.getAuthority());
}
/**
* Android 10 以上适配 另一种写法
* @param context
* @param uri
* @return
*/
private static String getFileFromContentUri(Context context, Uri uri) {
if (uri == null) {
return null;
}
String filePath;
String[] filePathColumn = {MediaStore.MediaColumns.DATA, MediaStore.MediaColumns.DISPLAY_NAME};
ContentResolver contentResolver = context.getContentResolver();
Cursor cursor = contentResolver.query(uri, filePathColumn, null,
null, null);
if (cursor != null) {
cursor.moveToFirst();
try {
filePath = cursor.getString(cursor.getColumnIndex(filePathColumn[0]));
return filePath;
} catch (Exception e) {
} finally {
cursor.close();
}
}
return "";
}
/**
* Android 10 以上适配
* @param context
* @param uri
* @return
*/
@RequiresApi(api = Build.VERSION_CODES.Q)
private static String uriToFileApiQ(Context context, Uri uri) {
File file = null;
//android10以上转换
if (uri.getScheme().equals(ContentResolver.SCHEME_FILE)) {
file = new File(uri.getPath());
} else if (uri.getScheme().equals(ContentResolver.SCHEME_CONTENT)) {
//把文件复制到沙盒目录
ContentResolver contentResolver = context.getContentResolver();
Cursor cursor = contentResolver.query(uri, null, null, null, null);
if (cursor.moveToFirst()) {
String displayName = cursor.getString(cursor.getColumnIndex(OpenableColumns.DISPLAY_NAME));
try {
InputStream is = contentResolver.openInputStream(uri);
File cache = new File(context.getExternalCacheDir().getAbsolutePath(), Math.round((Math.random() + 1) * 1000) + displayName);
FileOutputStream fos = new FileOutputStream(cache);
FileUtils.copy(is, fos);
file = cache;
fos.close();
is.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
return file.getAbsolutePath();
}
}

Some files were not shown because too many files have changed in this diff Show More