From 705a9f620dbacadd8e718b534b03f8737aa0345b Mon Sep 17 00:00:00 2001 From: LUX-Timber Date: Thu, 11 Apr 2024 14:00:08 +0800 Subject: [PATCH] =?UTF-8?q?=E5=AE=8C=E6=88=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .idea/deploymentTargetDropDown.xml | 15 +- .idea/misc.xml | 1 - app/proguard-rules.pro | 8 +- app/src/main/AndroidManifest.xml | 11 +- .../timber/soft/myemoticon/MainFragment.kt | 7 +- .../soft/myemoticon/SetDetailsActivity.kt | 76 +++++-- .../timber/soft/myemoticon/StickerProvider.kt | 199 ++++++++++++++++++ .../soft/myemoticon/model/ContentModel.kt | 30 +++ .../timber/soft/myemoticon/tools/AppTools.kt | 19 ++ .../timber/soft/myemoticon/tools/AppVal.kt | 5 +- app/src/main/res/drawable/svg_img_error.xml | 9 + app/src/main/res/layout/activity_details.xml | 3 +- app/src/main/res/values/strings.xml | 2 + 13 files changed, 352 insertions(+), 33 deletions(-) create mode 100644 app/src/main/java/com/timber/soft/myemoticon/StickerProvider.kt create mode 100644 app/src/main/java/com/timber/soft/myemoticon/model/ContentModel.kt create mode 100644 app/src/main/res/drawable/svg_img_error.xml 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