This commit is contained in:
LUX-Timber 2024-04-11 14:00:08 +08:00
parent deb979c591
commit 705a9f620d
13 changed files with 352 additions and 33 deletions

View File

@ -3,7 +3,20 @@
<component name="deploymentTargetDropDown">
<value>
<entry key="app">
<State />
<State>
<runningDeviceTargetSelectedWithDropDown>
<Target>
<type value="RUNNING_DEVICE_TARGET" />
<deviceKey>
<Key>
<type value="SERIAL_NUMBER" />
<value value="ZX1G22HXL8" />
</Key>
</deviceKey>
</Target>
</runningDeviceTargetSelectedWithDropDown>
<timeTargetWasSelectedWithDropDown value="2024-04-11T05:55:42.206591900Z" />
</State>
</entry>
</value>
</component>

1
.idea/misc.xml generated
View File

@ -1,4 +1,3 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ExternalStorageConfigurationManager" enabled="true" />
<component name="ProjectRootManager" version="2" languageLevel="JDK_17" default="true" project-jdk-name="jbr-17" project-jdk-type="JavaSDK">

View File

@ -18,4 +18,10 @@
# If you keep the line number information, uncomment this to
# hide the original source file name.
#-renamesourcefileattribute SourceFile
#-renamesourcefileattribute SourceFile
-keep class com.timber.soft.myemoticon.model.**{ *; }
-keep class com.google.gson.** { *; }
-keepattributes Signature
-keep class com.google.gson.reflect.TypeToken { *; }
-keep class * extends com.google.gson.reflect.TypeToken
-keepattributes AnnotationDefault,RuntimeVisibleAnnotations

View File

@ -24,7 +24,16 @@
</intent-filter>
</activity>
<activity android:name=".SetDetailsActivity" />
<activity android:name=".SetDetailsActivity"
android:exported="false"
/>
<provider
android:name=".StickerProvider"
android:authorities="com.timber.soft.myemoticon.StickerProvider"
android:enabled="true"
android:exported="true"
android:readPermission="com.whatsapp.sticker.READ" />
</application>
</manifest>

View File

