# 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> | ||||