diff --git a/.idea/deploymentTargetDropDown.xml b/.idea/deploymentTargetDropDown.xml
index 0c0c338..64b7865 100644
--- a/.idea/deploymentTargetDropDown.xml
+++ b/.idea/deploymentTargetDropDown.xml
@@ -3,7 +3,20 @@
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/.idea/misc.xml b/.idea/misc.xml
index 0ad17cb..8978d23 100644
--- a/.idea/misc.xml
+++ b/.idea/misc.xml
@@ -1,4 +1,3 @@
-
diff --git a/app/proguard-rules.pro b/app/proguard-rules.pro
index 481bb43..d80bac3 100644
--- a/app/proguard-rules.pro
+++ b/app/proguard-rules.pro
@@ -18,4 +18,10 @@
# If you keep the line number information, uncomment this to
# hide the original source file name.
-#-renamesourcefileattribute SourceFile
\ No newline at end of file
+#-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
\ No newline at end of file
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index d84299f..2c2256c 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -24,7 +24,16 @@
-
+
+
+
\ No newline at end of file
diff --git a/app/src/main/java/com/timber/soft/myemoticon/MainFragment.kt b/app/src/main/java/com/timber/soft/myemoticon/MainFragment.kt
index c85cc5a..c98daae 100644
--- a/app/src/main/java/com/timber/soft/myemoticon/MainFragment.kt
+++ b/app/src/main/java/com/timber/soft/myemoticon/MainFragment.kt
@@ -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()
diff --git a/app/src/main/java/com/timber/soft/myemoticon/SetDetailsActivity.kt b/app/src/main/java/com/timber/soft/myemoticon/SetDetailsActivity.kt
index 1a154c2..0e0babd 100644
--- a/app/src/main/java/com/timber/soft/myemoticon/SetDetailsActivity.kt
+++ b/app/src/main/java/com/timber/soft/myemoticon/SetDetailsActivity.kt
@@ -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 = 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 = 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())
diff --git a/app/src/main/java/com/timber/soft/myemoticon/StickerProvider.kt b/app/src/main/java/com/timber/soft/myemoticon/StickerProvider.kt
new file mode 100644
index 0000000..955c18a
--- /dev/null
+++ b/app/src/main/java/com/timber/soft/myemoticon/StickerProvider.kt
@@ -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?,
+ selection: String?,
+ selectionArgs: Array?,
+ 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(
+ 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(AppVal.STICKER_FILE_NAME, AppVal.STICKER_EMOJI)
+ val matrixCursor = MatrixCursor(a)
+ for (sticker in contentModel.packs[0].stickers) {
+ val values =
+ arrayOf(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?): Int {
+ return 0
+ }
+
+ override fun update(
+ uri: Uri, values: ContentValues?, selection: String?, selectionArgs: Array?
+ ): Int {
+ return 0
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/timber/soft/myemoticon/model/ContentModel.kt b/app/src/main/java/com/timber/soft/myemoticon/model/ContentModel.kt
new file mode 100644
index 0000000..f620066
--- /dev/null
+++ b/app/src/main/java/com/timber/soft/myemoticon/model/ContentModel.kt
@@ -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
+)
+
+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
+)
+
+data class PackImg(
+ @SerializedName("image_file") var imFileName: String,
+ @SerializedName("emojis") val emojis: List,
+)
diff --git a/app/src/main/java/com/timber/soft/myemoticon/tools/AppTools.kt b/app/src/main/java/com/timber/soft/myemoticon/tools/AppTools.kt
index a68b9ad..13d5eab 100644
--- a/app/src/main/java/com/timber/soft/myemoticon/tools/AppTools.kt
+++ b/app/src/main/java/com/timber/soft/myemoticon/tools/AppTools.kt
@@ -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
diff --git a/app/src/main/java/com/timber/soft/myemoticon/tools/AppVal.kt b/app/src/main/java/com/timber/soft/myemoticon/tools/AppVal.kt
index cce0b17..8ed19a7 100644
--- a/app/src/main/java/com/timber/soft/myemoticon/tools/AppVal.kt
+++ b/app/src/main/java/com/timber/soft/myemoticon/tools/AppVal.kt
@@ -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"
diff --git a/app/src/main/res/drawable/svg_img_error.xml b/app/src/main/res/drawable/svg_img_error.xml
new file mode 100644
index 0000000..ffd3e30
--- /dev/null
+++ b/app/src/main/res/drawable/svg_img_error.xml
@@ -0,0 +1,9 @@
+
+
+
diff --git a/app/src/main/res/layout/activity_details.xml b/app/src/main/res/layout/activity_details.xml
index 13d18ca..fdfc139 100644
--- a/app/src/main/res/layout/activity_details.xml
+++ b/app/src/main/res/layout/activity_details.xml
@@ -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" />
Item Name
+66
SET TO WHATSAPP
+ vnd.android.cursor.dir/vnd.%1$s.%2$s
+ https://play.google.com/store/apps/details?id=com.whatsapp
\ No newline at end of file