@ -34,7 +34,7 @@ class MainViewPagerFragment(private val rootModel: RootDataModel) : Fragment() {
requireContext(),
rootModel,
OnItemClickListenerImpl(requireContext())
)
)
recyclerViewList.adapter = pagerAdapter
return view
@ -95,7 +95,7 @@ class MainHomeCardAdapter(
childModel.count,
childModel,
OnItemClickListenerImpl(context)
)
)
holder.recyclerPreview.adapter = cardImgAdapter
@ -131,10 +131,11 @@ class CardImgAdapter(
override fun onBindViewHolder(holder: ImgViewHolder, position: Int) {
val preUrl: String = urlList[position]
if (preUrl != "xxx"){
if (preUrl != "xxx") {
try {
Glide.with(context).load(preUrl)
.transition(DrawableTransitionOptions.withCrossFade())
.error(R.drawable.svg_img_error)
.into(holder.preCardImg)
} catch (e: Exception) {
e.printStackTrace()

View File

@ -1,7 +1,9 @@
package com.timber.soft.myemoticon
import android.content.Context
import android.content.Intent
import android.graphics.Color
import android.net.Uri
import android.os.Build
import android.os.Bundle
import android.view.LayoutInflater
@ -10,6 +12,7 @@ import android.view.ViewGroup
import android.widget.ImageView
import android.widget.TextView
import android.widget.Toast
import androidx.appcompat.app.AlertDialog
import androidx.appcompat.app.AppCompatActivity
import androidx.recyclerview.widget.GridLayoutManager
import androidx.recyclerview.widget.RecyclerView
@ -17,7 +20,6 @@ import com.bumptech.glide.Glide
import com.bumptech.glide.load.resource.drawable.DrawableTransitionOptions
import com.timber.soft.myemoticon.databinding.ActivityDetailsBinding
import com.timber.soft.myemoticon.model.ChildDataModel
import com.timber.soft.myemoticon.tools.AppTools
import com.timber.soft.myemoticon.tools.AppTools.downLoadFile
import com.timber.soft.myemoticon.tools.AppTools.dpCovertPx
import com.timber.soft.myemoticon.tools.AppVal
@ -29,7 +31,6 @@ class SetDetailsActivity : AppCompatActivity(), DownloadListener {
private lateinit var binding: ActivityDetailsBinding
private lateinit var identifierName: String
private lateinit var dataModel: ChildDataModel
private var data: MutableList<File> = mutableListOf()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
@ -49,12 +50,6 @@ class SetDetailsActivity : AppCompatActivity(), DownloadListener {
binding.backBt.setOnClickListener() {
finish()
}
/**
* TODO no implement
*/
binding.addIconBt.setOnClickListener() {
}
binding.emoTitle.text = dataModel.title
@ -62,19 +57,48 @@ class SetDetailsActivity : AppCompatActivity(), DownloadListener {
val newPath: String = "$cacheDir/$identifierName"
val zipUrl = dataModel.zipUrl
val file = File(newPath)
if (!file.exists()) {
downLoadFile(this@SetDetailsActivity, zipUrl, newPath, this)
} else {
initImgData(newPath)
}
val stickerDetailsAdapter = StickerDetailsAdapter(
this@SetDetailsActivity, data
)
binding.recyclerSticker.adapter = stickerDetailsAdapter
binding.recyclerSticker.layoutManager = GridLayoutManager(this@SetDetailsActivity, 3)
binding.addIconBt.setOnClickListener() {
val intent = Intent()
intent.setAction(AppVal.STICKER_ACTION)
intent.putExtra(AppVal.KEY_PACK_ID, identifierName)
intent.putExtra(AppVal.KEY_PACK_AUTHORITY, AppVal.AUTHOR)
intent.putExtra(AppVal.KEY_PACK_NAME, title)
try {
startActivityForResult(intent, 200)
} catch (e: Exception) {
Toast.makeText(
applicationContext, "WhatsApp not found", Toast.LENGTH_SHORT
).show()
val builder = AlertDialog.Builder(this@SetDetailsActivity)
builder
.setTitle("WhatsApp is not installed")
.setMessage("Do you want to install?").setCancelable(false)
.setPositiveButton("OK") { dialog, _ ->
dialog.dismiss()
// 执行安装WhatsApp的代码或者跳转到Google Play Store
val url = getString(R.string.whatsapp_link)
// 创建intent打开链接
val intent = Intent(Intent.ACTION_VIEW)
intent.setData(Uri.parse(url))
startActivity(intent)
}
.setNegativeButton("Cancel") { dialog, _ ->
dialog.dismiss()
// 取消操作
}
val dialog = builder.create()
dialog.show()
}
}
}
override fun downloadListener(
@ -91,32 +115,42 @@ class SetDetailsActivity : AppCompatActivity(), DownloadListener {
}
private fun initImgData(newPath: String) {
val data: MutableList<File> = mutableListOf()
val file = File(newPath)
if (!file.exists()) {
binding.progressBar.visibility = View.GONE
Toast.makeText(
applicationContext, "no file", Toast.LENGTH_SHORT
applicationContext, "Check network connection!", Toast.LENGTH_SHORT
).show()
return
}
val fileList = file.listFiles()
if (fileList == null) {
binding.progressBar.visibility = View.GONE
Toast.makeText(
applicationContext, "no file2", Toast.LENGTH_SHORT
applicationContext, "Check network connection!", Toast.LENGTH_SHORT
).show()
return
}
for (listFile in fileList) {
val name = listFile.getName()
if (name == "tray.webp") {
Glide.with(this@SetDetailsActivity).load(listFile).into(binding.emoIcon)
Glide.with(this@SetDetailsActivity).load(listFile)
.error(R.drawable.svg_img_error)
.into(binding.emoIcon)
} else if (listFile.getName().endsWith(".webp")) {
data.add(listFile)
} else {
val sp = this.getSharedPreferences("", Context.MODE_PRIVATE)
sp.edit().putString(identifierName, listFile.absolutePath).apply()
}
}
val stickerDetailsAdapter = StickerDetailsAdapter(
this@SetDetailsActivity, data
)
binding.recyclerSticker.adapter = stickerDetailsAdapter
binding.recyclerSticker.layoutManager = GridLayoutManager(this@SetDetailsActivity, 3)
binding.progressBar.visibility = View.GONE
}
@ -147,7 +181,9 @@ class StickerDetailsAdapter(
val preFile = data[position]
Glide.with(context).load(preFile).transition(
DrawableTransitionOptions.withCrossFade()
).into(holder.stickerImg)
)
.error(R.drawable.svg_img_error)
.into(holder.stickerImg)
holder.stickerIndexTv.setText((position + 1).toString())

View File

@ -0,0 +1,199 @@
package com.timber.soft.myemoticon
import android.content.ContentProvider
import android.content.ContentValues
import android.content.Context
import android.content.UriMatcher
import android.content.res.AssetFileDescriptor
import android.database.Cursor
import android.database.MatrixCursor
import android.net.Uri
import android.os.ParcelFileDescriptor
import android.text.TextUtils
import android.util.Log
import com.google.gson.Gson
import com.timber.soft.myemoticon.model.ContentModel
import com.timber.soft.myemoticon.model.Pack
import com.timber.soft.myemoticon.tools.AppTools
import com.timber.soft.myemoticon.tools.AppVal
import java.io.File
import java.io.FileInputStream
import java.io.FileNotFoundException
class StickerProvider : ContentProvider() {
private lateinit var context: Context
private val uriMatcher = UriMatcher(UriMatcher.NO_MATCH)
override fun onCreate(): Boolean {
context = getContext()!!
if (!AppVal.AUTHOR.startsWith(context.packageName)) {
return false
}
uriMatcher.addURI(
AppVal.AUTHOR, AppVal.METADATA + "/*", AppVal.METADATA_CODE_FOR_SINGLE_PACK
)
uriMatcher.addURI(
AppVal.AUTHOR, AppVal.STICKERS + "/*", AppVal.STICKERS_CODE
)
return true
}
override fun query(
uri: Uri,
projection: Array<out String>?,
selection: String?,
selectionArgs: Array<out String>?,
sortOrder: String?
): Cursor? {
val match = uriMatcher.match(uri)
val contentModel = uri.lastPathSegment?.let { getPack(it) } ?: return null
if (match == AppVal.STICKERS_CODE) {
val cursor = getCursorThree(contentModel)
cursor.setNotificationUri(context.contentResolver, uri)
return cursor
} else if (match == AppVal.METADATA_CODE_FOR_SINGLE_PACK) {
val matrixCursor = getCursorTwo(contentModel)
if (matrixCursor != null) {
matrixCursor.setNotificationUri(context.contentResolver, uri)
return matrixCursor
}
}
return null
}
private fun getCursorTwo(contentModel: ContentModel): MatrixCursor? {
val a = arrayOf<String>(
AppVal.ANDROID_LINK,
AppVal.IOS_LINK,
AppVal.STICKER_TRAY,
AppVal.STICKER_IDENTIFIER,
AppVal.STICKER_NAME,
AppVal.STICKER_PUBLISHER,
AppVal.EMAIL,
AppVal.WEBSITE_PUBLISH,
AppVal.WEBSITE_LICENSE,
AppVal.WEBSITE_POLICY,
AppVal.ANIMATED
)
val matrixCursor = MatrixCursor(a)
val rowBuilder = matrixCursor.newRow()
val pack: Pack = contentModel.packs[0]
rowBuilder.add(contentModel.storeLink)
rowBuilder.add(contentModel.iosLink)
rowBuilder.add(pack.trayName)
rowBuilder.add(pack.identifier)
rowBuilder.add(pack.name)
rowBuilder.add(pack.publisherFull)
rowBuilder.add(pack.email)
rowBuilder.add(pack.websitePublisher)
rowBuilder.add(pack.websiteLicense)
rowBuilder.add(pack.websitePrivacy)
if (pack.animated) {
rowBuilder.add(1)
} else {
rowBuilder.add(0)
}
return matrixCursor
return null
}
private fun getCursorThree(contentModel: ContentModel): MatrixCursor {
val a = arrayOf<String>(AppVal.STICKER_FILE_NAME, AppVal.STICKER_EMOJI)
val matrixCursor = MatrixCursor(a)
for (sticker in contentModel.packs[0].stickers) {
val values =
arrayOf<String>(sticker.imFileName, TextUtils.join(",", sticker.emojis))
matrixCursor.addRow(values)
}
return matrixCursor
}
private fun getPack(lastPathSegment: String): ContentModel? {
val filePath = this.getContext()?.getSharedPreferences("", Context.MODE_PRIVATE)
?.getString(lastPathSegment, "")
val file = filePath?.let { File(it) }
if (file != null) {
return if (file.exists()) {
try {
val fileInputStream = FileInputStream(file)
val s = AppTools.fileToString(fileInputStream)
Gson().fromJson(s,ContentModel::class.java)
} catch (e: FileNotFoundException) {
Log.e("FileNotFoundException", e.toString())
null
}
} else {
null
}
}
return null
}
/**
* TODO no impl
*/
override fun getType(uri: Uri): String? {
if (uriMatcher.match(uri) == AppVal.STICKERS_CODE) {
return String.format(
context.getString(R.string.type_sticker),
AppVal.AUTHOR,
AppVal.STICKERS
)
}
return null
}
override fun openAssetFile(uri: Uri, mode: String): AssetFileDescriptor? {
val pathSegments = uri.pathSegments
if (pathSegments.size < 3) return null
val fileName = pathSegments[pathSegments.size - 1]
val identifierName = pathSegments[pathSegments.size - 2]
if (fileName.isEmpty() || identifierName.isEmpty()) return null
val fileDir: String = getFileDir(identifierName)
val path = fileDir + fileName
val file = File(path)
return if (file.exists()) {
AssetFileDescriptor(
ParcelFileDescriptor.open(
file,
ParcelFileDescriptor.MODE_READ_ONLY
), 0L, -1L
)
} else {
null
}
// return super.openAssetFile(uri, mode)
}
private fun getFileDir(identifierName: String?): String {
val filePath = this.getContext()?.getSharedPreferences("", Context.MODE_PRIVATE)
?.getString(identifierName, "")
return filePath!!.substring(0, filePath.lastIndexOf("contents.json"))
}
override fun insert(uri: Uri, values: ContentValues?): Uri? {
return null
}
override fun delete(uri: Uri, selection: String?, selectionArgs: Array<out String>?): Int {
return 0
}
override fun update(
uri: Uri, values: ContentValues?, selection: String?, selectionArgs: Array<out String>?
): Int {
return 0
}
}

View File

@ -0,0 +1,30 @@
package com.timber.soft.myemoticon.model
import com.google.gson.annotations.SerializedName
data class ContentModel(
@SerializedName("android_play_store_link") val storeLink: String,
@SerializedName("ios_app_store_link") val iosLink: String,
@SerializedName("sticker_packs") val packs: List<Pack>
)
data class Pack(
@SerializedName("identifier") val identifier: String,
@SerializedName("name") val name: String,
@SerializedName("tray_image_file") val trayName: String,
@SerializedName("publisher") private val publisher: String,
@SerializedName("publisher_full") val publisherFull: String,
@SerializedName("publisher_email") val email: String,
@SerializedName("publisher_website") val websitePublisher: String,
@SerializedName("privacy_policy_website") val websitePrivacy: String,
@SerializedName("license_agreement_website") val websiteLicense: String,
@SerializedName("share_url") val shareUrl: String,
@SerializedName("is_premium") val isPremium: Boolean,
@SerializedName("animated_sticker_pack") val animated: Boolean,
@SerializedName("stickers") val stickers: List<PackImg>
)
data class PackImg(
@SerializedName("image_file") var imFileName: String,
@SerializedName("emojis") val emojis: List<String>,
)

View File

@ -8,14 +8,33 @@ import com.bumptech.glide.request.RequestListener
import com.bumptech.glide.request.target.Target
import com.google.gson.Gson
import com.timber.soft.myemoticon.model.RootDataModel
import java.io.BufferedReader
import java.io.File
import java.io.FileOutputStream
import java.io.IOException
import java.io.InputStream
import java.io.InputStreamReader
import java.io.StringWriter
import java.util.zip.ZipFile
object AppTools {
fun fileToString(fileInputStream: InputStream): String {
return try {
val charArray = CharArray(fileInputStream.available())
var readCount = 0
val streamReader = InputStreamReader(fileInputStream)
val bufferedReader = BufferedReader(streamReader)
val stringWriter = StringWriter()
while (bufferedReader.read(charArray).also { readCount = it } != -1) {
stringWriter.write(charArray, 0, readCount)
}
stringWriter.toString()
} catch (exception: IOException) {
""
}
}
fun dpCovertPx(context: Context): Int {
// 获取当前设备的屏幕密度,并赋值给变量 scale
var result = 0

View File

@ -1,10 +1,7 @@
package com.timber.soft.myemoticon.tools
object AppVal {
const val ZIP_KEY = "zip_url_key"
const val identifierName_KEY = "identifierName_key"
const val TITLE_KEY = "title_key"
const val AUTHOR = "emoticon.wasticker.app.provider.StickerProvider"
const val AUTHOR = "com.timber.soft.myemoticon.StickerProvider"
const val STICKER_ACTION = "com.whatsapp.intent.action.ENABLE_STICKER_PACK"
const val KEY_PACK_ID = "sticker_pack_id"
const val KEY_PACK_AUTHORITY = "sticker_pack_authority"

View File

@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="90.6dp"
android:height="64dp"
android:viewportWidth="1450"
android:viewportHeight="1024">
<path
android:pathData="M1431.3,51.8A74.2,74.2 0,0 0,1380.3 29.3l-554.7,-21 -42.1,89.1 57.8,167.8 -104.4,190.8 42.5,153.6 105.3,131.8 152.7,-135.8a37.5,37.5 0,0 1,51.7 2l196.4,204.4a34.6,34.6 0,0 1,6.4 38.7,36.9 36.9,0 0,1 -34.6,20.5l-538.1,-20.3 -26.8,74.2 29.1,66.9 618.5,23c19.5,0.7 38.3,-6 52.6,-18.8 14.3,-12.7 22.7,-30.3 23.5,-49L1450.5,102.4a68.1,68.1 0,0 0,-19.3 -50.6zM1033.8,439.2c-60.6,-2.2 -107.8,-51.4 -105.5,-109.7 2.3,-58.3 53.5,-103.8 114.1,-101.5 60.6,2.3 107.8,51.5 105.5,109.8 -2.3,58.3 -53.5,103.7 -114.1,101.5zM635,926.3l18.6,-77.7 -436.9,28.9a36.9,36.9 0,0 1,-35.4 -19.5,34.4 34.4,0 0,1 5.2,-38.9l323.7,-357.7a37.6,37.6 0,0 1,54 -1.2l108.4,108.7 -46.7,-116 82.3,-200.1 -75.8,-160.2L664,0 68.1,39.6C27.7,42.3 -2.7,75.9 0.2,114.8l60.5,843.7c1.3,18.6 10.2,36 24.9,48.2 14.6,12.2 33.8,18.3 53.1,17.2l532,-35.3 -35.6,-62.3z"
android:fillColor="#DFEAF0"/>
</vector>

View File

@ -28,8 +28,7 @@
android:layout_centerInParent="true"
android:layout_marginStart="20dp"
android:layout_marginTop="9dp"
android:layout_toEndOf="@+id/back_bt"
android:src="@mipmap/ic_launcher_round" />
android:layout_toEndOf="@+id/back_bt" />
<TextView
android:id="@+id/emo_title"

View File

@ -6,4 +6,6 @@
<string name="item_name">Item Name</string>
<string name="item_count">+66</string>
<string name="set_to_whatsapp">SET TO WHATSAPP</string>
<string name="type_sticker">vnd.android.cursor.dir/vnd.%1$s.%2$s</string>
<string name="whatsapp_link">https://play.google.com/store/apps/details?id=com.whatsapp</string>
</resources>