# Conflicts: # app/src/main/java/io/legado/app/App.kt # app/src/main/java/io/legado/app/ui/main/MainViewModel.kt # app/src/main/java/io/legado/app/ui/widget/anima/RotateLoading.ktpull/32/head
Before Width: | Height: | Size: 12 KiB After Width: | Height: | Size: 200 KiB |
@ -0,0 +1,18 @@ |
||||
package io.legado.app.data.dao |
||||
|
||||
import androidx.paging.DataSource |
||||
import androidx.room.Dao |
||||
import androidx.room.Query |
||||
import io.legado.app.data.entities.SearchBook |
||||
|
||||
@Dao |
||||
interface SearchBookDao { |
||||
|
||||
@Query("SELECT * FROM searchBooks") |
||||
fun observeAll(): DataSource.Factory<Int, SearchBook> |
||||
|
||||
@Query("SELECT * FROM searchBooks where time >= :time") |
||||
fun observeNew(time: Long): DataSource.Factory<Int, SearchBook> |
||||
|
||||
|
||||
} |
@ -1,3 +1,53 @@ |
||||
package io.legado.app.data.entities |
||||
|
||||
class SearchBook |
||||
import android.os.Parcelable |
||||
import android.text.TextUtils |
||||
import androidx.room.Entity |
||||
import androidx.room.Ignore |
||||
import androidx.room.Index |
||||
import androidx.room.PrimaryKey |
||||
import com.google.gson.Gson |
||||
import io.legado.app.utils.fromJson |
||||
import kotlinx.android.parcel.IgnoredOnParcel |
||||
import kotlinx.android.parcel.Parcelize |
||||
|
||||
@Parcelize |
||||
@Entity(tableName = "searchBooks", indices = [(Index(value = ["descUrl"], unique = true))]) |
||||
data class SearchBook( |
||||
@PrimaryKey |
||||
var descUrl: String = "", |
||||
var origin: String = "", // 书源规则id(默认-1,表示本地书籍) |
||||
var name: String? = null, |
||||
var author: String? = null, |
||||
var tag: String? = null, |
||||
var coverUrl: String? = null, |
||||
var description: String? = null, |
||||
var latestChapterTitle: String? = null, |
||||
var time: Long = 0L, |
||||
var variable: String? = null |
||||
) : Parcelable, BaseBook { |
||||
|
||||
@IgnoredOnParcel |
||||
@Ignore |
||||
override var variableMap: HashMap<String, String>? = null |
||||
get() = run { |
||||
initVariableMap() |
||||
return field |
||||
} |
||||
|
||||
private fun initVariableMap() { |
||||
if (variableMap == null) { |
||||
variableMap = if (TextUtils.isEmpty(variable)) { |
||||
HashMap() |
||||
} else { |
||||
Gson().fromJson<HashMap<String, String>>(variable!!) |
||||
} |
||||
} |
||||
} |
||||
|
||||
override fun putVariable(key: String, value: String) { |
||||
initVariableMap() |
||||
variableMap?.put(key, value) |
||||
variable = Gson().toJson(variableMap) |
||||
} |
||||
} |
@ -0,0 +1,2 @@ |
||||
package io.legado.app.model.analyzeRule |
||||
|
@ -0,0 +1,2 @@ |
||||
package io.legado.app.model.content |
||||
|
@ -0,0 +1,12 @@ |
||||
package io.legado.app.service |
||||
|
||||
import android.app.Service |
||||
import android.content.Intent |
||||
import android.os.IBinder |
||||
|
||||
class CheckSourceService : Service() { |
||||
override fun onBind(intent: Intent?): IBinder? { |
||||
return null |
||||
} |
||||
|
||||
} |
@ -0,0 +1,12 @@ |
||||
package io.legado.app.service |
||||
|
||||
import android.app.Service |
||||
import android.content.Intent |
||||
import android.os.IBinder |
||||
|
||||
class DownloadService : Service() { |
||||
override fun onBind(intent: Intent?): IBinder? { |
||||
return null |
||||
} |
||||
|
||||
} |
@ -0,0 +1,12 @@ |
||||
package io.legado.app.service |
||||
|
||||
import android.app.Service |
||||
import android.content.Intent |
||||
import android.os.IBinder |
||||
|
||||
class ReadAloudService : Service() { |
||||
override fun onBind(intent: Intent?): IBinder? { |
||||
return null |
||||
} |
||||
|
||||
} |
@ -0,0 +1,12 @@ |
||||
package io.legado.app.service |
||||
|
||||
import android.app.Service |
||||
import android.content.Intent |
||||
import android.os.IBinder |
||||
|
||||
class ShareService : Service() { |
||||
override fun onBind(intent: Intent?): IBinder? { |
||||
return null |
||||
} |
||||
|
||||
} |
@ -0,0 +1,12 @@ |
||||
package io.legado.app.service |
||||
|
||||
import android.app.Service |
||||
import android.content.Intent |
||||
import android.os.IBinder |
||||
|
||||
class UpdateService : Service() { |
||||
override fun onBind(intent: Intent?): IBinder? { |
||||
return null |
||||
} |
||||
|
||||
} |
@ -0,0 +1,12 @@ |
||||
package io.legado.app.service |
||||
|
||||
import android.app.Service |
||||
import android.content.Intent |
||||
import android.os.IBinder |
||||
|
||||
class WebService : Service() { |
||||
override fun onBind(intent: Intent?): IBinder? { |
||||
return null |
||||
} |
||||
|
||||
} |
@ -0,0 +1,35 @@ |
||||
package io.legado.app.ui.about |
||||
|
||||
import android.content.Intent |
||||
import android.net.Uri |
||||
import android.os.Bundle |
||||
import androidx.preference.Preference |
||||
import androidx.preference.PreferenceFragmentCompat |
||||
import io.legado.app.App |
||||
import io.legado.app.R |
||||
import io.legado.app.utils.toast |
||||
|
||||
class AboutFragment : PreferenceFragmentCompat() { |
||||
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) { |
||||
addPreferencesFromResource(R.xml.about) |
||||
findPreference<Preference>("version")?.summary = App.INSTANCE.versionName |
||||
} |
||||
|
||||
override fun onPreferenceTreeClick(preference: Preference?): Boolean { |
||||
when (preference?.key) { |
||||
|
||||
} |
||||
return super.onPreferenceTreeClick(preference) |
||||
} |
||||
|
||||
private fun openIntent(intentName: String, address: String) { |
||||
try { |
||||
val intent = Intent(intentName) |
||||
intent.data = Uri.parse(address) |
||||
startActivity(intent) |
||||
} catch (e: Exception) { |
||||
toast(R.string.can_not_open) |
||||
} |
||||
|
||||
} |
||||
} |
@ -0,0 +1,95 @@ |
||||
package io.legado.app.ui.about |
||||
|
||||
|
||||
import android.content.ClipData |
||||
import android.content.ClipboardManager |
||||
import android.content.Context |
||||
import android.content.Intent |
||||
import android.net.Uri |
||||
import android.os.Bundle |
||||
import android.widget.Toast |
||||
import androidx.lifecycle.AndroidViewModel |
||||
import io.legado.app.R |
||||
import io.legado.app.base.BaseActivity |
||||
import io.legado.app.utils.getViewModel |
||||
import kotlinx.android.synthetic.main.activity_donate.* |
||||
import kotlinx.android.synthetic.main.view_title_bar.* |
||||
import org.jetbrains.anko.toast |
||||
import java.net.URLEncoder |
||||
|
||||
/** |
||||
* Created by GKF on 2018/1/13. |
||||
* 捐赠页面 |
||||
*/ |
||||
|
||||
class DonateActivity : BaseActivity<AndroidViewModel>() { |
||||
|
||||
override val viewModel: AndroidViewModel |
||||
get() = getViewModel(AndroidViewModel::class.java) |
||||
|
||||
override val layoutID: Int |
||||
get() = R.layout.activity_donate |
||||
|
||||
override fun onViewModelCreated(viewModel: AndroidViewModel, savedInstanceState: Bundle?) { |
||||
setSupportActionBar(toolbar) |
||||
vw_zfb_tz.setOnClickListener { aliDonate(this) } |
||||
cv_wx_gzh.setOnClickListener { |
||||
val clipboard = this.getSystemService(Context.CLIPBOARD_SERVICE) as? ClipboardManager |
||||
val clipData = ClipData.newPlainText(null, "开源阅读软件") |
||||
clipboard?.let { |
||||
clipboard.primaryClip = clipData |
||||
toast(R.string.copy_complete) |
||||
} |
||||
} |
||||
vw_zfb_hb.setOnClickListener { openActionViewIntent("https://gedoor.github.io/MyBookshelf/zfbhbrwm.png") } |
||||
vw_zfb_rwm.setOnClickListener { openActionViewIntent("https://gedoor.github.io/MyBookshelf/zfbskrwm.jpg") } |
||||
vw_wx_rwm.setOnClickListener { openActionViewIntent("https://gedoor.github.io/MyBookshelf/wxskrwm.jpg") } |
||||
vw_qq_rwm.setOnClickListener { openActionViewIntent("https://gedoor.github.io/MyBookshelf/qqskrwm.jpg") } |
||||
vw_zfb_hb_ssm.setOnClickListener { getZfbHb(this) } |
||||
} |
||||
|
||||
private fun getZfbHb(context: Context) { |
||||
val clipboard = context.getSystemService(Context.CLIPBOARD_SERVICE) as? ClipboardManager |
||||
val clipData = ClipData.newPlainText(null, "537954522") |
||||
clipboard?.let { |
||||
clipboard.primaryClip = clipData |
||||
Toast.makeText(context, "高级功能已开启\n红包码已复制\n支付宝首页搜索“537954522” 立即领红包", Toast.LENGTH_LONG) |
||||
.show() |
||||
} |
||||
try { |
||||
val packageManager = context.applicationContext.packageManager |
||||
val intent = packageManager.getLaunchIntentForPackage("com.eg.android.AlipayGphone")!! |
||||
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) |
||||
context.startActivity(intent) |
||||
} catch (e: Exception) { |
||||
e.printStackTrace() |
||||
} finally { |
||||
|
||||
} |
||||
} |
||||
|
||||
private fun openActionViewIntent(address: String) { |
||||
try { |
||||
val intent = Intent(Intent.ACTION_VIEW) |
||||
intent.data = Uri.parse(address) |
||||
startActivity(intent) |
||||
} catch (e: Exception) { |
||||
e.printStackTrace() |
||||
Toast.makeText(this, R.string.can_not_open, Toast.LENGTH_SHORT).show() |
||||
} |
||||
|
||||
} |
||||
|
||||
private fun aliDonate(context: Context) { |
||||
try { |
||||
val qrCode = URLEncoder.encode("tsx06677nwdk3javroq4ef0", "utf-8") |
||||
val aliPayQr = |
||||
"alipayqr://platformapi/startapp?saId=10000007&qrcode=https://qr.alipay.com/$qrCode" |
||||
val intent = Intent(Intent.ACTION_VIEW, Uri.parse(aliPayQr)) |
||||
context.startActivity(intent) |
||||
} catch (e: Exception) { |
||||
e.printStackTrace() |
||||
} |
||||
|
||||
} |
||||
} |
@ -0,0 +1,19 @@ |
||||
package io.legado.app.ui.bookinfo |
||||
|
||||
import android.os.Bundle |
||||
import io.legado.app.R |
||||
import io.legado.app.base.BaseActivity |
||||
import io.legado.app.utils.getViewModel |
||||
|
||||
class BookInfoActivity : BaseActivity<BookInfoViewModel>() { |
||||
override val viewModel: BookInfoViewModel |
||||
get() = getViewModel(BookInfoViewModel::class.java) |
||||
override val layoutID: Int |
||||
get() = R.layout.activity_book_info |
||||
|
||||
override fun onViewModelCreated(viewModel: BookInfoViewModel, savedInstanceState: Bundle?) { |
||||
|
||||
} |
||||
|
||||
|
||||
} |
@ -0,0 +1,18 @@ |
||||
package io.legado.app.ui.bookinfo |
||||
|
||||
import android.os.Bundle |
||||
import io.legado.app.R |
||||
import io.legado.app.base.BaseActivity |
||||
import io.legado.app.utils.getViewModel |
||||
|
||||
class BookInfoEditActivity : BaseActivity<BookInfoViewModel>() { |
||||
override val viewModel: BookInfoViewModel |
||||
get() = getViewModel(BookInfoViewModel::class.java) |
||||
override val layoutID: Int |
||||
get() = R.layout.activity_book_info_edit |
||||
|
||||
override fun onViewModelCreated(viewModel: BookInfoViewModel, savedInstanceState: Bundle?) { |
||||
|
||||
} |
||||
|
||||
} |
@ -0,0 +1,8 @@ |
||||
package io.legado.app.ui.bookinfo |
||||
|
||||
import android.app.Application |
||||
import io.legado.app.base.BaseViewModel |
||||
|
||||
class BookInfoViewModel(application: Application) : BaseViewModel(application) { |
||||
|
||||
} |
@ -0,0 +1,87 @@ |
||||
package io.legado.app.ui.bookshelf |
||||
|
||||
import android.text.TextUtils.isEmpty |
||||
import android.view.LayoutInflater |
||||
import android.view.View |
||||
import android.view.ViewGroup |
||||
import androidx.paging.PagedListAdapter |
||||
import androidx.recyclerview.widget.DiffUtil |
||||
import androidx.recyclerview.widget.RecyclerView |
||||
import com.bumptech.glide.Glide |
||||
import com.bumptech.glide.load.engine.DiskCacheStrategy |
||||
import io.legado.app.R |
||||
import io.legado.app.data.entities.Book |
||||
import io.legado.app.lib.theme.ThemeStore |
||||
import kotlinx.android.synthetic.main.item_bookshelf_list.view.* |
||||
import kotlinx.android.synthetic.main.item_relace_rule.view.tv_name |
||||
import java.io.File |
||||
|
||||
class BookshelfAdapter : PagedListAdapter<Book, BookshelfAdapter.MyViewHolder>(DIFF_CALLBACK) { |
||||
|
||||
companion object { |
||||
@JvmField |
||||
val DIFF_CALLBACK = object : DiffUtil.ItemCallback<Book>() { |
||||
override fun areItemsTheSame(oldItem: Book, newItem: Book): Boolean = |
||||
oldItem.descUrl == newItem.descUrl |
||||
|
||||
override fun areContentsTheSame(oldItem: Book, newItem: Book): Boolean = |
||||
oldItem.descUrl == newItem.descUrl |
||||
&& oldItem.durChapterTitle == newItem.durChapterTitle |
||||
&& oldItem.latestChapterTitle == newItem.latestChapterTitle |
||||
} |
||||
} |
||||
|
||||
var callBack: CallBack? = null |
||||
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MyViewHolder { |
||||
return MyViewHolder( |
||||
LayoutInflater.from(parent.context).inflate( |
||||
R.layout.item_bookshelf_list, |
||||
parent, |
||||
false |
||||
) |
||||
) |
||||
} |
||||
|
||||
override fun onBindViewHolder(holder: MyViewHolder, position: Int) { |
||||
currentList?.get(position)?.let { |
||||
holder.bind(it, callBack) |
||||
} |
||||
} |
||||
|
||||
class MyViewHolder(view: View) : RecyclerView.ViewHolder(view) { |
||||
|
||||
init { |
||||
itemView.setBackgroundColor(ThemeStore.backgroundColor(itemView.context)) |
||||
} |
||||
|
||||
fun bind(book: Book, callBack: CallBack?) = with(itemView) { |
||||
tv_name.text = book.name |
||||
tv_author.text = book.author |
||||
tv_read.text = book.durChapterTitle |
||||
tv_last.text = book.latestChapterTitle |
||||
val cover = if (isEmpty(book.customCoverUrl)) book.coverUrl else book.customCoverUrl |
||||
cover?.let { |
||||
if (it.startsWith("http")) { |
||||
Glide.with(itemView).load(it) |
||||
.placeholder(R.drawable.img_cover_default) |
||||
.centerCrop() |
||||
.diskCacheStrategy(DiskCacheStrategy.RESOURCE) |
||||
.into(iv_cover) |
||||
} else { |
||||
Glide.with(itemView).load(File(it)) |
||||
.placeholder(R.drawable.img_cover_default) |
||||
.centerCrop() |
||||
.diskCacheStrategy(DiskCacheStrategy.RESOURCE) |
||||
.into(iv_cover) |
||||
} |
||||
} |
||||
itemView.setOnClickListener { callBack?.open(book) } |
||||
} |
||||
} |
||||
|
||||
interface CallBack { |
||||
fun open(book: Book) |
||||
fun search() |
||||
} |
||||
} |
@ -1,22 +1,16 @@ |
||||
package io.legado.app.ui.main |
||||
|
||||
import android.app.Application |
||||
import io.legado.app.App |
||||
import io.legado.app.base.BaseViewModel |
||||
import kotlinx.coroutines.async |
||||
import org.jetbrains.anko.toast |
||||
import androidx.lifecycle.AndroidViewModel |
||||
import io.legado.app.help.storage.Restore |
||||
import kotlinx.coroutines.GlobalScope |
||||
import kotlinx.coroutines.launch |
||||
|
||||
class MainViewModel(application: Application) : BaseViewModel(application) { |
||||
class MainViewModel(application: Application) : AndroidViewModel(application) { |
||||
|
||||
fun test() { |
||||
launchOnUI({ |
||||
|
||||
val result = async { |
||||
"结果" |
||||
} |
||||
|
||||
// App.INSTANCE.toast("result: $result") |
||||
}) |
||||
fun restore() { |
||||
GlobalScope.launch { |
||||
Restore.importYueDuData(getApplication()) |
||||
} |
||||
} |
||||
|
||||
} |
@ -0,0 +1,50 @@ |
||||
package io.legado.app.ui.welcome |
||||
|
||||
import android.animation.Animator |
||||
import android.animation.ValueAnimator |
||||
import android.os.Bundle |
||||
import androidx.lifecycle.AndroidViewModel |
||||
import io.legado.app.R |
||||
import io.legado.app.base.BaseActivity |
||||
import io.legado.app.lib.theme.ThemeStore |
||||
import io.legado.app.ui.main.MainActivity |
||||
import io.legado.app.utils.getViewModel |
||||
import kotlinx.android.synthetic.main.activity_welcome.* |
||||
import org.jetbrains.anko.startActivity |
||||
|
||||
class WelcomeActivity : BaseActivity<AndroidViewModel>() { |
||||
override val viewModel: AndroidViewModel |
||||
get() = getViewModel(AndroidViewModel::class.java) |
||||
override val layoutID: Int |
||||
get() = R.layout.activity_welcome |
||||
|
||||
override fun onViewModelCreated(viewModel: AndroidViewModel, savedInstanceState: Bundle?) { |
||||
iv_bg.setColorFilter(ThemeStore.accentColor(this)) |
||||
val welAnimator = ValueAnimator.ofFloat(1f, 0f).setDuration(800) |
||||
welAnimator.startDelay = 100 |
||||
welAnimator.addUpdateListener { animation -> |
||||
val alpha = animation.animatedValue as Float |
||||
iv_bg.alpha = alpha |
||||
} |
||||
welAnimator.addListener(object : Animator.AnimatorListener { |
||||
override fun onAnimationStart(animation: Animator) { |
||||
startActivity<MainActivity>() |
||||
finish() |
||||
} |
||||
|
||||
override fun onAnimationEnd(animation: Animator) { |
||||
|
||||
} |
||||
|
||||
override fun onAnimationCancel(animation: Animator) { |
||||
|
||||
} |
||||
|
||||
override fun onAnimationRepeat(animation: Animator) { |
||||
|
||||
} |
||||
}) |
||||
welAnimator.start() |
||||
} |
||||
|
||||
} |
@ -0,0 +1,150 @@ |
||||
/* |
||||
* Copyright (C) 2015 tyrantgit |
||||
* |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
* you may not use this file except in compliance with the License. |
||||
* You may obtain a copy of the License at |
||||
* |
||||
* http://www.apache.org/licenses/LICENSE-2.0 |
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
*/ |
||||
package io.legado.app.ui.widget.anima.explosion_field |
||||
|
||||
import android.animation.ValueAnimator |
||||
import android.graphics.* |
||||
import android.view.View |
||||
import android.view.animation.AccelerateInterpolator |
||||
import java.util.* |
||||
|
||||
class ExplosionAnimator(private val mContainer: View, bitmap: Bitmap, bound: Rect) : |
||||
ValueAnimator() { |
||||
private val mPaint: Paint |
||||
private val mParticles: Array<Particle?> |
||||
private val mBound: Rect |
||||
|
||||
init { |
||||
mPaint = Paint() |
||||
mBound = Rect(bound) |
||||
val partLen = 15 |
||||
mParticles = arrayOfNulls(partLen * partLen) |
||||
val random = Random(System.currentTimeMillis()) |
||||
val w = bitmap.width / (partLen + 2) |
||||
val h = bitmap.height / (partLen + 2) |
||||
for (i in 0 until partLen) { |
||||
for (j in 0 until partLen) { |
||||
mParticles[i * partLen + j] = |
||||
generateParticle(bitmap.getPixel((j + 1) * w, (i + 1) * h), random) |
||||
} |
||||
} |
||||
setFloatValues(0f, END_VALUE) |
||||
interpolator = DEFAULT_INTERPOLATOR |
||||
duration = DEFAULT_DURATION |
||||
} |
||||
|
||||
private fun generateParticle(color: Int, random: Random): Particle { |
||||
val particle = Particle() |
||||
particle.color = color |
||||
particle.radius = V |
||||
if (random.nextFloat() < 0.2f) { |
||||
particle.baseRadius = V + (X - V) * random.nextFloat() |
||||
} else { |
||||
particle.baseRadius = W + (V - W) * random.nextFloat() |
||||
} |
||||
val nextFloat = random.nextFloat() |
||||
particle.top = mBound.height() * (0.18f * random.nextFloat() + 0.2f) |
||||
particle.top = |
||||
if (nextFloat < 0.2f) particle.top else particle.top + particle.top * 0.2f * random.nextFloat() |
||||
particle.bottom = mBound.height() * (random.nextFloat() - 0.5f) * 1.8f |
||||
var f = |
||||
if (nextFloat < 0.2f) particle.bottom else if (nextFloat < 0.8f) particle.bottom * 0.6f else particle.bottom * 0.3f |
||||
particle.bottom = f |
||||
particle.mag = 4.0f * particle.top / particle.bottom |
||||
particle.neg = -particle.mag / particle.bottom |
||||
f = mBound.centerX() + Y * (random.nextFloat() - 0.5f) |
||||
particle.baseCx = f |
||||
particle.cx = f |
||||
f = mBound.centerY() + Y * (random.nextFloat() - 0.5f) |
||||
particle.baseCy = f |
||||
particle.cy = f |
||||
particle.life = END_VALUE / 10 * random.nextFloat() |
||||
particle.overflow = 0.4f * random.nextFloat() |
||||
particle.alpha = 1f |
||||
return particle |
||||
} |
||||
|
||||
fun draw(canvas: Canvas): Boolean { |
||||
if (!isStarted) { |
||||
return false |
||||
} |
||||
for (particle in mParticles) { |
||||
particle?.let { |
||||
particle.advance(animatedValue as Float) |
||||
if (particle.alpha > 0f) { |
||||
mPaint.color = particle.color |
||||
mPaint.alpha = (Color.alpha(particle.color) * particle.alpha).toInt() |
||||
canvas.drawCircle(particle.cx, particle.cy, particle.radius, mPaint) |
||||
} |
||||
} |
||||
} |
||||
mContainer.invalidate() |
||||
return true |
||||
} |
||||
|
||||
override fun start() { |
||||
super.start() |
||||
mContainer.invalidate(mBound) |
||||
} |
||||
|
||||
private inner class Particle { |
||||
internal var alpha: Float = 0.toFloat() |
||||
internal var color: Int = 0 |
||||
internal var cx: Float = 0.toFloat() |
||||
internal var cy: Float = 0.toFloat() |
||||
internal var radius: Float = 0.toFloat() |
||||
internal var baseCx: Float = 0.toFloat() |
||||
internal var baseCy: Float = 0.toFloat() |
||||
internal var baseRadius: Float = 0.toFloat() |
||||
internal var top: Float = 0.toFloat() |
||||
internal var bottom: Float = 0.toFloat() |
||||
internal var mag: Float = 0.toFloat() |
||||
internal var neg: Float = 0.toFloat() |
||||
internal var life: Float = 0.toFloat() |
||||
internal var overflow: Float = 0.toFloat() |
||||
|
||||
|
||||
fun advance(factor: Float) { |
||||
var f = 0f |
||||
var normalization = factor / END_VALUE |
||||
if (normalization < life || normalization > 1f - overflow) { |
||||
alpha = 0f |
||||
return |
||||
} |
||||
normalization = (normalization - life) / (1f - life - overflow) |
||||
val f2 = normalization * END_VALUE |
||||
if (normalization >= 0.7f) { |
||||
f = (normalization - 0.7f) / 0.3f |
||||
} |
||||
alpha = 1f - f |
||||
f = bottom * f2 |
||||
cx = baseCx + f |
||||
cy = (baseCy - this.neg * Math.pow(f.toDouble(), 2.0)).toFloat() - f * mag |
||||
radius = V + (baseRadius - V) * f2 |
||||
} |
||||
} |
||||
|
||||
companion object { |
||||
|
||||
internal var DEFAULT_DURATION: Long = 0x400 |
||||
private val DEFAULT_INTERPOLATOR = AccelerateInterpolator(0.6f) |
||||
private val END_VALUE = 1.4f |
||||
private val X = Utils.dp2Px(5).toFloat() |
||||
private val Y = Utils.dp2Px(20).toFloat() |
||||
private val V = Utils.dp2Px(2).toFloat() |
||||
private val W = Utils.dp2Px(1).toFloat() |
||||
} |
||||
} |
@ -0,0 +1,191 @@ |
||||
/* |
||||
* Copyright (C) 2015 tyrantgit |
||||
* |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
* you may not use this file except in compliance with the License. |
||||
* You may obtain a copy of the License at |
||||
* |
||||
* http://www.apache.org/licenses/LICENSE-2.0 |
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
*/ |
||||
package io.legado.app.ui.widget.anima.explosion_field |
||||
|
||||
import android.animation.Animator |
||||
import android.animation.AnimatorListenerAdapter |
||||
import android.animation.ValueAnimator |
||||
import android.app.Activity |
||||
import android.content.Context |
||||
import android.graphics.Bitmap |
||||
import android.graphics.Canvas |
||||
import android.graphics.Rect |
||||
import android.media.MediaPlayer |
||||
import android.util.AttributeSet |
||||
import android.util.Log |
||||
import android.view.View |
||||
import android.view.ViewGroup |
||||
import android.view.Window |
||||
import java.util.* |
||||
|
||||
|
||||
class ExplosionField : View { |
||||
|
||||
private var customDuration = ExplosionAnimator.DEFAULT_DURATION |
||||
private var idPlayAnimationEffect = 0 |
||||
private var mZAnimatorListener: OnAnimatorListener? = null |
||||
private var mOnClickListener: View.OnClickListener? = null |
||||
|
||||
private val mExplosions = ArrayList<ExplosionAnimator>() |
||||
private val mExpandInset = IntArray(2) |
||||
|
||||
constructor(context: Context) : super(context) { |
||||
init() |
||||
} |
||||
|
||||
constructor(context: Context, attrs: AttributeSet) : super(context, attrs) { |
||||
init() |
||||
} |
||||
|
||||
constructor(context: Context, attrs: AttributeSet, defStyleAttr: Int) : super( |
||||
context, |
||||
attrs, |
||||
defStyleAttr |
||||
) { |
||||
init() |
||||
} |
||||
|
||||
private fun init() { |
||||
|
||||
Arrays.fill(mExpandInset, Utils.dp2Px(32)) |
||||
} |
||||
|
||||
override fun onDraw(canvas: Canvas) { |
||||
super.onDraw(canvas) |
||||
for (explosion in mExplosions) { |
||||
explosion.draw(canvas) |
||||
} |
||||
} |
||||
|
||||
fun playSoundAnimationEffect(id: Int) { |
||||
this.idPlayAnimationEffect = id |
||||
} |
||||
|
||||
fun setCustomDuration(customDuration: Long) { |
||||
this.customDuration = customDuration |
||||
} |
||||
|
||||
fun addActionEvent(ievents: OnAnimatorListener) { |
||||
this.mZAnimatorListener = ievents |
||||
} |
||||
|
||||
|
||||
fun expandExplosionBound(dx: Int, dy: Int) { |
||||
mExpandInset[0] = dx |
||||
mExpandInset[1] = dy |
||||
} |
||||
|
||||
@JvmOverloads |
||||
fun explode(bitmap: Bitmap?, bound: Rect, startDelay: Long, view: View? = null) { |
||||
val currentDuration = customDuration |
||||
val explosion = ExplosionAnimator(this, bitmap!!, bound) |
||||
explosion.addListener(object : AnimatorListenerAdapter() { |
||||
override fun onAnimationEnd(animation: Animator) { |
||||
mExplosions.remove(animation) |
||||
if (view != null) { |
||||
view.scaleX = 1f |
||||
view.scaleY = 1f |
||||
view.alpha = 1f |
||||
view.setOnClickListener(mOnClickListener)//set event |
||||
|
||||
} |
||||
} |
||||
}) |
||||
explosion.startDelay = startDelay |
||||
explosion.duration = currentDuration |
||||
mExplosions.add(explosion) |
||||
explosion.start() |
||||
} |
||||
|
||||
@JvmOverloads |
||||
fun explode(view: View, restartState: Boolean? = false) { |
||||
|
||||
val r = Rect() |
||||
view.getGlobalVisibleRect(r) |
||||
val location = IntArray(2) |
||||
getLocationOnScreen(location) |
||||
// getLocationInWindow(location); |
||||
// view.getLocationInWindow(location); |
||||
r.offset(-location[0], -location[1]) |
||||
r.inset(-mExpandInset[0], -mExpandInset[1]) |
||||
val startDelay = 100 |
||||
val animator = ValueAnimator.ofFloat(0f, 1f).setDuration(150) |
||||
animator.addUpdateListener(object : ValueAnimator.AnimatorUpdateListener { |
||||
|
||||
internal var random = Random() |
||||
|
||||
override fun onAnimationUpdate(animation: ValueAnimator) { |
||||
view.translationX = (random.nextFloat() - 0.5f) * view.width.toFloat() * 0.05f |
||||
view.translationY = (random.nextFloat() - 0.5f) * view.height.toFloat() * 0.05f |
||||
} |
||||
}) |
||||
|
||||
animator.addListener(object : Animator.AnimatorListener { |
||||
override fun onAnimationStart(animator: Animator) { |
||||
if (idPlayAnimationEffect != 0) |
||||
MediaPlayer.create(context, idPlayAnimationEffect).start() |
||||
} |
||||
|
||||
override fun onAnimationEnd(animator: Animator) { |
||||
if (mZAnimatorListener != null) { |
||||
mZAnimatorListener!!.onAnimationEnd(animator, this@ExplosionField) |
||||
} |
||||
} |
||||
|
||||
override fun onAnimationCancel(animator: Animator) { |
||||
Log.i("PRUEBA", "CANCEL") |
||||
} |
||||
|
||||
override fun onAnimationRepeat(animator: Animator) { |
||||
Log.i("PRUEBA", "REPEAT") |
||||
} |
||||
}) |
||||
|
||||
animator.start() |
||||
view.animate().setDuration(150).setStartDelay(startDelay.toLong()).scaleX(0f).scaleY(0f) |
||||
.alpha(0f).start() |
||||
if (restartState!!) |
||||
explode(Utils.createBitmapFromView(view), r, startDelay.toLong(), view) |
||||
else |
||||
explode(Utils.createBitmapFromView(view), r, startDelay.toLong()) |
||||
|
||||
} |
||||
|
||||
fun clear() { |
||||
mExplosions.clear() |
||||
invalidate() |
||||
} |
||||
|
||||
override fun setOnClickListener(mOnClickListener: View.OnClickListener?) { |
||||
this.mOnClickListener = mOnClickListener |
||||
} |
||||
|
||||
companion object { |
||||
|
||||
fun attach2Window(activity: Activity): ExplosionField { |
||||
val rootView = activity.findViewById<View>(Window.ID_ANDROID_CONTENT) as ViewGroup |
||||
val explosionField = ExplosionField(activity) |
||||
rootView.addView( |
||||
explosionField, ViewGroup.LayoutParams( |
||||
ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT |
||||
) |
||||
) |
||||
return explosionField |
||||
} |
||||
} |
||||
|
||||
|
||||
} |
@ -0,0 +1,8 @@ |
||||
package io.legado.app.ui.widget.anima.explosion_field |
||||
|
||||
import android.animation.Animator |
||||
import android.view.View |
||||
|
||||
interface OnAnimatorListener { |
||||
fun onAnimationEnd(animator: Animator, view: View) |
||||
} |
@ -0,0 +1,76 @@ |
||||
/* |
||||
* Copyright (C) 2015 tyrantgit |
||||
* |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
* you may not use this file except in compliance with the License. |
||||
* You may obtain a copy of the License at |
||||
* |
||||
* http://www.apache.org/licenses/LICENSE-2.0 |
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
*/ |
||||
package io.legado.app.ui.widget.anima.explosion_field |
||||
|
||||
|
||||
import android.content.res.Resources |
||||
import android.graphics.Bitmap |
||||
import android.graphics.Canvas |
||||
import android.graphics.drawable.BitmapDrawable |
||||
import android.view.View |
||||
import android.widget.ImageView |
||||
|
||||
object Utils { |
||||
|
||||
private val DENSITY = Resources.getSystem().displayMetrics.density |
||||
private val sCanvas = Canvas() |
||||
|
||||
fun dp2Px(dp: Int): Int { |
||||
return Math.round(dp * DENSITY) |
||||
} |
||||
|
||||
fun createBitmapFromView(view: View): Bitmap? { |
||||
if (view is ImageView) { |
||||
val drawable = view.drawable |
||||
if (drawable != null && drawable is BitmapDrawable) { |
||||
return drawable.bitmap |
||||
} |
||||
} |
||||
view.clearFocus() |
||||
val bitmap = createBitmapSafely( |
||||
view.width, |
||||
view.height, Bitmap.Config.ARGB_8888, 1 |
||||
) |
||||
if (bitmap != null) { |
||||
synchronized(sCanvas) { |
||||
val canvas = sCanvas |
||||
canvas.setBitmap(bitmap) |
||||
view.draw(canvas) |
||||
canvas.setBitmap(null) |
||||
} |
||||
} |
||||
return bitmap |
||||
} |
||||
|
||||
fun createBitmapSafely( |
||||
width: Int, |
||||
height: Int, |
||||
config: Bitmap.Config, |
||||
retryCount: Int |
||||
): Bitmap? { |
||||
try { |
||||
return Bitmap.createBitmap(width, height, config) |
||||
} catch (e: OutOfMemoryError) { |
||||
e.printStackTrace() |
||||
if (retryCount > 0) { |
||||
System.gc() |
||||
return createBitmapSafely(width, height, config, retryCount - 1) |
||||
} |
||||
return null |
||||
} |
||||
|
||||
} |
||||
} |
@ -1,5 +1,34 @@ |
||||
package io.legado.app.utils |
||||
|
||||
import android.text.TextUtils |
||||
import com.google.gson.Gson |
||||
import com.google.gson.GsonBuilder |
||||
import com.google.gson.JsonParser |
||||
|
||||
inline fun <reified T> Gson.fromJson(json: String) = fromJson(json, T::class.java) |
||||
inline fun <reified T> Gson.fromJson(json: String): T = fromJson(json, T::class.java) |
||||
|
||||
inline fun <reified T> Gson.arrayFromJson(json: String): ArrayList<T>? = kotlin.run { |
||||
var result: ArrayList<T>? = null |
||||
if (!TextUtils.isEmpty(json)) { |
||||
val gson = GsonBuilder().create() |
||||
try { |
||||
val parser = JsonParser() |
||||
val jArray = parser.parse(json).asJsonArray |
||||
jArray?.let { |
||||
result = java.util.ArrayList() |
||||
for (obj in it) { |
||||
try { |
||||
val cse = gson.fromJson(obj, T::class.java) |
||||
result?.add(cse) |
||||
} catch (e: Exception) { |
||||
e.printStackTrace() |
||||
} |
||||
} |
||||
} |
||||
} catch (e: Exception) { |
||||
e.printStackTrace() |
||||
} |
||||
|
||||
} |
||||
return result |
||||
} |
@ -1,14 +1,49 @@ |
||||
<?xml version="1.0" encoding="utf-8"?> |
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" |
||||
xmlns:tools="http://schemas.android.com/tools" |
||||
xmlns:app="http://schemas.android.com/apk/res-auto" |
||||
android:layout_width="match_parent" android:layout_height="match_parent" |
||||
tools:context="io.legado.app.ui.about.AboutActivity"> |
||||
xmlns:app="http://schemas.android.com/apk/res-auto" |
||||
xmlns:tools="http://schemas.android.com/tools" |
||||
android:layout_width="match_parent" |
||||
android:layout_height="match_parent" |
||||
android:orientation="vertical" |
||||
tools:context="io.legado.app.ui.about.AboutActivity"> |
||||
|
||||
<io.legado.app.ui.widget.TitleBar |
||||
android:id="@+id/title_bar" |
||||
android:id="@+id/title_bar" |
||||
android:layout_width="match_parent" |
||||
android:layout_height="wrap_content" |
||||
app:title="@string/about" /> |
||||
|
||||
<androidx.cardview.widget.CardView |
||||
android:layout_width="match_parent" |
||||
android:layout_height="wrap_content" |
||||
android:layout_margin="6dp" |
||||
app:cardBackgroundColor="@color/background_card"> |
||||
|
||||
<LinearLayout |
||||
android:layout_width="match_parent" |
||||
android:layout_height="wrap_content" |
||||
app:title="@string/about"/> |
||||
android:orientation="vertical" |
||||
android:padding="10dp"> |
||||
|
||||
<TextView |
||||
android:layout_width="wrap_content" |
||||
android:layout_height="wrap_content" |
||||
android:layout_gravity="center" |
||||
android:text="@string/app_name" |
||||
android:textSize="20sp" |
||||
android:textStyle="bold" /> |
||||
|
||||
<TextView |
||||
android:id="@+id/tv_app_summary" |
||||
android:layout_width="match_parent" |
||||
android:layout_height="wrap_content" |
||||
android:text="@string/about_description" /> |
||||
</LinearLayout> |
||||
|
||||
</androidx.cardview.widget.CardView> |
||||
|
||||
<FrameLayout |
||||
android:id="@+id/fl_fragment" |
||||
android:layout_width="match_parent" |
||||
android:layout_height="match_parent" /> |
||||
</LinearLayout> |
||||
|
@ -0,0 +1,7 @@ |
||||
<?xml version="1.0" encoding="utf-8"?> |
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" |
||||
android:orientation="vertical" |
||||
android:layout_width="match_parent" |
||||
android:layout_height="match_parent"> |
||||
|
||||
</LinearLayout> |
@ -0,0 +1,7 @@ |
||||
<?xml version="1.0" encoding="utf-8"?> |
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" |
||||
android:orientation="vertical" |
||||
android:layout_width="match_parent" |
||||
android:layout_height="match_parent"> |
||||
|
||||
</LinearLayout> |
@ -0,0 +1,234 @@ |
||||
<?xml version="1.0" encoding="utf-8"?> |
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" |
||||
xmlns:app="http://schemas.android.com/apk/res-auto" |
||||
android:id="@+id/ll_content" |
||||
android:layout_width="match_parent" |
||||
android:layout_height="match_parent" |
||||
android:orientation="vertical"> |
||||
|
||||
<io.legado.app.ui.widget.TitleBar |
||||
android:id="@+id/title_bar" |
||||
android:layout_width="match_parent" |
||||
android:layout_height="wrap_content" |
||||
app:title="@string/donate" /> |
||||
|
||||
<ScrollView |
||||
android:layout_width="match_parent" |
||||
android:layout_height="match_parent" |
||||
android:layout_margin="5dp"> |
||||
|
||||
<LinearLayout |
||||
android:layout_width="match_parent" |
||||
android:layout_height="wrap_content" |
||||
android:orientation="vertical"> |
||||
|
||||
<androidx.cardview.widget.CardView |
||||
android:layout_width="match_parent" |
||||
android:layout_height="wrap_content" |
||||
android:layout_margin="5dp" |
||||
app:cardBackgroundColor="@color/background_card"> |
||||
|
||||
<LinearLayout |
||||
android:layout_width="match_parent" |
||||
android:layout_height="wrap_content" |
||||
android:orientation="vertical"> |
||||
|
||||
<TextView |
||||
android:layout_width="match_parent" |
||||
android:layout_height="wrap_content" |
||||
android:layout_margin="10dp" |
||||
android:text="你的支持是我更新的动力" /> |
||||
|
||||
</LinearLayout> |
||||
|
||||
</androidx.cardview.widget.CardView> |
||||
|
||||
<androidx.cardview.widget.CardView |
||||
android:id="@+id/cv_wx_gzh" |
||||
android:layout_width="match_parent" |
||||
android:layout_height="wrap_content" |
||||
android:layout_margin="5dp" |
||||
android:clickable="true" |
||||
android:focusable="true" |
||||
android:foreground="?attr/selectableItemBackground" |
||||
app:cardBackgroundColor="@color/background_card"> |
||||
|
||||
<LinearLayout |
||||
android:layout_width="match_parent" |
||||
android:layout_height="wrap_content" |
||||
android:orientation="vertical"> |
||||
|
||||
<TextView |
||||
android:layout_width="match_parent" |
||||
android:layout_height="wrap_content" |
||||
android:layout_margin="10dp" |
||||
android:text="请关注微信公众号 开源阅读软件 点击复制" /> |
||||
</LinearLayout> |
||||
</androidx.cardview.widget.CardView> |
||||
|
||||
<androidx.cardview.widget.CardView |
||||
android:id="@+id/vw_zfb_hb_ssm" |
||||
android:layout_width="match_parent" |
||||
android:layout_height="wrap_content" |
||||
android:layout_margin="5dp" |
||||
android:clickable="true" |
||||
android:focusable="true" |
||||
android:foreground="?attr/selectableItemBackground" |
||||
app:cardBackgroundColor="@color/background_card"> |
||||
|
||||
<LinearLayout |
||||
android:layout_width="match_parent" |
||||
android:layout_height="wrap_content" |
||||
android:orientation="horizontal" |
||||
android:padding="5dp"> |
||||
|
||||
<TextView |
||||
android:layout_width="match_parent" |
||||
android:layout_height="wrap_content" |
||||
android:layout_gravity="center" |
||||
android:layout_margin="5dp" |
||||
android:text="支付宝红包搜索码 537954522 点击复制" /> |
||||
|
||||
</LinearLayout> |
||||
|
||||
</androidx.cardview.widget.CardView> |
||||
|
||||
<androidx.cardview.widget.CardView |
||||
android:id="@+id/vw_zfb_tz" |
||||
android:layout_width="match_parent" |
||||
android:layout_height="wrap_content" |
||||
android:layout_margin="5dp" |
||||
android:clickable="true" |
||||
android:focusable="true" |
||||
android:foreground="?attr/selectableItemBackground" |
||||
app:cardBackgroundColor="@color/background_card"> |
||||
|
||||
<LinearLayout |
||||
android:layout_width="match_parent" |
||||
android:layout_height="wrap_content" |
||||
android:orientation="horizontal" |
||||
android:padding="5dp"> |
||||
|
||||
<TextView |
||||
android:layout_width="match_parent" |
||||
android:layout_height="wrap_content" |
||||
android:layout_gravity="center" |
||||
android:layout_margin="5dp" |
||||
android:text="支付宝收款,支持红包,点击直接跳转支付宝" /> |
||||
|
||||
</LinearLayout> |
||||
|
||||
</androidx.cardview.widget.CardView> |
||||
|
||||
<androidx.cardview.widget.CardView |
||||
android:id="@+id/vw_zfb_hb" |
||||
android:layout_width="match_parent" |
||||
android:layout_height="wrap_content" |
||||
android:layout_margin="5dp" |
||||
android:clickable="true" |
||||
android:focusable="true" |
||||
android:foreground="?attr/selectableItemBackground" |
||||
app:cardBackgroundColor="@color/background_card"> |
||||
|
||||
<LinearLayout |
||||
android:layout_width="match_parent" |
||||
android:layout_height="wrap_content" |
||||
android:orientation="horizontal" |
||||
android:padding="5dp"> |
||||
|
||||
<TextView |
||||
android:layout_width="match_parent" |
||||
android:layout_height="wrap_content" |
||||
android:layout_gravity="center" |
||||
android:layout_margin="5dp" |
||||
android:text="支付宝红包二维码" /> |
||||
|
||||
</LinearLayout> |
||||
|
||||
</androidx.cardview.widget.CardView> |
||||
|
||||
<androidx.cardview.widget.CardView |
||||
android:id="@+id/vw_zfb_rwm" |
||||
android:layout_width="match_parent" |
||||
android:layout_height="wrap_content" |
||||
android:layout_margin="5dp" |
||||
android:clickable="true" |
||||
android:focusable="true" |
||||
android:foreground="?attr/selectableItemBackground" |
||||
app:cardBackgroundColor="@color/background_card"> |
||||
|
||||
<LinearLayout |
||||
android:layout_width="match_parent" |
||||
android:layout_height="wrap_content" |
||||
android:orientation="horizontal" |
||||
android:padding="5dp"> |
||||
|
||||
<TextView |
||||
android:layout_width="match_parent" |
||||
android:layout_height="wrap_content" |
||||
android:layout_gravity="center" |
||||
android:layout_margin="5dp" |
||||
android:text="支付宝收款二维码" /> |
||||
|
||||
</LinearLayout> |
||||
|
||||
</androidx.cardview.widget.CardView> |
||||
|
||||
<androidx.cardview.widget.CardView |
||||
android:id="@+id/vw_wx_rwm" |
||||
android:layout_width="match_parent" |
||||
android:layout_height="wrap_content" |
||||
android:layout_margin="5dp" |
||||
android:clickable="true" |
||||
android:focusable="true" |
||||
android:foreground="?attr/selectableItemBackground" |
||||
app:cardBackgroundColor="@color/background_card"> |
||||
|
||||
<LinearLayout |
||||
android:layout_width="match_parent" |
||||
android:layout_height="wrap_content" |
||||
android:orientation="horizontal" |
||||
android:padding="5dp"> |
||||
|
||||
<TextView |
||||
android:layout_width="match_parent" |
||||
android:layout_height="wrap_content" |
||||
android:layout_gravity="center" |
||||
android:layout_margin="5dp" |
||||
android:text="微信赞赏码" /> |
||||
|
||||
</LinearLayout> |
||||
|
||||
</androidx.cardview.widget.CardView> |
||||
|
||||
<androidx.cardview.widget.CardView |
||||
android:id="@+id/vw_qq_rwm" |
||||
android:layout_width="match_parent" |
||||
android:layout_height="wrap_content" |
||||
android:layout_margin="5dp" |
||||
android:clickable="true" |
||||
android:focusable="true" |
||||
android:foreground="?attr/selectableItemBackground" |
||||
app:cardBackgroundColor="@color/background_card"> |
||||
|
||||
<LinearLayout |
||||
android:layout_width="match_parent" |
||||
android:layout_height="wrap_content" |
||||
android:orientation="horizontal" |
||||
android:padding="5dp"> |
||||
|
||||
<TextView |
||||
android:layout_width="match_parent" |
||||
android:layout_height="wrap_content" |
||||
android:layout_gravity="center" |
||||
android:layout_margin="5dp" |
||||
android:text="QQ收款码" /> |
||||
|
||||
</LinearLayout> |
||||
|
||||
</androidx.cardview.widget.CardView> |
||||
|
||||
</LinearLayout> |
||||
</ScrollView> |
||||
|
||||
</LinearLayout> |
@ -0,0 +1,15 @@ |
||||
<?xml version="1.0" encoding="utf-8"?> |
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" |
||||
android:orientation="vertical" |
||||
android:layout_width="match_parent" |
||||
android:layout_height="match_parent"> |
||||
|
||||
<ImageView |
||||
android:id="@+id/iv_bg" |
||||
android:layout_width="wrap_content" |
||||
android:layout_height="match_parent" |
||||
android:src="@drawable/image_welcome" |
||||
android:scaleType="fitCenter" |
||||
android:contentDescription="@string/welcome" /> |
||||
|
||||
</LinearLayout> |
@ -1,41 +1,42 @@ |
||||
<?xml version="1.0" encoding="utf-8"?> |
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" |
||||
xmlns:app="http://schemas.android.com/apk/res-auto" |
||||
android:layout_width="match_parent" |
||||
android:layout_height="match_parent" |
||||
android:orientation="vertical"> |
||||
xmlns:app="http://schemas.android.com/apk/res-auto" |
||||
android:layout_width="match_parent" |
||||
android:layout_height="match_parent" |
||||
android:orientation="vertical"> |
||||
|
||||
<io.legado.app.ui.widget.TitleBar |
||||
android:id="@+id/title_bar" |
||||
android:layout_width="match_parent" |
||||
android:layout_height="wrap_content" |
||||
app:attachToActivity="false" |
||||
app:title="@string/bookshelf"/> |
||||
android:id="@+id/title_bar" |
||||
android:layout_width="match_parent" |
||||
android:layout_height="wrap_content" |
||||
app:attachToActivity="false" |
||||
app:title="@string/bookshelf" /> |
||||
|
||||
<androidx.recyclerview.widget.RecyclerView |
||||
android:id="@+id/rv_book_group" |
||||
android:layout_width="match_parent" |
||||
android:layout_height="wrap_content"/> |
||||
android:id="@+id/rv_book_group" |
||||
android:layout_width="match_parent" |
||||
android:layout_height="wrap_content" /> |
||||
|
||||
<TextView |
||||
android:id="@+id/tv_recent_reading" |
||||
android:layout_width="match_parent" |
||||
android:layout_height="wrap_content" |
||||
android:background="@color/background" |
||||
android:elevation="3dp" |
||||
android:padding="5dp" |
||||
android:text="@string/recent_reading"/> |
||||
android:id="@+id/tv_recent_reading" |
||||
android:layout_width="match_parent" |
||||
android:layout_height="wrap_content" |
||||
android:background="@color/background" |
||||
android:elevation="3dp" |
||||
android:focusable="true" |
||||
android:padding="5dp" |
||||
android:text="@string/recent_reading" /> |
||||
|
||||
<androidx.swiperefreshlayout.widget.SwipeRefreshLayout |
||||
android:id="@+id/refresh_layout" |
||||
android:layout_width="match_parent" |
||||
android:layout_height="0dp" |
||||
android:layout_weight="1"> |
||||
android:id="@+id/refresh_layout" |
||||
android:layout_width="match_parent" |
||||
android:layout_height="0dp" |
||||
android:layout_weight="1"> |
||||
|
||||
<androidx.recyclerview.widget.RecyclerView |
||||
android:id="@+id/rv_bookshelf" |
||||
android:layout_width="match_parent" |
||||
android:layout_height="match_parent"/> |
||||
android:id="@+id/rv_bookshelf" |
||||
android:layout_width="match_parent" |
||||
android:layout_height="match_parent" /> |
||||
|
||||
</androidx.swiperefreshlayout.widget.SwipeRefreshLayout> |
||||
</LinearLayout> |
@ -0,0 +1,9 @@ |
||||
<?xml version="1.0" encoding="utf-8"?> |
||||
<menu xmlns:app="http://schemas.android.com/apk/res-auto" |
||||
xmlns:android="http://schemas.android.com/apk/res/android"> |
||||
|
||||
<item |
||||
android:id="@+id/menu_scoring" |
||||
android:title="@string/scoring" |
||||
app:showAsAction="always" /> |
||||
</menu> |
Before Width: | Height: | Size: 1.1 KiB After Width: | Height: | Size: 6.3 KiB |
Before Width: | Height: | Size: 979 B After Width: | Height: | Size: 17 KiB |
Before Width: | Height: | Size: 3.0 KiB After Width: | Height: | Size: 9.3 KiB |
Before Width: | Height: | Size: 841 B After Width: | Height: | Size: 3.2 KiB |
Before Width: | Height: | Size: 673 B After Width: | Height: | Size: 8.1 KiB |
Before Width: | Height: | Size: 1.9 KiB After Width: | Height: | Size: 4.7 KiB |
Before Width: | Height: | Size: 1.5 KiB After Width: | Height: | Size: 10 KiB |
Before Width: | Height: | Size: 1.4 KiB After Width: | Height: | Size: 28 KiB |
Before Width: | Height: | Size: 4.2 KiB After Width: | Height: | Size: 15 KiB |
Before Width: | Height: | Size: 2.5 KiB After Width: | Height: | Size: 20 KiB |
Before Width: | Height: | Size: 2.4 KiB After Width: | Height: | Size: 60 KiB |
Before Width: | Height: | Size: 6.8 KiB After Width: | Height: | Size: 30 KiB |
Before Width: | Height: | Size: 3.4 KiB After Width: | Height: | Size: 33 KiB |
Before Width: | Height: | Size: 3.6 KiB After Width: | Height: | Size: 100 KiB |
Before Width: | Height: | Size: 9.8 KiB After Width: | Height: | Size: 48 KiB |
@ -1,4 +1,4 @@ |
||||
<?xml version="1.0" encoding="utf-8"?> |
||||
<resources> |
||||
<color name="ic_launcher_background">#EAE7EE</color> |
||||
<color name="ic_launcher_background">#EC5436</color> |
||||
</resources> |
@ -0,0 +1,51 @@ |
||||
<?xml version="1.0" encoding="utf-8"?> |
||||
<androidx.preference.PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android" |
||||
xmlns:app="http://schemas.android.com/apk/res-auto"> |
||||
|
||||
<androidx.preference.Preference |
||||
android:key="isNightTheme" |
||||
android:title="开发人员" |
||||
android:summary="gedoor, Invinciblelee, atbest, Antecer, mabDc" |
||||
app:iconSpaceReserved="false" /> |
||||
|
||||
<androidx.preference.Preference |
||||
android:key="version" |
||||
android:title="@string/version" |
||||
app:iconSpaceReserved="false" /> |
||||
|
||||
<androidx.preference.Preference |
||||
android:key="update_log" |
||||
android:title="@string/update_log" |
||||
app:iconSpaceReserved="false" /> |
||||
|
||||
<androidx.preference.Preference |
||||
android:key="check_update" |
||||
android:title="@string/check_update" |
||||
app:iconSpaceReserved="false" /> |
||||
|
||||
<androidx.preference.Preference |
||||
android:key="qq" |
||||
android:title="@string/join_qq_group" |
||||
app:iconSpaceReserved="false" /> |
||||
|
||||
<androidx.preference.Preference |
||||
android:key="mail" |
||||
android:title="@string/send_mail" |
||||
app:iconSpaceReserved="false" /> |
||||
|
||||
<androidx.preference.Preference |
||||
android:key="git" |
||||
android:title="@string/git_hub" |
||||
app:iconSpaceReserved="false" /> |
||||
|
||||
<androidx.preference.Preference |
||||
android:key="home_page" |
||||
android:title="@string/home_page" |
||||
app:iconSpaceReserved="false" /> |
||||
|
||||
<androidx.preference.Preference |
||||
android:key="disclaimer" |
||||
android:title="@string/disclaimer" |
||||
app:iconSpaceReserved="false" /> |
||||
|
||||
</androidx.preference.PreferenceScreen> |