# 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 |
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 |
package io.legado.app.ui.main |
||||||
|
|
||||||
import android.app.Application |
import android.app.Application |
||||||
import io.legado.app.App |
import androidx.lifecycle.AndroidViewModel |
||||||
import io.legado.app.base.BaseViewModel |
import io.legado.app.help.storage.Restore |
||||||
import kotlinx.coroutines.async |
import kotlinx.coroutines.GlobalScope |
||||||
import org.jetbrains.anko.toast |
import kotlinx.coroutines.launch |
||||||
|
|
||||||
class MainViewModel(application: Application) : BaseViewModel(application) { |
class MainViewModel(application: Application) : AndroidViewModel(application) { |
||||||
|
|
||||||
fun test() { |
fun restore() { |
||||||
launchOnUI({ |
GlobalScope.launch { |
||||||
|
Restore.importYueDuData(getApplication()) |
||||||
val result = async { |
|
||||||
"结果" |
|
||||||
} |
} |
||||||
|
|
||||||
// App.INSTANCE.toast("result: $result") |
|
||||||
}) |
|
||||||
} |
} |
||||||
|
|
||||||
} |
} |
@ -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 |
package io.legado.app.utils |
||||||
|
|
||||||
|
import android.text.TextUtils |
||||||
import com.google.gson.Gson |
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"?> |
<?xml version="1.0" encoding="utf-8"?> |
||||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" |
<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" |
xmlns:app="http://schemas.android.com/apk/res-auto" |
||||||
android:layout_width="match_parent" android:layout_height="match_parent" |
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"> |
tools:context="io.legado.app.ui.about.AboutActivity"> |
||||||
|
|
||||||
<io.legado.app.ui.widget.TitleBar |
<io.legado.app.ui.widget.TitleBar |
||||||
android:id="@+id/title_bar" |
android:id="@+id/title_bar" |
||||||
android:layout_width="match_parent" |
android:layout_width="match_parent" |
||||||
android:layout_height="wrap_content" |
android:layout_height="wrap_content" |
||||||
app:title="@string/about"/> |
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" |
||||||
|
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> |
</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> |
@ -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"?> |
<?xml version="1.0" encoding="utf-8"?> |
||||||
<resources> |
<resources> |
||||||
<color name="ic_launcher_background">#EAE7EE</color> |
<color name="ic_launcher_background">#EC5436</color> |
||||||
</resources> |
</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> |