Compare commits

..

8 Commits

Author SHA1 Message Date
18401019693
ee5d3bb028 送礼物更改 2022-11-22 14:52:49 +08:00
18401019693
1346a1de52 测试修改 2022-11-18 17:28:42 +08:00
18401019693
9fa0109c2e 测试更改 2022-11-18 10:50:40 +08:00
94a96ba91a 调整直播间礼物弹框高度 2022-11-18 10:40:11 +08:00
18401019693
5006b8414e 测试修改 2022-11-15 13:53:15 +08:00
18401019693
5eac127142 测试修改 2022-11-15 11:31:59 +08:00
18401019693
8e89fdfa65 测试修改 2022-11-15 10:24:37 +08:00
5f210af804 修复关闭小窗时部分机型闪退问题
修复热度加gif不播放问题
修复送礼主播会闪退问题(心愿单id冲突)
修复一处PK中的空指针问题
修复滑动直播间不显示离开图
2022-11-15 10:10:28 +08:00
2608 changed files with 19039 additions and 177018 deletions

View File

@@ -1,12 +1,12 @@
apply plugin: 'com.android.library'
apply plugin: 'img-optimizer'
apply plugin: 'kotlin-android'
apply plugin: 'kotlin-parcelize'
apply from: "../package_config.gradle"
apply plugin: 'kotlin-android-extensions'
android {
namespace "com.yunbao.faceunity"
compileSdk rootProject.ext.android.compileSdkVersion
compileSdkVersion rootProject.ext.android.compileSdkVersion
buildToolsVersion rootProject.ext.android.buildToolsVersion
packagingOptions {
pickFirst "lib/armeabi/libyuvutils.so"
pickFirst "lib/arm64-v8a/libyuvutils.so"
@@ -33,7 +33,7 @@ android {
versionName rootProject.ext.android.versionName
manifestPlaceholders = rootProject.ext.manifestPlaceholders
ndk {
abiFilters "armeabi-v7a", "arm64-v8a","x86","x86_64"
abiFilters "armeabi-v7a", "arm64-v8a"
}
}
aaptOptions {
@@ -48,8 +48,8 @@ android {
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_18
targetCompatibility JavaVersion.VERSION_18
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
}
repositories {
@@ -58,15 +58,15 @@ repositories {
}
}
dependencies {
api fileTree(dir: 'libs', include: ['*.jar'])
api rootProject.ext.dependencies["appcompat-androidx"]
api rootProject.ext.dependencies["recyclerview-androidx"]
api "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation rootProject.ext.dependencies["appcompat-androidx"]
implementation rootProject.ext.dependencies["recyclerview-androidx"]
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
//common
api project(path: ':common')
implementation project(path: ':common')
api 'com.faceunity:core:8.7.0'
api 'com.faceunity:model:8.7.0'
implementation 'com.faceunity:core:8.3.1'
implementation 'com.faceunity:model:8.3.1'
//implementation 'com.faceunity:nama:8.3.1' //底层库-标准版

View File

@@ -2,7 +2,7 @@ package com.yunbao.faceunity;
import android.content.Context;
import androidx.test.platform.app.Instrimport com.yunbao.common.utils.MobclickAgent;ntationRegistry;
import androidx.test.platform.app.InstrumentationRegistry;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import org.junit.Test;

View File

@@ -1,5 +1,7 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
xmlns:tools="http://schemas.android.com/tools"
package="com.yunbao.faceunity"
>
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.CAMERA" />

View File

@@ -71,7 +71,7 @@ public class FaceManager implements SensorEventListener {
* 配置美颜SDK
*/
public void initFURender(Context context) {
initFaceUnity(context);
mFURenderer = FURenderer.getInstance();
mFURenderer.setInputTextureType(FUInputTextureEnum.FU_ADM_FLAG_COMMON_TEXTURE);
mFURenderer.setCameraFacing(CameraFacingEnum.CAMERA_FRONT);
@@ -96,26 +96,18 @@ public class FaceManager implements SensorEventListener {
faceUnityView.setIFaceUnityInter(new FaceUnityView.IFaceUnityInter() {
@Override
public void onPause() {
if(onMirrorChanged!=null){
onMirrorChanged.onChange(false);
}
pauseFace = true;
}
@Override
public void onStart() {
if(onMirrorChanged!=null){
onMirrorChanged.onChange(true);
}
pauseFace = false;
}
});
}
public void loadConfig() {
public void loadConfig(){
initFaceBeauty();
}
public void initFaceBeauty() {
FaceBeautyDataFactory faceBeautyDataFactory;
faceBeautyDataFactory = new FaceBeautyDataFactory();
@@ -123,13 +115,9 @@ public class FaceManager implements SensorEventListener {
for (String key : configMap.keySet()) {
if ("FilterViewHolder_".equals(key)) {
for (FaceBeautyFilterBean filter : faceBeautyDataFactory.getBeautyFilters()) {
if (filter.getKey().equals(configMap.get(key)) && !"origin".equals(configMap.get(key))) {
try {
faceBeautyDataFactory.onFilterSelected(filter.getKey(), Double.parseDouble((String) Objects.requireNonNull(configMap.get("FilterViewHolder_" + configMap.get(key) + "_val"))) / 100, filter.getDesRes());
Log.i(TAG, "test: 设置滤镜 =" + filter.getKey() + " val = " + configMap.get("FilterViewHolder_" + configMap.get(key) + "_val"));
} catch (Exception e) {
e.printStackTrace();
}
if (filter.getKey().equals(configMap.get(key))&&!"origin".equals(configMap.get(key))) {
faceBeautyDataFactory.onFilterSelected(filter.getKey(), Double.parseDouble((String) Objects.requireNonNull(configMap.get("FilterViewHolder_" + configMap.get(key) + "_val")))/100, filter.getDesRes());
Log.i(TAG, "test: 设置滤镜 ="+filter.getKey()+" val = "+configMap.get("FilterViewHolder_" + configMap.get(key) + "_val"));
break;
}
}
@@ -137,25 +125,17 @@ public class FaceManager implements SensorEventListener {
String name = key.replace("BeautySkinViewHolder_", "");
for (FaceBeautyBean bean : faceBeautyDataFactory.getShapeBeauty()) {
if (bean.getKey().equals(name)) {
try {
faceBeautyDataFactory.updateParamIntensity(bean.getKey(), Double.parseDouble((String) Objects.requireNonNull(configMap.get(key))));
Log.i(TAG, "test: 设置美颜 = " + bean.getKey() + " val = " + configMap.get(key));
} catch (Exception e) {
e.printStackTrace();
}
faceBeautyDataFactory.updateParamIntensity(bean.getKey(),Double.parseDouble((String) Objects.requireNonNull(configMap.get(key))));
Log.i(TAG, "test: 设置美颜 = "+bean.getKey()+" val = "+configMap.get(key));
break;
}
}
} else if (key.startsWith("BeautyShapeViewHolder")) {
}else if(key.startsWith("BeautyShapeViewHolder")){
String name = key.replace("BeautyShapeViewHolder_", "");
for (FaceBeautyBean bean : faceBeautyDataFactory.getShapeBeauty()) {
if (bean.getKey().equals(name)) {
try {
faceBeautyDataFactory.updateParamIntensity(bean.getKey(), Double.parseDouble((String) Objects.requireNonNull(configMap.get(key))));
Log.i(TAG, "test: 设置美肤 = " + bean.getKey() + " val = " + configMap.get(key));
} catch (Exception e) {
e.printStackTrace();
}
faceBeautyDataFactory.updateParamIntensity(bean.getKey(),Double.parseDouble((String) Objects.requireNonNull(configMap.get(key))));
Log.i(TAG, "test: 设置美肤 = "+bean.getKey()+" val = "+configMap.get(key));
break;
}
}
@@ -297,22 +277,10 @@ public class FaceManager implements SensorEventListener {
} catch (Exception e) {
e.printStackTrace();
}
isInit = false;
}
OnMirrorChanged onMirrorChanged;
public void setOnMirrorChanged(OnMirrorChanged onMirrorChanged) {
this.onMirrorChanged = onMirrorChanged;
}
public interface FaceStatusChanged {
void onFaceChanged(int num);
}
public interface OnMirrorChanged{
void onChange(boolean falg);
}
}

View File

@@ -80,11 +80,11 @@ public class ContainerRecyclerAdapter extends RecyclerView.Adapter<BaseViewHolde
}
public void hideSeekBar() {
if (seekBar.getVisibility() == View.INVISIBLE) {
if (seekBar.getVisibility() == View.GONE) {
return;
}
seekBar.setOnProgressChangeListener(null);
seekBar.setVisibility(View.INVISIBLE);
seekBar.setVisibility(View.GONE);
}

View File

@@ -52,11 +52,11 @@ public class FURenderer extends IFURenderer {
/* 特效FURenderKit*/
public FURenderKit mFURenderKit;
private FURenderKit mFURenderKit;
/* AI道具*/
public static String BUNDLE_AI_FACE = "model" + File.separator + "ai_face_processor.bundle";
public static String BUNDLE_AI_HUMAN = "model" + File.separator + "ai_human_processor.bundle";
private String BUNDLE_AI_FACE = "model" + File.separator + "ai_face_processor_lite.bundle";
private String BUNDLE_AI_HUMAN = "model" + File.separator + "ai_human_processor.bundle";
/* GL 线程 ID */
private Long mGlThreadId = 0L;

View File

@@ -15,7 +15,7 @@ public class FaceUnityConfig {
/************************** 算法Model ******************************/
// 人脸识别
public static String BUNDLE_AI_FACE = "model" + File.separator + "ai_face_processor.bundle";
public static String BUNDLE_AI_FACE = "model" + File.separator + "ai_face_processor_lite.bundle";
// 手势
public static String BUNDLE_AI_HAND = "model" + File.separator + "ai_hand_processor.bundle";

View File

@@ -314,35 +314,6 @@ public class FileUtils {
return null;
}
public static String copyAssetsFile(Context context, String assetsPath, String fileName, String saveFileDir) {
File fileDir = new File(saveFileDir);
if (!fileDir.exists()) {
fileDir.mkdirs();
}
File file = new File(fileDir, fileName);
if (file.exists()) {
return file.getAbsolutePath();
}
try {
InputStream inputStream = context.getAssets().open(assetsPath);
FileOutputStream fos = new FileOutputStream(file);
BufferedInputStream bis = new BufferedInputStream(inputStream);
BufferedOutputStream bos = new BufferedOutputStream(fos);
byte[] byteArray = new byte[1024];
int bytes = bis.read(byteArray);
while (bytes > 0) {
bos.write(byteArray, 0, bytes);
bos.flush();
bytes = bis.read(byteArray);
}
bos.close();
fos.close();
return file.getAbsolutePath();
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
/**
* 获取Uri文件绝对路径
@@ -551,7 +522,6 @@ public class FileUtils {
/**
* 遍历一个文件夹获取改文件夹下所有文件名
*
* @param path
* @return
*/
@@ -593,7 +563,7 @@ public class FileUtils {
* @param path String
* @return Boolean
*/
public static Boolean checkIsVideo(Context context, String path) {
public static Boolean checkIsVideo(Context context,String path) {
MediaMetadataRetriever retriever = new MediaMetadataRetriever();
try {
retriever.setDataSource(context, Uri.fromFile(new File(path)));

View File

@@ -1,11 +1,10 @@
package com.yunbao.common.utils;
package com.yunbao.faceunity.utils;
import android.util.Log;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;
@@ -152,36 +151,16 @@ public class ZipUtils {
* @param zipFileString 解压完成的Zip路径
* @throws Exception
*/
public static void zipFolder(List<String> srcFileString, String zipFileString, List<String> filters, Runnable runnable) throws Exception {
if (filters == null) {
filters = new ArrayList<>();
}
filters.add(new File(zipFileString).getAbsolutePath());
final List<String> tmp = new ArrayList<>(filters);
public static void zipFolder(String srcFileString, String zipFileString) throws Exception {
//创建ZIP
ZipOutputStream outZip = new ZipOutputStream(new FileOutputStream(zipFileString));
new Thread(new Runnable() {
@Override
public void run() {
try {
for (String src : srcFileString) {
File file = new File(src);
//压缩
zipFiles(file.getParent() + File.separator, file.getName(), outZip, tmp);
}
//创建文件
//完成和关闭
outZip.finish();
outZip.close();
runnable.run();
} catch (Exception e) {
e.printStackTrace();
}
}
}).start();
//创建文件
File file = new File(srcFileString);
//压缩
zipFiles(file.getParent() + File.separator, file.getName(), outZip);
//完成和关闭
outZip.finish();
outZip.close();
}
/**
@@ -192,15 +171,9 @@ public class ZipUtils {
* @param zipOutputSteam
* @throws Exception
*/
private static void zipFiles(String folderString, String fileString, ZipOutputStream zipOutputSteam, List<String> filters) throws Exception {
private static void zipFiles(String folderString, String fileString, ZipOutputStream zipOutputSteam) throws Exception {
if (zipOutputSteam == null)
return;
for (String filter : filters) {
if (fileString.startsWith(filter)) {
return;
}
}
System.out.println("压缩文件:" + folderString + "|" + fileString);
File file = new File(folderString + fileString);
if (file.isFile()) {
ZipEntry zipEntry = new ZipEntry(fileString);
@@ -223,7 +196,7 @@ public class ZipUtils {
}
//子文件和递归
for (int i = 0; i < fileList.length; i++) {
zipFiles(folderString, fileString + File.separator + fileList[i], zipOutputSteam, filters);
zipFiles(folderString, fileString + File.separator + fileList[i], zipOutputSteam);
}
}
}

View File

@@ -13,7 +13,7 @@ import com.google.gson.JsonObject;
import com.yunbao.faceunity.entity.net.FineStickerEntity;
import com.yunbao.faceunity.utils.FaceUnityData;
import com.yunbao.faceunity.utils.FileUtils;
import com.yunbao.common.utils.ZipUtils;
import com.yunbao.faceunity.utils.ZipUtils;
import java.io.File;
import java.util.ArrayList;

View File

@@ -1,96 +1,96 @@
<resources>
<string name="camera_dialog_title">警告</string>
<string name="sorry_no_permission">抱歉,你所使用的證書權限或SDK不包括功能。</string>
<string name="camera_dialog_message">機權限被禁用或者相機被別的應用佔用!</string>
<string name="camera_dialog_open"></string>
<string name="sorry_no_permission">抱歉,你所使用的证书权限或SDK不包括功能。</string>
<string name="camera_dialog_message">机权限被禁用或者相机被别的应用占用!</string>
<string name="camera_dialog_open"></string>
<string name="camera_dialog_back">退出</string>
<string name="fu_base_debug">Resolution:\n\t%dX%d\nFPS: %d\nRender time:\n\t%dms</string>
<string name="save_photo_success">保存照片成功!</string>
<string name="save_video_success">保存視頻成功!</string>
<string name="save_video_failed">保存視頻失敗</string>
<string name="save_video_too_short">視頻太短啦!</string>
<string name="save_video_wait">視頻處理中稍等</string>
<string name="fu_base_is_tracking_text">檢測到人</string>
<string name="fu_base_incomplete_face_text">不全</string>
<string name="fu_base_input_type_single">單輸</string>
<string name="fu_base_input_type_double">雙輸</string>
<string name="save_video_success">保存视频成功!</string>
<string name="save_video_failed">保存视频失败</string>
<string name="save_video_too_short">视频太短啦!</string>
<string name="save_video_wait">视频处理中稍等</string>
<string name="fu_base_is_tracking_text">检测到人</string>
<string name="fu_base_incomplete_face_text">不全</string>
<string name="fu_base_input_type_single">单输</string>
<string name="fu_base_input_type_double">双输</string>
<string name="beauty_box_heavy_blur_fine">磨皮</string>
<string name="beauty_box_color_level">美白</string>
<string name="beauty_box_red_level">紅潤</string>
<string name="beauty_box_sharpen"></string>
<string name="beauty_box_red_level">红润</string>
<string name="beauty_box_sharpen"></string>
<string name="beauty_box_eye_bright">亮眼</string>
<string name="beauty_box_tooth_whiten">美牙</string>
<string name="beauty_box_eye_enlarge">大眼</string>
<string name="beauty_box_eye_circle"></string>
<string name="beauty_box_eye_circle"></string>
<string name="beauty_box_cheek_natural">自然</string>
<string name="beauty_box_cheek_goddess">女神</string>
<string name="beauty_box_cheek_long_face">長臉</string>
<string name="beauty_box_cheek_round_face">圓臉</string>
<string name="beauty_box_cheek_thinning"></string>
<string name="beauty_box_cheek_v">V</string>
<string name="beauty_box_cheek_narrow"></string>
<string name="beauty_box_cheek_short"></string>
<string name="beauty_box_cheek_small"></string>
<string name="beauty_box_cheek_long_face">长脸</string>
<string name="beauty_box_cheek_round_face">圆脸</string>
<string name="beauty_box_cheek_thinning"></string>
<string name="beauty_box_cheek_v">V</string>
<string name="beauty_box_cheek_narrow"></string>
<string name="beauty_box_cheek_short"></string>
<string name="beauty_box_cheek_small"></string>
<string name="beauty_box_intensity_chin">下巴</string>
<string name="beauty_box_intensity_forehead">額頭</string>
<string name="beauty_box_intensity_forehead">额头</string>
<string name="beauty_box_intensity_nose">瘦鼻</string>
<string name="beauty_box_intensity_mouth">嘴型</string>
<string name="beauty_box_cheekbones"></string>
<string name="beauty_box_lower_jaw">瘦下</string>
<string name="beauty_radio_skin_beauty"></string>
<string name="beauty_box_cheekbones"></string>
<string name="beauty_box_lower_jaw">瘦下</string>
<string name="beauty_radio_skin_beauty"></string>
<string name="beauty_radio_face_shape">美型</string>
<string name="beauty_radio_filter">濾鏡</string>
<string name="beauty_radio_style">格推</string>
<string name="beauty_radio_filter">滤镜</string>
<string name="beauty_radio_style">格推</string>
<string name="beauty_micro_pouch">去黑眼圈</string>
<string name="beauty_micro_nasolabial">去法令</string>
<string name="beauty_micro_nasolabial">去法令</string>
<string name="beauty_micro_smile">微笑嘴角</string>
<string name="beauty_brow_height">眉毛上下</string>
<string name="beauty_brow_space"></string>
<string name="beauty_micro_canthus">眼角</string>
<string name="beauty_micro_philtrum">人中</string>
<string name="beauty_micro_long_nose"></string>
<string name="beauty_brow_space"></string>
<string name="beauty_micro_canthus">眼角</string>
<string name="beauty_micro_philtrum">人中</string>
<string name="beauty_micro_long_nose"></string>
<string name="beauty_micro_eye_space">眼距</string>
<string name="beauty_micro_eye_rotate">眼睛角度</string>
<string name="makeup_radio_lipstick"></string>
<string name="makeup_radio_blusher"></string>
<string name="makeup_radio_lipstick"></string>
<string name="makeup_radio_blusher"></string>
<string name="makeup_radio_eyebrow">眉毛</string>
<string name="makeup_radio_eye_shadow">眼影</string>
<string name="makeup_radio_eye_liner"></string>
<string name="makeup_radio_eye_liner">线</string>
<string name="makeup_radio_eyelash">睫毛</string>
<string name="makeup_radio_contact_lens">美瞳</string>
<string name="makeup_radio_foundation">粉底</string>
<string name="makeup_radio_highlight">高光</string>
<string name="makeup_radio_shadow"></string>
<string name="makeup_radio_remove"></string>
<string name="makeup_customize">自定</string>
<string name="makeup_radio_shadow"></string>
<string name="makeup_radio_remove"></string>
<string name="makeup_customize">自定</string>
<string name="makeup_peach_blossom">桃花</string>
<string name="makeup_boyfriend">男友</string>
<string name="makeup_clear">清透</string>
<string name="makeup_grapefruit">西柚</string>
<string name="select_data_photo">選擇圖</string>
<string name="select_data_video">選擇視頻</string>
<string name="select_data_title">請從相冊中選擇圖片或視頻</string>
<string name="image_file_does_not_exist">選圖片文件不存在。</string>
<string name="video_file_does_not_exist">選視頻文件不存在。</string>
<string name="select_data_photo">选择图</string>
<string name="select_data_video">选择视频</string>
<string name="select_data_title">请从相册中选择图片或视频</string>
<string name="image_file_does_not_exist">选图片文件不存在。</string>
<string name="video_file_does_not_exist">选视频文件不存在。</string>
<string name="future_warrior">張嘴試試</string>
<string name="jet_mask">鼓腮</string>
<string name="sdx2">皺眉試試</string>
<string name="future_warrior">张嘴试试</string>
<string name="jet_mask">鼓腮</string>
<string name="sdx2">皱眉试试</string>
<string name="luhantongkuan_ztt_fu">眨一眨眼</string>
<string name="qingqing_ztt_fu">嘟嘴試試</string>
<string name="xiaobianzi_zh_fu">微笑觸發</string>
<string name="xiaoxueshen_ztt_fu">氣觸發</string>
<string name="hez_ztt_fu">張嘴試試</string>
<string name="qingqing_ztt_fu">嘟嘴试试</string>
<string name="xiaobianzi_zh_fu">微笑触发</string>
<string name="xiaoxueshen_ztt_fu">气触发</string>
<string name="hez_ztt_fu">张嘴试试</string>
<string name="push_hand">推出手掌</string>
<string name="fu_lm_koreaheart">手手指比心</string>
<string name="ssd_thread_six"></string>
<string name="ssd_thread_cute">拳靠近臉頰賣</string>
<string name="fu_lm_koreaheart">手手指比心</string>
<string name="ssd_thread_six"></string>
<string name="ssd_thread_cute">拳靠近脸颊卖</string>
<string name="origin"></string>
<string name="origin"></string>
<string name="bailiang_1">白亮 1</string>
<string name="bailiang_2">白亮 2</string>
<string name="bailiang_3">白亮 3</string>
@@ -109,23 +109,23 @@
<string name="xiaoqingxin_3">小清新 3</string>
<string name="xiaoqingxin_4">小清新 4</string>
<string name="xiaoqingxin_6">小清新 6</string>
<string name="lengsediao_1">冷色調 1</string>
<string name="lengsediao_2">冷色調 2</string>
<string name="lengsediao_3">冷色調 3</string>
<string name="lengsediao_4">冷色調 4</string>
<string name="lengsediao_7">冷色調 7</string>
<string name="lengsediao_8">冷色調 8</string>
<string name="lengsediao_11">冷色調 11</string>
<string name="nuansediao_1">暖色調 1</string>
<string name="nuansediao_2">暖色調 2</string>
<string name="gexing_1">性 1</string>
<string name="gexing_2">性 2</string>
<string name="gexing_3">性 3</string>
<string name="gexing_4">性 4</string>
<string name="gexing_5">性 5</string>
<string name="gexing_7">性 7</string>
<string name="gexing_10">性 10</string>
<string name="gexing_11">性 11</string>
<string name="lengsediao_1">冷色 1</string>
<string name="lengsediao_2">冷色 2</string>
<string name="lengsediao_3">冷色 3</string>
<string name="lengsediao_4">冷色 4</string>
<string name="lengsediao_7">冷色 7</string>
<string name="lengsediao_8">冷色 8</string>
<string name="lengsediao_11">冷色 11</string>
<string name="nuansediao_1">暖色 1</string>
<string name="nuansediao_2">暖色 2</string>
<string name="gexing_1">性 1</string>
<string name="gexing_2">性 2</string>
<string name="gexing_3">性 3</string>
<string name="gexing_4">性 4</string>
<string name="gexing_5">性 5</string>
<string name="gexing_7">性 7</string>
<string name="gexing_10">性 10</string>
<string name="gexing_11">性 11</string>
<string name="heibai_1">黑白 1</string>
<string name="heibai_2">黑白 2</string>
<string name="heibai_3">黑白 3</string>
@@ -138,14 +138,14 @@
<string name="ziran_6">自然 6</string>
<string name="ziran_7">自然 7</string>
<string name="ziran_8">自然 8</string>
<string name="zhiganhui_1">感灰 1</string>
<string name="zhiganhui_2">感灰 2</string>
<string name="zhiganhui_3">感灰 3</string>
<string name="zhiganhui_4">感灰 4</string>
<string name="zhiganhui_5">感灰 5</string>
<string name="zhiganhui_6">感灰 6</string>
<string name="zhiganhui_7">感灰 7</string>
<string name="zhiganhui_8">感灰 8</string>
<string name="zhiganhui_1">感灰 1</string>
<string name="zhiganhui_2">感灰 2</string>
<string name="zhiganhui_3">感灰 3</string>
<string name="zhiganhui_4">感灰 4</string>
<string name="zhiganhui_5">感灰 5</string>
<string name="zhiganhui_6">感灰 6</string>
<string name="zhiganhui_7">感灰 7</string>
<string name="zhiganhui_8">感灰 8</string>
<string name="mitao_1">蜜桃 1</string>
<string name="mitao_2">蜜桃 2</string>
<string name="mitao_3">蜜桃 3</string>
@@ -155,85 +155,85 @@
<string name="mitao_7">蜜桃 7</string>
<string name="mitao_8">蜜桃 8</string>
<string name="beauty_face_style_none"></string>
<string name="beauty_face_style_1">格 1</string>
<string name="beauty_face_style_2">格 2</string>
<string name="beauty_face_style_3">格 3</string>
<string name="beauty_face_style_4">格 4</string>
<string name="beauty_face_style_5">格 5</string>
<string name="beauty_face_style_6">格 6</string>
<string name="beauty_face_style_7">格 7</string>
<string name="beauty_face_style_toast">使用%s先取消“格推</string>
<string name="beauty_face_style_none"></string>
<string name="beauty_face_style_1">格 1</string>
<string name="beauty_face_style_2">格 2</string>
<string name="beauty_face_style_3">格 3</string>
<string name="beauty_face_style_4">格 4</string>
<string name="beauty_face_style_5">格 5</string>
<string name="beauty_face_style_6">格 6</string>
<string name="beauty_face_style_7">格 7</string>
<string name="beauty_face_style_toast">使用%s先取消“格推</string>
<string name="poster_take_photo">對準線框 正臉拍攝</string>
<string name="poster_change_face_error">換失敗</string>
<string name="poster_template_face_none">識別模板的人臉,請重新選擇模板</string>
<string name="dialog_no_track_face">檢測到人臉,請重新拍</string>
<string name="dialog_no_incomplete_face">不全,重新拍</string>
<string name="dialog_face_rotation_not_valid">臉偏轉角度大,請正臉拍攝</string>
<string name="poster_take_photo">对准线框 正脸拍摄</string>
<string name="poster_change_face_error">换失败</string>
<string name="poster_template_face_none">识别模板的人脸,请重新选择模板</string>
<string name="dialog_no_track_face">检测到人脸,请重新拍</string>
<string name="dialog_no_incomplete_face">不全,重新拍</string>
<string name="dialog_face_rotation_not_valid">脸偏转角度大,请正脸拍摄</string>
<string name="dialog_got">知道啦</string>
<string name="tip_dual_face">檢測到多人,請選擇一人進行換臉</string>
<string name="tip_dual_face">检测到多人,请选择一人进行换脸</string>
<string name="animoji_filter">Animoji</string>
<string name="cartoon_filter">動漫濾鏡</string>
<string name="cartoon_filter">动漫滤镜</string>
<string name="delete_avatar_model">除模型</string>
<string name="delete_avatar_model">除模型</string>
<string name="new_avatar_model">新建模型</string>
<string name="edit_avatar_model">編輯模型</string>
<string name="edit_avatar_model">编辑模型</string>
<string name="avatar_face_hair"></string>
<string name="avatar_face_face"></string>
<string name="avatar_face_hair"></string>
<string name="avatar_face_face"></string>
<string name="avatar_face_eye">眼睛</string>
<string name="avatar_face_lip">嘴唇</string>
<string name="avatar_face_nose">鼻子</string>
<string name="avatar_face_length">臉型長</string>
<string name="avatar_face_width">臉頰寬</string>
<string name="avatar_chin_width">顎寬</string>
<string name="avatar_face_length">脸型长</string>
<string name="avatar_face_width">脸颊宽</string>
<string name="avatar_chin_width">颚宽</string>
<string name="avatar_chin_height">下巴高低</string>
<string name="avatar_eye_position">眼睛位置</string>
<string name="avatar_eye_corner_height">眼角高度</string>
<string name="avatar_eye_height">眼睛高低</string>
<string name="avatar_eye_width">眼睛</string>
<string name="avatar_eye_width">眼睛</string>
<string name="avatar_nose_position">鼻子位置</string>
<string name="avatar_nose_width">鼻翼</string>
<string name="avatar_nose_height">高低</string>
<string name="avatar_nose_width">鼻翼</string>
<string name="avatar_nose_height">高低</string>
<string name="avatar_mouth_position">嘴部位置</string>
<string name="avatar_up_lip_thickness">上唇厚度</string>
<string name="avatar_down_lip_thickness">下唇厚度</string>
<string name="avatar_lip_width">嘴唇</string>
<string name="model_empty_tip">還沒有創建過模型哦</string>
<string name="dialog_reset_avatar_model">是否所有參數恢復到默值?</string>
<string name="avatar_face_customize">自定</string>
<string name="avatar_lip_width">嘴唇</string>
<string name="model_empty_tip">还没有创建过模型哦</string>
<string name="dialog_reset_avatar_model">是否所有参数恢复到默值?</string>
<string name="avatar_face_customize">自定</string>
<string name="avatar_save_succeed">保存成功</string>
<string name="live_photo_back_not_save">返回后前操作將不會被保存哦</string>
<string name="live_photo_btn_delete"></string>
<string name="live_photo_back_not_save">返回后前操作将不会被保存哦</string>
<string name="live_photo_btn_delete"></string>
<string name="live_photo_btn_cancel">取消</string>
<string name="live_photo_btn_delete_">除(%d)</string>
<string name="live_photo_delete_effect">除道具</string>
<string name="live_photo__delete_all"></string>
<string name="live_photo_empty_list_tip">還沒有創建過道具哦</string>
<string name="confirm"></string>
<string name="live_photo_btn_delete_">除(%d)</string>
<string name="live_photo_delete_effect">除道具</string>
<string name="live_photo__delete_all"></string>
<string name="live_photo_empty_list_tip">还没有创建过道具哦</string>
<string name="confirm"></string>
<string name="cancel">取消</string>
<string name="dialog_confirm_delete">確定刪除所中的道具?</string>
<string name="toast_delete_succeed">除成功</string>
<string name="toast_delete_failed">除失</string>
<string name="dialog_confirm_delete">确定删除所中的道具?</string>
<string name="toast_delete_succeed">除成功</string>
<string name="toast_delete_failed">除失</string>
<string name="live_photo_save_succeed">道具保存成功</string>
<string name="recover"></string>
<string name="recover"></string>
<string name="makeup_lip_fog"></string>
<string name="makeup_lip_moist1">潤澤</string>
<string name="makeup_lip_moist2">潤澤</string>
<string name="makeup_lip_fog"></string>
<string name="makeup_lip_moist1">润泽</string>
<string name="makeup_lip_moist2">润泽</string>
<string name="makeup_lip_pearl">珠光</string>
<string name="makeup_lip_bitelip">咬唇</string>
<string name="makeup_blusher_apple">果肌</string>
<string name="makeup_blusher_apple">果肌</string>
<string name="makeup_blusher_fan">扇形</string>
<string name="makeup_blusher_eye_corner">眼角</string>
<string name="makeup_blusher_slight_drunk">微醺</string>
<string name="makeup_highlight_one">高光 I</string>
<string name="makeup_highlight_two">高光 II</string>
<string name="makeup_shadow_one">影 I</string>
<string name="makeup_shadow_one">影 I</string>
<string name="makeup_pupil_1">蜜糖</string>
<string name="makeup_pupil_2">奶茶</string>
<string name="makeup_pupil_3">水波</string>
@@ -241,106 +241,106 @@
<string name="makeup_pupil_5">孔雀</string>
<string name="makeup_pupil_6">星河</string>
<string name="makeup_pupil_7">落目</string>
<string name="makeup_pupil_8"></string>
<string name="makeup_eyebrow_willow">恭弘=叶 恭弘</string>
<string name="makeup_pupil_8"></string>
<string name="makeup_eyebrow_willow"></string>
<string name="makeup_eyebrow_wild">野生眉</string>
<string name="makeup_eyebrow_classical">古典眉</string>
<string name="makeup_eyebrow_standard">標準</string>
<string name="makeup_eye_shadow_single">色眼影</string>
<string name="makeup_eye_shadow_double1">色眼影 I</string>
<string name="makeup_eye_shadow_double2">色眼影 II</string>
<string name="makeup_eye_shadow_double3">色眼影 III</string>
<string name="makeup_eyebrow_standard">标准</string>
<string name="makeup_eye_shadow_single">色眼影</string>
<string name="makeup_eye_shadow_double1">色眼影 I</string>
<string name="makeup_eye_shadow_double2">色眼影 II</string>
<string name="makeup_eye_shadow_double3">色眼影 III</string>
<string name="makeup_eye_shadow_triple1">三色眼影 I</string>
<string name="makeup_eye_shadow_triple2">三色眼影 II</string>
<string name="makeup_eyelash_natural1">自然型 I</string>
<string name="makeup_eyelash_natural2">自然型 II</string>
<string name="makeup_eyelash_thick1">密型 I</string>
<string name="makeup_eyelash_thick2">密型 II</string>
<string name="makeup_eyelash_exaggerate1">誇張型 I</string>
<string name="makeup_eyelash_exaggerate2">誇張型 II</string>
<string name="makeup_eye_linear_cat"></string>
<string name="makeup_eyelash_thick1">密型 I</string>
<string name="makeup_eyelash_thick2">密型 II</string>
<string name="makeup_eyelash_exaggerate1">夸张型 I</string>
<string name="makeup_eyelash_exaggerate2">夸张型 II</string>
<string name="makeup_eye_linear_cat"></string>
<string name="makeup_eye_linear_drooping">下垂眼</string>
<string name="makeup_eye_linear_pull_open">眼距</string>
<string name="makeup_eye_linear_pull_open">眼距</string>
<string name="makeup_eye_linear_pull_close">拉近眼距</string>
<string name="makeup_eye_linear_long"></string>
<string name="makeup_eye_linear_circular"></string>
<string name="makeup_eye_linear_long"></string>
<string name="makeup_eye_linear_circular"></string>
<string name="makeup_combination_diadiatu">嗲嗲兔</string>
<string name="makeup_combination_dongling">凍齡</string>
<string name="makeup_combination_guofeng">國風</string>
<string name="makeup_combination_dongling">冻龄</string>
<string name="makeup_combination_guofeng">国风</string>
<string name="makeup_combination_hunxie">混血</string>
<string name="makeup_combination_sexy">性感</string>
<string name="makeup_combination_sweet">甜美</string>
<string name="makeup_combination_neighbor"></string>
<string name="makeup_combination_occident"></string>
<string name="makeup_combination_charming"></string>
<string name="makeup_combination_jianling">減齡</string>
<string name="makeup_combination_neighbor"></string>
<string name="makeup_combination_occident"></string>
<string name="makeup_combination_charming"></string>
<string name="makeup_combination_jianling">减龄</string>
<string name="makeup_combination_nuandong">暖冬</string>
<string name="makeup_combination_hongfeng">紅楓</string>
<string name="makeup_combination_hongfeng">红枫</string>
<string name="makeup_combination_shaonv">少女</string>
<string name="makeup_combination_ziyun"></string>
<string name="makeup_combination_yanshimao">厭世貓</string>
<string name="makeup_combination_renyu"></string>
<string name="makeup_combination_ziyun"></string>
<string name="makeup_combination_yanshimao">厌世猫</string>
<string name="makeup_combination_renyu"></string>
<string name="makeup_combination_chuqiu">初秋</string>
<string name="makeup_combination_qianzhihe">紙鶴</string>
<string name="makeup_combination_qianzhihe">纸鹤</string>
<string name="makeup_combination_chaomo">超模</string>
<string name="makeup_combination_chuju"></string>
<string name="makeup_combination_gangfeng"></string>
<string name="makeup_combination_chuju"></string>
<string name="makeup_combination_gangfeng"></string>
<string name="makeup_combination_rose">Rose</string>
<string name="slimming">瘦身</string>
<string name="long_legs"></string>
<string name="thin_waist"></string>
<string name="long_legs"></string>
<string name="thin_waist"></string>
<string name="beautify_shoulder">美肩</string>
<string name="beautify_hip_slim">美臀</string>
<string name="beautify_head_slim"></string>
<string name="beautify_head_slim"></string>
<string name="beautify_leg_thin_slim">瘦腿</string>
<string name="toast_not_detect_body">檢測到人</string>
<string name="pta_human_full_body">全身驅動</string>
<string name="pta_human_half_body">半身驅動</string>
<string name="toast_not_detect_body">检测到人</string>
<string name="pta_human_full_body">全身驱动</string>
<string name="pta_human_half_body">半身驱动</string>
<string name="select_data_photo_or_video">載入圖片或視頻</string>
<string name="toast_not_detect_gesture">檢測到手</string>
<string name="select_data_photo_or_video">载入图片或视频</string>
<string name="toast_not_detect_gesture">检测到手</string>
<string name="bg_seg_green_graphic"></string>
<string name="bg_seg_green_graphic"></string>
<string name="bg_seg_green_background">背景</string>
<string name="bg_seg_green_key_color">關鍵顏</string>
<string name="bg_seg_green_key_color">关键颜</string>
<string name="bg_seg_green_similarity">相似度</string>
<string name="bg_seg_green_smooth">平滑</string>
<string name="bg_seg_green_alpha">祛色度</string>
<string name="bg_seg_green_safe_area">安全</string>
<string name="bg_seg_green_safe_area">安全</string>
<string name="bg_seg_green_science">科技</string>
<string name="bg_seg_green_beach"></string>
<string name="bg_seg_green_beach"></string>
<string name="bg_seg_green_classroom">教室</string>
<string name="bg_seg_green_forest">森林</string>
<string name="bg_seg_green_ink">水墨</string>
<string name="dialog_guide_bg_seg_green">使用色背景拍,推薦綠色幕布效果最佳</string>
<string name="bg_seg_green_ink">水墨</string>
<string name="dialog_guide_bg_seg_green">使用色背景拍,推荐绿色幕布效果最佳</string>
<string name="dialog_i_know">我知道了</string>
<string name="download_error">載失敗</string>
<string name="download_error">载失败</string>
<string name="back">返回</string>
<string name="safe_area_tips">白色區域為安全域,不參与綠幕摳</string>
<string name="safe_area_tips">白色区域为安全域,不参与绿幕抠</string>
<string name="brow_height_tips">眉毛上下功能支持在高端上使用</string>
<string name="brow_space_tips">距功能支持在高端上使用</string>
<string name="brow_height_tips">眉毛上下功能支持在高端上使用</string>
<string name="brow_space_tips">距功能支持在高端上使用</string>
<string name="home_function_name_beauty"></string>
<string name="home_function_name_makeup"></string>
<string name="home_function_name_sticker">貼紙</string>
<string name="home_function_name_beauty_body"></string>
<string name="home_function_name_beauty"></string>
<string name="home_function_name_makeup"></string>
<string name="home_function_name_sticker">贴纸</string>
<string name="home_function_name_beauty_body"></string>
<string name="toast_not_detect_face">檢測到人</string>
<string name="toast_not_detect_face_or_body">檢測到人或人</string>
<string name="toast_not_detect_face">检测到人</string>
<string name="toast_not_detect_face_or_body">检测到人或人</string>
<string name="makeup_combination_naicha">奶茶</string>
<string name="makeup_combination_dousha">豆沙</string>
<string name="makeup_combination_chaoa">超A</string>
<string name="home_function_name_big_head">搞笑大</string>
<string name="home_function_name_big_head">搞笑大</string>
<string name="home_function_name_animoji">Animoji</string>
<string name="home_function_name_fine_sticker">精品貼紙</string>
<string name="home_function_name_fine_sticker">精品贴纸</string>
<string name="dialog_reset">重置</string>
<string name="menu_diy">自定義</string>

View File

@@ -1,8 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<color name="colorPrimary">#2ED0FF</color>
<color name="colorPrimaryDark">#2ED0FF</color>
<color name="colorAccent">#2ED0FF</color>
<color name="colorPrimary">#FF8D41</color>
<color name="colorPrimaryDark">#FF8D41</color>
<color name="colorAccent">#FF8D41</color>
<!--fulive demo 主配色-->
<color name="primary_background">#050F14</color>

View File

@@ -16,33 +16,33 @@
<string name="fu_base_input_type_single">SingleInput</string>
<string name="fu_base_input_type_double">DualInput</string>
<string name="beauty_box_heavy_blur_fine">Buffing</string>
<string name="beauty_box_color_level">Skin Tone</string>
<string name="beauty_box_red_level">Rosy</string>
<string name="beauty_box_heavy_blur_fine">Fine smooth</string>
<string name="beauty_box_color_level">Whiten</string>
<string name="beauty_box_red_level">Ruddy</string>
<string name="beauty_box_sharpen">Sharpen</string>
<string name="beauty_box_eye_bright">Brighen</string>
<string name="beauty_box_tooth_whiten">Whiten</string>
<string name="beauty_box_eye_enlarge">Enlarge</string>
<string name="beauty_box_eye_circle">Round</string>
<string name="beauty_box_cheek_natural">Origin</string>
<string name="beauty_box_eye_bright">Eye brighten</string>
<string name="beauty_box_tooth_whiten">Tooth whiten</string>
<string name="beauty_box_eye_enlarge">Eye enlarge</string>
<string name="beauty_box_eye_circle">Eye round</string>
<string name="beauty_box_cheek_natural">Natural</string>
<string name="beauty_box_cheek_goddess">Goddess</string>
<string name="beauty_box_cheek_long_face">Long face</string>
<string name="beauty_box_cheek_round_face">Round face</string>
<string name="beauty_box_cheekbones">Cheek</string>
<string name="beauty_box_lower_jaw">Jaw</string>
<string name="beauty_box_cheek_thinning">Lower Width</string>
<string name="beauty_box_cheek_v">V Shape</string>
<string name="beauty_box_cheek_narrow">Upper Width</string>
<string name="beauty_box_cheek_short">Short Face</string>
<string name="beauty_box_cheek_small">Size Face</string>
<string name="beauty_box_intensity_chin">Chin Length</string>
<string name="beauty_box_intensity_forehead">Hairline</string>
<string name="beauty_box_intensity_nose">Nose Size</string>
<string name="beauty_box_intensity_mouth">Mouth Size</string>
<string name="beauty_radio_skin_beauty">Skincare</string>
<string name="beauty_radio_face_shape">Beauty type</string>
<string name="beauty_box_cheekbones">Cheekbone</string>
<string name="beauty_box_lower_jaw">Jawbone</string>
<string name="beauty_box_cheek_thinning">Cheek thin</string>
<string name="beauty_box_cheek_v">V face</string>
<string name="beauty_box_cheek_narrow">CheekNarrow</string>
<string name="beauty_box_cheek_short">Cheek short</string>
<string name="beauty_box_cheek_small">Cheek small</string>
<string name="beauty_box_intensity_chin">Chin</string>
<string name="beauty_box_intensity_forehead">Forehead</string>
<string name="beauty_box_intensity_nose">Nose</string>
<string name="beauty_box_intensity_mouth">Mouth</string>
<string name="beauty_radio_skin_beauty">Skin</string>
<string name="beauty_radio_face_shape">Reshape</string>
<string name="beauty_radio_filter">Filter</string>
<string name="beauty_radio_style">Style recommend</string>
<string name="beauty_radio_style">Presets</string>
<string name="makeup_radio_lipstick">Lipstick</string>
<string name="makeup_radio_blusher">Blush</string>
<string name="makeup_radio_eyebrow">Eyebrow</string>
@@ -59,16 +59,16 @@
<string name="makeup_boyfriend">Boyfriend</string>
<string name="makeup_clear">Clear</string>
<string name="makeup_grapefruit">Grapefruit</string>
<string name="beauty_micro_pouch">Dark Circles</string>
<string name="beauty_micro_nasolabial">Laugh Line</string>
<string name="beauty_micro_pouch">Circle</string>
<string name="beauty_micro_nasolabial">Wrinkles</string>
<string name="beauty_micro_smile">Smile</string>
<string name="beauty_brow_height">Brow Position</string>
<string name="beauty_brow_space">Brow Distance</string>
<string name="beauty_micro_canthus">Inner Corner</string>
<string name="beauty_micro_philtrum">Mouth Position</string>
<string name="beauty_micro_long_nose">Nose Lift</string>
<string name="beauty_micro_eye_space">Eye Distance</string>
<string name="beauty_micro_eye_rotate">Eye Upturn</string>
<string name="beauty_brow_height">Brow height</string>
<string name="beauty_brow_space">Brow space</string>
<string name="beauty_micro_canthus">Canthus</string>
<string name="beauty_micro_philtrum">Philtrum</string>
<string name="beauty_micro_long_nose">Length</string>
<string name="beauty_micro_eye_space">Eye distance</string>
<string name="beauty_micro_eye_rotate">Slant</string>
<string name="beauty_face_style_none">None</string>
<string name="beauty_face_style_1">Style 1</string>
<string name="beauty_face_style_2">Style 2</string>
@@ -77,7 +77,7 @@
<string name="beauty_face_style_5">Style 5</string>
<string name="beauty_face_style_6">Style 6</string>
<string name="beauty_face_style_7">Style 7</string>
<string name="beauty_face_style_toast">To use %s, cancel \'Style recommend\' first.</string>
<string name="beauty_face_style_toast">To use %s, cancel \'Presets\' first.</string>
<string name="select_data_photo">Photo</string>
<string name="select_data_video">Video</string>
@@ -299,7 +299,7 @@
<string name="beautify_hip_slim">Hip</string>
<string name="beautify_head_slim">Head shrink</string>
<string name="beautify_leg_thin_slim">Thin leg</string>
<string name="toast_not_detect_body">No message tracking</string>
<string name="toast_not_detect_body">No body tracking</string>
<string name="pta_human_full_body">Body driver</string>
<string name="pta_human_half_body">Bust driver</string>
@@ -334,7 +334,4 @@
<string name="home_function_name_fine_sticker">Exquisite sticker</string>
<string name="dialog_reset">Reset</string>
<string name="menu_diy">Custom</string>
<string name="toast_not_detect_face">No face tracking</string>
<string name="toast_not_detect_face_or_body">No face or body tracking</string>
</resources>

View File

@@ -1,5 +1,8 @@
package com.yunbao.faceunity;
import org.junit.Test;
import static org.junit.Assert.*;
/**
* Example local unit test, which will execute on the development machine (host).
@@ -7,5 +10,8 @@ package com.yunbao.faceunity;
* @see <a href="http://d.android.com/tools/testing">Testing documentation</a>
*/
public class ExampleUnitTest {
@Test
public void addition_isCorrect() {
assertEquals(4, 2 + 2);
}
}

View File

@@ -1,36 +0,0 @@
apply plugin: 'com.android.library'
apply from: "../package_config.gradle"
android {
namespace "com.samsung.iap6helper"
compileSdk rootProject.ext.android.compileSdkVersion
defaultConfig {
minSdkVersion rootProject.ext.android.minSdkVersion
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
consumerProguardFiles "consumer-rules.pro"
versionCode rootProject.ext.android.versionCode
versionName rootProject.ext.android.versionName
targetSdkVersion rootProject.ext.android.targetSdkVersion
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.txt'
}
}
buildFeatures {
buildConfig = true
}
}
repositories {
flatDir {
dirs 'libs', '../libs'
}
}
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar', '*.aar'])
implementation 'com.google.code.gson:gson:2.8.6'
}

View File

@@ -1,25 +0,0 @@
# Add project specific ProGuard rules here.
# By default, the flags in this file are appended to flags specified
# in C:\Users\sbkim\AppData\Local\Android\sdk/tools/proguard/proguard-android.txt
# You can edit the include path and order by changing the proguardFiles
# directive in build.gradle.
#
# For more details, see
# http://developer.android.com/guide/developing/tools/proguard.html
# Add any project specific keep options here:
# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
# public *;
#}
# Uncomment this to preserve the line number information for
# debugging stack traces.
#-keepattributes SourceFile,LineNumberTable
# If you keep the line number information, uncomment this to
# hide the original source file name.
#-renamesourcefileattribute SourceFile

View File

@@ -1,19 +0,0 @@
package com.samsung.android.iap;
import com.samsung.android.iap.IAPServiceCallback;
interface IAPConnector {
boolean requestCmd(IAPServiceCallback callback, in Bundle bundle);
boolean unregisterCallback(IAPServiceCallback callback);
///////////////////////////// IAP 5.0
Bundle getProductsDetails(String packageName, String itemIds, int pagingIndex, int mode);
Bundle getOwnedList(String packageName, String itemType, int pagingIndex, int mode);
Bundle consumePurchasedItems(String packageName, String purchaseIds, int mode);
Bundle requestServiceAPI(String packageName, String requestId, String extraData);
}

View File

@@ -1,7 +0,0 @@
package com.samsung.android.iap;
import android.os.Bundle;
interface IAPServiceCallback {
oneway void responseCallback(in Bundle bundle);
}

View File

@@ -1,134 +0,0 @@
package com.samsung.utils;
import android.content.Context;
import android.util.Log;
import com.google.gson.Gson;
import com.samsung.android.sdk.iap.lib.constants.HelperDefine;
import com.samsung.android.sdk.iap.lib.helper.IapHelper;
import com.samsung.android.sdk.iap.lib.listener.OnConsumePurchasedItemsListener;
import com.samsung.android.sdk.iap.lib.listener.OnGetOwnedListListener;
import com.samsung.android.sdk.iap.lib.vo.ConsumeVo;
import com.samsung.android.sdk.iap.lib.vo.ErrorVo;
import com.samsung.android.sdk.iap.lib.vo.OwnedProductVo;
import com.samsung.iap6helper.R;
import java.util.ArrayList;
public class SamsungUtil {
private Context mContext;
IapHelper iapHelper;
private static SamsungUtil samsungUtil;
public static SamsungUtil newInstance(Context context) {
if (samsungUtil == null) {
samsungUtil = new SamsungUtil(context);
}
return samsungUtil;
}
public SamsungUtil(Context mContext) {
this.mContext = mContext;
}
/**
* 初始化
*/
public void init() {
iapHelper = IapHelper.getInstance(mContext);
//设置支付模式 OPERATION_MODE_PRODUCTION 正式模式 OPERATION_MODE_TEST 测试模式
iapHelper.setOperationMode(HelperDefine.OperationMode.OPERATION_MODE_PRODUCTION);
}
public void dispose() {
if (iapHelper != null) {
iapHelper.dispose();
}
}
/**
* 购买
*
* @param skuId
*/
public void buy(String skuId, OnPaymentListener onPaymentListener) {
//购买
iapHelper.startPayment(skuId, "", (errorVo, purchaseVo) -> {
if (purchaseVo != null) {
onPaymentListener.onPaymentSuccess(purchaseVo.getPurchaseId());
} else {
if (errorVo.getErrorCode() == HelperDefine.IAP_PAYMENT_IS_CANCELED) {
onPaymentListener.onPaymentFailed(mContext.getString(R.string.pay_cancel));
} else {
onPaymentListener.onPaymentFailed(errorVo.getErrorString());
}
}
});
}
public interface OnPaymentListener {
void onPaymentSuccess(String purchaseVo);
void onPaymentFailed(String errorVo);
}
/**
* 消耗指定商品
*
* @param skuId
*/
public void consume(String skuId) {
//消耗
iapHelper.consumePurchasedItems(skuId, new OnConsumePurchasedItemsListener() {
@Override
public void onConsumePurchasedItems(ErrorVo _errorVO, ArrayList<ConsumeVo> _consumeList) {
if (_consumeList != null) {
Log.e("samsung","消耗:" + new Gson().toJson(_consumeList));
Log.e("samsung","ErrorVo" + _errorVO.dump());
}
}
});
}
public void consumeAll(ArrayList<OwnedProductVo> _ownedList) {
if (_ownedList.size() > 0) {
iapHelper.consumePurchasedItems(_ownedList.get(0).getPurchaseId(), new OnConsumePurchasedItemsListener() {
@Override
public void onConsumePurchasedItems(ErrorVo _errorVO, ArrayList<ConsumeVo> _consumeList1) {
if (_errorVO.getErrorCode() == IapHelper.IAP_ERROR_NONE) {
Log.e("samsung","消耗:" + new Gson().toJson(_consumeList1));
Log.e("samsung","ErrorVo" + _errorVO.dump());
_ownedList.remove(0);
consumeAll(_ownedList);
}
}
});
}
}
/**
* 消耗所有未消耗的消耗品
*/
public void query() {
//查询商品 PRODUCT_TYPE_ITEM 消耗品&非消耗品
iapHelper.getOwnedList(HelperDefine.PRODUCT_TYPE_ITEM, new OnGetOwnedListListener() {
@Override
public void onGetOwnedProducts(ErrorVo _errorVo, ArrayList<OwnedProductVo> _ownedList) {
if (_errorVo != null) {
if (_errorVo.getErrorCode() == IapHelper.IAP_ERROR_NONE) {
if (_ownedList != null) {
consumeAll(_ownedList);
}
}
}
}
});
}
}

View File

@@ -1,24 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
/*
** Copyright 2006, The Android Open Source Project
**
** 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.
**
** Created with STMS Automation System
*/ -->
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="pay_cancel">支付取消</string>
<string name="pay_suc">支付成功</string>
<string name="pay_fail">支付失敗</string>
</resources>

View File

@@ -1,24 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
/*
** Copyright 2006, The Android Open Source Project
**
** 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.
**
** Created with STMS Automation System
*/ -->
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="pay_cancel">支付取消</string>
<string name="pay_suc">支付成功</string>
<string name="pay_fail">支付失敗</string>
</resources>

View File

@@ -1,24 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
/*
** Copyright 2006, The Android Open Source Project
**
** 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.
**
** Created with STMS Automation System
*/ -->
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="pay_cancel">支付取消</string>
<string name="pay_suc">支付成功</string>
<string name="pay_fail">支付失敗</string>
</resources>

View File

@@ -1,6 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="pay_cancel">Payment cancellation</string>
<string name="pay_suc">Payment successful</string>
<string name="pay_fail">Payment failed</string>
</resources>

View File

@@ -1,2 +0,0 @@
/build
*.iml

View File

@@ -1,37 +0,0 @@
apply plugin: 'com.android.library'
apply plugin: 'kotlin-android'
android {
compileSdkVersion 28
defaultConfig {
minSdkVersion 14
targetSdkVersion 28
}
compileOptions {
kotlinOptions.freeCompilerArgs += ['-module-name', "com.opensource.svgaplayer"]
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
packagingOptions {
exclude 'META-INF/ASL2.0'
exclude 'META-INF/LICENSE'
exclude 'META-INF/NOTICE'
exclude 'META-INF/NOTICE.txt'
exclude 'META-INF/LICENSE.txt'
exclude 'META-INF/MANIFEST.MF'
}
}
dependencies {
implementation 'com.squareup.wire:wire-runtime:4.4.1'
}

View File

@@ -1,17 +0,0 @@
# Add project specific ProGuard rules here.
# By default, the flags in this file are appended to flags specified
# in /Users/PonyCui_Home/Library/Android/sdk/tools/proguard/proguard-android.txt
# You can edit the include path and order by changing the proguardFiles
# directive in build.gradle.
#
# For more details, see
# http://developer.android.com/guide/developing/tools/proguard.html
# Add any project specific keep options here:
# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
# public *;
#}

View File

@@ -1,8 +0,0 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.opensource.svgaplayer">
<application android:allowBackup="true" android:label="@string/app_name">
</application>
</manifest>

View File

@@ -1,9 +0,0 @@
package com.opensource.svgaplayer
/**
* Created by miaojun on 2019/6/21.
* mail:1290846731@qq.com
*/
interface IClickAreaListener{
fun onResponseArea(key : String,x0 : Int, y0 : Int, x1 : Int, y1 : Int)
}

View File

@@ -1,119 +0,0 @@
package com.opensource.svgaplayer
import android.content.Context
import com.opensource.svgaplayer.utils.log.LogUtils
import java.io.File
import java.net.URL
import java.security.MessageDigest
/**
* SVGA 缓存管理
*/
object SVGACache {
enum class Type {
DEFAULT,
FILE
}
private const val TAG = "SVGACache"
private var type: Type = Type.DEFAULT
private var cacheDir: String = "/"
get() {
if (field != "/") {
val dir = File(field)
if (!dir.exists()) {
dir.mkdirs()
}
}
return field
}
fun onCreate(context: Context?) {
onCreate(context, Type.DEFAULT)
}
fun onCreate(context: Context?, type: Type) {
if (isInitialized()) return
context ?: return
cacheDir = "${context.cacheDir.absolutePath}/svga/"
File(cacheDir).takeIf { !it.exists() }?.mkdirs()
this.type = type
}
/**
* 清理缓存
*/
fun clearCache() {
if (!isInitialized()) {
LogUtils.error(TAG, "SVGACache is not init!")
return
}
SVGAParser.threadPoolExecutor.execute {
clearDir(cacheDir)
LogUtils.info(TAG, "Clear svga cache done!")
}
}
// 清除目录下的所有文件
internal fun clearDir(path: String) {
try {
val dir = File(path)
dir.takeIf { it.exists() }?.let { parentDir ->
parentDir.listFiles()?.forEach { file ->
if (!file.exists()) {
return@forEach
}
if (file.isDirectory) {
clearDir(file.absolutePath)
}
file.delete()
}
}
} catch (e: Exception) {
LogUtils.error(TAG, "Clear svga cache path: $path fail", e)
}
}
fun isInitialized(): Boolean {
return "/" != cacheDir && File(cacheDir).exists()
}
fun isDefaultCache(): Boolean = type == Type.DEFAULT
fun isCached(cacheKey: String): Boolean {
return if (isDefaultCache()) {
buildCacheDir(cacheKey)
} else {
buildSvgaFile(
cacheKey
)
}.exists()
}
fun buildCacheKey(str: String): String {
val messageDigest = MessageDigest.getInstance("MD5")
messageDigest.update(str.toByteArray(charset("UTF-8")))
val digest = messageDigest.digest()
var sb = ""
for (b in digest) {
sb += String.format("%02x", b)
}
return sb
}
fun buildCacheKey(url: URL): String = buildCacheKey(url.toString())
fun buildCacheDir(cacheKey: String): File {
return File("$cacheDir$cacheKey/")
}
fun buildSvgaFile(cacheKey: String): File {
return File("$cacheDir$cacheKey.svga")
}
fun buildAudioFile(audio: String): File {
return File("$cacheDir$audio.mp3")
}
}

View File

@@ -1,13 +0,0 @@
package com.opensource.svgaplayer
/**
* Created by cuiminghui on 2017/3/30.
*/
interface SVGACallback {
fun onPause()
fun onFinished()
fun onRepeat()
fun onStep(frame: Int, percentage: Double)
}

View File

@@ -1,9 +0,0 @@
package com.opensource.svgaplayer
/**
* Created by miaojun on 2019/6/21.
* mail:1290846731@qq.com
*/
interface SVGAClickAreaListener{
fun onClick(clickKey : String)
}

View File

@@ -1,106 +0,0 @@
package com.opensource.svgaplayer
import android.graphics.Canvas
import android.graphics.ColorFilter
import android.graphics.PixelFormat
import android.graphics.drawable.Drawable
import android.widget.ImageView
import com.opensource.svgaplayer.drawer.SVGACanvasDrawer
class SVGADrawable(val videoItem: SVGAVideoEntity, val dynamicItem: SVGADynamicEntity): Drawable() {
constructor(videoItem: SVGAVideoEntity): this(videoItem, SVGADynamicEntity())
var cleared = true
internal set (value) {
if (field == value) {
return
}
field = value
invalidateSelf()
}
var currentFrame = 0
internal set (value) {
if (field == value) {
return
}
field = value
invalidateSelf()
}
var scaleType: ImageView.ScaleType = ImageView.ScaleType.MATRIX
private val drawer = SVGACanvasDrawer(videoItem, dynamicItem)
override fun draw(canvas: Canvas?) {
if (cleared) {
return
}
canvas?.let {
drawer.drawFrame(it,currentFrame, scaleType)
}
}
override fun setAlpha(alpha: Int) {
}
override fun getOpacity(): Int {
return PixelFormat.TRANSPARENT
}
override fun setColorFilter(colorFilter: ColorFilter?) {
}
fun resume() {
videoItem.audioList.forEach { audio ->
audio.playID?.let {
if (SVGASoundManager.isInit()){
SVGASoundManager.resume(it)
}else{
videoItem.soundPool?.resume(it)
}
}
}
}
fun pause() {
videoItem.audioList.forEach { audio ->
audio.playID?.let {
if (SVGASoundManager.isInit()){
SVGASoundManager.pause(it)
}else{
videoItem.soundPool?.pause(it)
}
}
}
}
fun stop() {
videoItem.audioList.forEach { audio ->
audio.playID?.let {
if (SVGASoundManager.isInit()){
SVGASoundManager.stop(it)
}else{
videoItem.soundPool?.stop(it)
}
}
}
}
fun clear() {
videoItem.audioList.forEach { audio ->
audio.playID?.let {
if (SVGASoundManager.isInit()){
SVGASoundManager.stop(it)
}else{
videoItem.soundPool?.stop(it)
}
}
audio.playID = null
}
videoItem.clear()
}
}

View File

@@ -1,153 +0,0 @@
package com.opensource.svgaplayer
import android.graphics.Bitmap
import android.graphics.BitmapFactory
import android.graphics.Canvas
import android.text.BoringLayout
import android.text.StaticLayout
import android.text.TextPaint
import java.net.HttpURLConnection
import java.net.URL
/**
* Created by cuiminghui on 2017/3/30.
*/
class SVGADynamicEntity {
internal var dynamicHidden: HashMap<String, Boolean> = hashMapOf()
internal var dynamicImage: HashMap<String, Bitmap> = hashMapOf()
internal var dynamicText: HashMap<String, String> = hashMapOf()
internal var dynamicTextPaint: HashMap<String, TextPaint> = hashMapOf()
internal var dynamicStaticLayoutText: HashMap<String, StaticLayout> = hashMapOf()
internal var dynamicBoringLayoutText: HashMap<String, BoringLayout> = hashMapOf()
internal var dynamicDrawer: HashMap<String, (canvas: Canvas, frameIndex: Int) -> Boolean> = hashMapOf()
//点击事件回调map
internal var mClickMap : HashMap<String, IntArray> = hashMapOf()
internal var dynamicIClickArea: HashMap<String, IClickAreaListener> = hashMapOf()
internal var dynamicDrawerSized: HashMap<String, (canvas: Canvas, frameIndex: Int, width: Int, height: Int) -> Boolean> = hashMapOf()
internal var isTextDirty = false
fun setHidden(value: Boolean, forKey: String) {
this.dynamicHidden.put(forKey, value)
}
fun setDynamicImage(bitmap: Bitmap, forKey: String) {
this.dynamicImage.put(forKey, bitmap)
}
fun setDynamicImage(url: String, forKey: String) {
val handler = android.os.Handler()
SVGAParser.threadPoolExecutor.execute {
(URL(url).openConnection() as? HttpURLConnection)?.let {
try {
it.connectTimeout = 20 * 1000
it.requestMethod = "GET"
it.connect()
it.inputStream.use { stream ->
BitmapFactory.decodeStream(stream)?.let {
handler.post { setDynamicImage(it, forKey) }
}
}
} catch (e: Exception) {
e.printStackTrace()
} finally {
try {
it.disconnect()
} catch (disconnectException: Throwable) {
// ignored here
}
}
}
}
}
fun setDynamicText(text: String, textPaint: TextPaint, forKey: String) {
this.isTextDirty = true
this.dynamicText.put(forKey, text)
this.dynamicTextPaint.put(forKey, textPaint)
}
fun setDynamicText(layoutText: StaticLayout, forKey: String) {
this.isTextDirty = true
this.dynamicStaticLayoutText.put(forKey, layoutText)
}
fun setDynamicText(layoutText: BoringLayout, forKey: String) {
this.isTextDirty = true
BoringLayout.isBoring(layoutText.text,layoutText.paint)?.let {
this.dynamicBoringLayoutText.put(forKey,layoutText)
}
}
fun setDynamicDrawer(drawer: (canvas: Canvas, frameIndex: Int) -> Boolean, forKey: String) {
this.dynamicDrawer.put(forKey, drawer)
}
fun setClickArea(clickKey: List<String>) {
for(itemKey in clickKey){
dynamicIClickArea.put(itemKey,object : IClickAreaListener {
override fun onResponseArea(key: String, x0: Int, y0: Int, x1: Int, y1: Int) {
mClickMap.let {
if(it.get(key) == null){
it.put(key, intArrayOf(x0,y0,x1,y1))
}else{
it.get(key)?.let {
it[0] = x0
it[1] = y0
it[2] = x1
it[3] = y1
}
}
}
}
})
}
}
fun setClickArea(clickKey: String) {
dynamicIClickArea.put(clickKey, object : IClickAreaListener {
override fun onResponseArea(key: String, x0: Int, y0: Int, x1: Int, y1: Int) {
mClickMap.let {
if (it.get(key) == null) {
it.put(key, intArrayOf(x0, y0, x1, y1))
} else {
it.get(key)?.let {
it[0] = x0
it[1] = y0
it[2] = x1
it[3] = y1
}
}
}
}
})
}
fun setDynamicDrawerSized(drawer: (canvas: Canvas, frameIndex: Int, width: Int, height: Int) -> Boolean, forKey: String) {
this.dynamicDrawerSized.put(forKey, drawer)
}
fun clearDynamicObjects() {
this.isTextDirty = true
this.dynamicHidden.clear()
this.dynamicImage.clear()
this.dynamicText.clear()
this.dynamicTextPaint.clear()
this.dynamicStaticLayoutText.clear()
this.dynamicBoringLayoutText.clear()
this.dynamicDrawer.clear()
this.dynamicIClickArea.clear()
this.mClickMap.clear()
this.dynamicDrawerSized.clear()
}
}

View File

@@ -1,329 +0,0 @@
package com.opensource.svgaplayer
import android.animation.Animator
import android.animation.ValueAnimator
import android.annotation.SuppressLint
import android.content.Context
import android.os.Build
import android.util.AttributeSet
import android.view.MotionEvent
import android.view.View
import android.view.animation.LinearInterpolator
import android.widget.ImageView
import com.opensource.svgaplayer.utils.SVGARange
import com.opensource.svgaplayer.utils.log.LogUtils
import java.lang.ref.WeakReference
import java.net.URL
/**
* Created by PonyCui on 2017/3/29.
*/
open class SVGAImageView @JvmOverloads constructor(
context: Context,
attrs: AttributeSet? = null,
defStyleAttr: Int = 0
) : ImageView(context, attrs, defStyleAttr) {
private val TAG = "SVGAImageView"
enum class FillMode {
Backward,
Forward,
Clear,
}
var isAnimating = false
private set
var loops = 0
@Deprecated(
"It is recommended to use clearAfterDetached, or manually call to SVGAVideoEntity#clear." +
"If you just consider cleaning up the canvas after playing, you can use FillMode#Clear.",
level = DeprecationLevel.WARNING
)
var clearsAfterStop = false
var clearsAfterDetached = false
var fillMode: FillMode = FillMode.Forward
var callback: SVGACallback? = null
private var mAnimator: ValueAnimator? = null
private var mItemClickAreaListener: SVGAClickAreaListener? = null
private var mAntiAlias = true
private var mAutoPlay = true
private val mAnimatorListener = AnimatorListener(this)
private val mAnimatorUpdateListener = AnimatorUpdateListener(this)
private var mStartFrame = 0
private var mEndFrame = 0
init {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN_MR2) {
this.setLayerType(View.LAYER_TYPE_SOFTWARE, null)
}
attrs?.let { loadAttrs(it) }
}
private fun loadAttrs(attrs: AttributeSet) {
val typedArray = context.theme.obtainStyledAttributes(attrs, R.styleable.SVGAImageView, 0, 0)
loops = typedArray.getInt(R.styleable.SVGAImageView_loopCount, 0)
clearsAfterStop = typedArray.getBoolean(R.styleable.SVGAImageView_clearsAfterStop, false)
clearsAfterDetached = typedArray.getBoolean(R.styleable.SVGAImageView_clearsAfterDetached, false)
mAntiAlias = typedArray.getBoolean(R.styleable.SVGAImageView_antiAlias, true)
mAutoPlay = typedArray.getBoolean(R.styleable.SVGAImageView_autoPlay, true)
typedArray.getString(R.styleable.SVGAImageView_fillMode)?.let {
when (it) {
"0" -> {
fillMode = FillMode.Backward
}
"1" -> {
fillMode = FillMode.Forward
}
"2" -> {
fillMode = FillMode.Clear
}
}
}
typedArray.getString(R.styleable.SVGAImageView_source)?.let {
parserSource(it)
}
typedArray.recycle()
}
private fun parserSource(source: String) {
val refImgView = WeakReference<SVGAImageView>(this)
val parser = SVGAParser(context)
if (source.startsWith("http://") || source.startsWith("https://")) {
parser.decodeFromURL(URL(source), createParseCompletion(refImgView))
} else {
parser.decodeFromAssets(source, createParseCompletion(refImgView))
}
}
private fun createParseCompletion(ref: WeakReference<SVGAImageView>): SVGAParser.ParseCompletion {
return object : SVGAParser.ParseCompletion {
override fun onComplete(videoItem: SVGAVideoEntity) {
ref.get()?.startAnimation(videoItem)
}
override fun onError() {}
}
}
private fun startAnimation(videoItem: SVGAVideoEntity) {
this@SVGAImageView.post {
videoItem.antiAlias = mAntiAlias
setVideoItem(videoItem)
getSVGADrawable()?.scaleType = scaleType
if (mAutoPlay) {
startAnimation()
}
}
}
fun startAnimation() {
startAnimation(null, false)
}
fun startAnimation(range: SVGARange?, reverse: Boolean = false) {
stopAnimation(false)
play(range, reverse)
}
private fun play(range: SVGARange?, reverse: Boolean) {
LogUtils.info(TAG, "================ start animation ================")
val drawable = getSVGADrawable() ?: return
setupDrawable()
mStartFrame = Math.max(0, range?.location ?: 0)
val videoItem = drawable.videoItem
mEndFrame = Math.min(videoItem.frames - 1, ((range?.location ?: 0) + (range?.length ?: Int.MAX_VALUE) - 1))
val animator = ValueAnimator.ofInt(mStartFrame, mEndFrame)
animator.interpolator = LinearInterpolator()
animator.duration = ((mEndFrame - mStartFrame + 1) * (1000 / videoItem.FPS) / generateScale()).toLong()
animator.repeatCount = if (loops <= 0) 99999 else loops - 1
animator.addUpdateListener(mAnimatorUpdateListener)
animator.addListener(mAnimatorListener)
if (reverse) {
animator.reverse()
} else {
animator.start()
}
mAnimator = animator
}
private fun setupDrawable() {
val drawable = getSVGADrawable() ?: return
drawable.cleared = false
drawable.scaleType = scaleType
}
private fun getSVGADrawable(): SVGADrawable? {
return drawable as? SVGADrawable
}
@Suppress("UNNECESSARY_SAFE_CALL")
private fun generateScale(): Double {
var scale = 1.0
try {
val animatorClass = Class.forName("android.animation.ValueAnimator") ?: return scale
val getMethod = animatorClass.getDeclaredMethod("getDurationScale") ?: return scale
scale = (getMethod.invoke(animatorClass) as Float).toDouble()
if (scale == 0.0) {
val setMethod = animatorClass.getDeclaredMethod("setDurationScale",Float::class.java) ?: return scale
setMethod.isAccessible = true
setMethod.invoke(animatorClass,1.0f)
scale = 1.0
LogUtils.info(TAG,
"The animation duration scale has been reset to" +
" 1.0x, because you closed it on developer options.")
}
} catch (ignore: Exception) {
ignore.printStackTrace()
}
return scale
}
private fun onAnimatorUpdate(animator: ValueAnimator?) {
val drawable = getSVGADrawable() ?: return
drawable.currentFrame = animator?.animatedValue as Int
val percentage = (drawable.currentFrame + 1).toDouble() / drawable.videoItem.frames.toDouble()
callback?.onStep(drawable.currentFrame, percentage)
}
private fun onAnimationEnd(animation: Animator?) {
isAnimating = false
stopAnimation()
val drawable = getSVGADrawable()
if (drawable != null) {
when (fillMode) {
FillMode.Backward -> {
drawable.currentFrame = mStartFrame
}
FillMode.Forward -> {
drawable.currentFrame = mEndFrame
}
FillMode.Clear -> {
drawable.cleared = true
}
}
}
callback?.onFinished()
}
fun clear() {
getSVGADrawable()?.cleared = true
getSVGADrawable()?.clear()
// 清除对 drawable 的引用
setImageDrawable(null)
}
fun pauseAnimation() {
stopAnimation(false)
callback?.onPause()
}
fun stopAnimation() {
stopAnimation(clear = clearsAfterStop)
}
fun stopAnimation(clear: Boolean) {
mAnimator?.cancel()
mAnimator?.removeAllListeners()
mAnimator?.removeAllUpdateListeners()
getSVGADrawable()?.stop()
getSVGADrawable()?.cleared = clear
}
fun setVideoItem(videoItem: SVGAVideoEntity?) {
setVideoItem(videoItem, SVGADynamicEntity())
}
fun setVideoItem(videoItem: SVGAVideoEntity?, dynamicItem: SVGADynamicEntity?) {
if (videoItem == null) {
setImageDrawable(null)
} else {
val drawable = SVGADrawable(videoItem, dynamicItem ?: SVGADynamicEntity())
drawable.cleared = true
setImageDrawable(drawable)
}
}
fun stepToFrame(frame: Int, andPlay: Boolean) {
pauseAnimation()
val drawable = getSVGADrawable() ?: return
drawable.currentFrame = frame
if (andPlay) {
startAnimation()
mAnimator?.let {
it.currentPlayTime = (Math.max(0.0f, Math.min(1.0f, (frame.toFloat() / drawable.videoItem.frames.toFloat()))) * it.duration).toLong()
}
}
}
fun stepToPercentage(percentage: Double, andPlay: Boolean) {
val drawable = drawable as? SVGADrawable ?: return
var frame = (drawable.videoItem.frames * percentage).toInt()
if (frame >= drawable.videoItem.frames && frame > 0) {
frame = drawable.videoItem.frames - 1
}
stepToFrame(frame, andPlay)
}
fun setOnAnimKeyClickListener(clickListener : SVGAClickAreaListener){
mItemClickAreaListener = clickListener
}
@SuppressLint("ClickableViewAccessibility")
override fun onTouchEvent(event: MotionEvent?): Boolean {
if (event?.action != MotionEvent.ACTION_DOWN) {
return super.onTouchEvent(event)
}
val drawable = getSVGADrawable() ?: return super.onTouchEvent(event)
for ((key, value) in drawable.dynamicItem.mClickMap) {
if (event.x >= value[0] && event.x <= value[2] && event.y >= value[1] && event.y <= value[3]) {
mItemClickAreaListener?.let {
it.onClick(key)
return true
}
}
}
return super.onTouchEvent(event)
}
override fun onDetachedFromWindow() {
super.onDetachedFromWindow()
stopAnimation(clearsAfterDetached)
if (clearsAfterDetached) {
clear()
}
}
private class AnimatorListener(view: SVGAImageView) : Animator.AnimatorListener {
private val weakReference = WeakReference<SVGAImageView>(view)
override fun onAnimationRepeat(animation: Animator?) {
weakReference.get()?.callback?.onRepeat()
}
override fun onAnimationEnd(animation: Animator?) {
weakReference.get()?.onAnimationEnd(animation)
}
override fun onAnimationCancel(animation: Animator?) {
weakReference.get()?.isAnimating = false
}
override fun onAnimationStart(animation: Animator?) {
weakReference.get()?.isAnimating = true
}
} // end of AnimatorListener
private class AnimatorUpdateListener(view: SVGAImageView) : ValueAnimator.AnimatorUpdateListener {
private val weakReference = WeakReference<SVGAImageView>(view)
override fun onAnimationUpdate(animation: ValueAnimator?) {
weakReference.get()?.onAnimatorUpdate(animation)
}
} // end of AnimatorUpdateListener
}

View File

@@ -1,565 +0,0 @@
package com.opensource.svgaplayer
import android.content.Context
import android.net.http.HttpResponseCache
import android.os.Handler
import android.os.Looper
import com.opensource.svgaplayer.proto.MovieEntity
import com.opensource.svgaplayer.utils.log.LogUtils
import org.json.JSONObject
import java.io.*
import java.net.HttpURLConnection
import java.net.URL
import java.util.concurrent.Executors
import java.util.concurrent.ThreadPoolExecutor
import java.util.concurrent.atomic.AtomicInteger
import java.util.zip.Inflater
import java.util.zip.ZipInputStream
/**
* Created by PonyCui 16/6/18.
*/
private var fileLock: Int = 0
private var isUnzipping = false
class SVGAParser(context: Context?) {
private var mContext = context?.applicationContext
init {
SVGACache.onCreate(context)
}
@Volatile
private var mFrameWidth: Int = 0
@Volatile
private var mFrameHeight: Int = 0
interface ParseCompletion {
fun onComplete(videoItem: SVGAVideoEntity)
fun onError()
}
interface PlayCallback{
fun onPlay(file: List<File>)
}
open class FileDownloader {
var noCache = false
open fun resume(url: URL, complete: (inputStream: InputStream) -> Unit, failure: (e: Exception) -> Unit): () -> Unit {
var cancelled = false
val cancelBlock = {
cancelled = true
}
threadPoolExecutor.execute {
try {
LogUtils.info(TAG, "================ svga file download start ================")
if (HttpResponseCache.getInstalled() == null && !noCache) {
LogUtils.error(TAG, "SVGAParser can not handle cache before install HttpResponseCache. see https://github.com/yyued/SVGAPlayer-Android#cache")
LogUtils.error(TAG, "在配置 HttpResponseCache 前 SVGAParser 无法缓存. 查看 https://github.com/yyued/SVGAPlayer-Android#cache ")
}
(url.openConnection() as? HttpURLConnection)?.let {
it.connectTimeout = 20 * 1000
it.requestMethod = "GET"
it.setRequestProperty("Connection", "close")
it.connect()
it.inputStream.use { inputStream ->
ByteArrayOutputStream().use { outputStream ->
val buffer = ByteArray(4096)
var count: Int
while (true) {
if (cancelled) {
LogUtils.warn(TAG, "================ svga file download canceled ================")
break
}
count = inputStream.read(buffer, 0, 4096)
if (count == -1) {
break
}
outputStream.write(buffer, 0, count)
}
if (cancelled) {
LogUtils.warn(TAG, "================ svga file download canceled ================")
return@execute
}
ByteArrayInputStream(outputStream.toByteArray()).use {
LogUtils.info(TAG, "================ svga file download complete ================")
complete(it)
}
}
}
}
} catch (e: Exception) {
LogUtils.error(TAG, "================ svga file download fail ================")
LogUtils.error(TAG, "error: ${e.message}")
e.printStackTrace()
failure(e)
}
}
return cancelBlock
}
}
var fileDownloader = FileDownloader()
companion object {
private const val TAG = "SVGAParser"
private val threadNum = AtomicInteger(0)
private var mShareParser = SVGAParser(null)
internal var threadPoolExecutor = Executors.newCachedThreadPool { r ->
Thread(r, "SVGAParser-Thread-${threadNum.getAndIncrement()}")
}
fun setThreadPoolExecutor(executor: ThreadPoolExecutor) {
threadPoolExecutor = executor
}
fun shareParser(): SVGAParser {
return mShareParser
}
}
fun init(context: Context) {
mContext = context.applicationContext
SVGACache.onCreate(mContext)
}
fun setFrameSize(frameWidth: Int, frameHeight: Int) {
mFrameWidth = frameWidth
mFrameHeight = frameHeight
}
fun decodeFromAssets(
name: String,
callback: ParseCompletion?,
playCallback: PlayCallback? = null
) {
if (mContext == null) {
LogUtils.error(TAG, "在配置 SVGAParser context 前, 无法解析 SVGA 文件。")
return
}
LogUtils.info(TAG, "================ decode $name from assets ================")
threadPoolExecutor.execute {
try {
mContext?.assets?.open(name)?.let {
this.decodeFromInputStream(
it,
SVGACache.buildCacheKey("file:///assets/$name"),
callback,
true,
playCallback,
alias = name
)
}
} catch (e: Exception) {
this.invokeErrorCallback(e, callback, name)
}
}
}
fun decodeFromURL(
url: URL,
callback: ParseCompletion?,
playCallback: PlayCallback? = null
): (() -> Unit)? {
if (mContext == null) {
LogUtils.error(TAG, "在配置 SVGAParser context 前, 无法解析 SVGA 文件。")
return null
}
val urlPath = url.toString()
LogUtils.info(TAG, "================ decode from url: $urlPath ================")
val cacheKey = SVGACache.buildCacheKey(url);
return if (SVGACache.isCached(cacheKey)) {
LogUtils.info(TAG, "this url cached")
threadPoolExecutor.execute {
if (SVGACache.isDefaultCache()) {
this.decodeFromCacheKey(cacheKey, callback, alias = urlPath)
} else {
this.decodeFromSVGAFileCacheKey(cacheKey, callback, playCallback, alias = urlPath)
}
}
return null
} else {
LogUtils.info(TAG, "no cached, prepare to download")
fileDownloader.resume(url, {
this.decodeFromInputStream(
it,
cacheKey,
callback,
false,
playCallback,
alias = urlPath
)
}, {
LogUtils.error(
TAG,
"================ svga file: $url download fail ================"
)
this.invokeErrorCallback(it, callback, alias = urlPath)
})
}
}
/**
* 读取解析本地缓存的 svga 文件.
*/
fun decodeFromSVGAFileCacheKey(
cacheKey: String,
callback: ParseCompletion?,
playCallback: PlayCallback?,
alias: String? = null
) {
threadPoolExecutor.execute {
try {
LogUtils.info(TAG, "================ decode $alias from svga cachel file to entity ================")
FileInputStream(SVGACache.buildSvgaFile(cacheKey)).use { inputStream ->
readAsBytes(inputStream)?.let { bytes ->
if (isZipFile(bytes)) {
this.decodeFromCacheKey(cacheKey, callback, alias)
} else {
LogUtils.info(TAG, "inflate start")
inflate(bytes)?.let {
LogUtils.info(TAG, "inflate complete")
val videoItem = SVGAVideoEntity(
MovieEntity.ADAPTER.decode(it),
File(cacheKey),
mFrameWidth,
mFrameHeight
)
LogUtils.info(TAG, "SVGAVideoEntity prepare start")
videoItem.prepare({
LogUtils.info(TAG, "SVGAVideoEntity prepare success")
this.invokeCompleteCallback(videoItem, callback, alias)
},playCallback)
} ?: this.invokeErrorCallback(
Exception("inflate(bytes) cause exception"),
callback,
alias
)
}
} ?: this.invokeErrorCallback(
Exception("readAsBytes(inputStream) cause exception"),
callback,
alias
)
}
} catch (e: java.lang.Exception) {
this.invokeErrorCallback(e, callback, alias)
} finally {
LogUtils.info(TAG, "================ decode $alias from svga cachel file to entity end ================")
}
}
}
fun decodeFromInputStream(
inputStream: InputStream,
cacheKey: String,
callback: ParseCompletion?,
closeInputStream: Boolean = false,
playCallback: PlayCallback? = null,
alias: String? = null
) {
if (mContext == null) {
LogUtils.error(TAG, "在配置 SVGAParser context 前, 无法解析 SVGA 文件。")
return
}
LogUtils.info(TAG, "================ decode $alias from input stream ================")
threadPoolExecutor.execute {
try {
readAsBytes(inputStream)?.let { bytes ->
if (isZipFile(bytes)) {
LogUtils.info(TAG, "decode from zip file")
if (!SVGACache.buildCacheDir(cacheKey).exists() || isUnzipping) {
synchronized(fileLock) {
if (!SVGACache.buildCacheDir(cacheKey).exists()) {
isUnzipping = true
LogUtils.info(TAG, "no cached, prepare to unzip")
ByteArrayInputStream(bytes).use {
unzip(it, cacheKey)
isUnzipping = false
LogUtils.info(TAG, "unzip success")
}
}
}
}
this.decodeFromCacheKey(cacheKey, callback, alias)
} else {
if (!SVGACache.isDefaultCache()) {
// 如果 SVGACache 设置类型为 FILE
threadPoolExecutor.execute {
SVGACache.buildSvgaFile(cacheKey).let { cacheFile ->
try {
cacheFile.takeIf { !it.exists() }?.createNewFile()
FileOutputStream(cacheFile).write(bytes)
} catch (e: Exception) {
LogUtils.error(TAG, "create cache file fail.", e)
cacheFile.delete()
}
}
}
}
LogUtils.info(TAG, "inflate start")
inflate(bytes)?.let {
LogUtils.info(TAG, "inflate complete")
val videoItem = SVGAVideoEntity(
MovieEntity.ADAPTER.decode(it),
File(cacheKey),
mFrameWidth,
mFrameHeight
)
LogUtils.info(TAG, "SVGAVideoEntity prepare start")
videoItem.prepare({
LogUtils.info(TAG, "SVGAVideoEntity prepare success")
this.invokeCompleteCallback(videoItem, callback, alias)
},playCallback)
} ?: this.invokeErrorCallback(
Exception("inflate(bytes) cause exception"),
callback,
alias
)
}
} ?: this.invokeErrorCallback(
Exception("readAsBytes(inputStream) cause exception"),
callback,
alias
)
} catch (e: java.lang.Exception) {
this.invokeErrorCallback(e, callback, alias)
} finally {
if (closeInputStream) {
inputStream.close()
}
LogUtils.info(TAG, "================ decode $alias from input stream end ================")
}
}
}
/**
* @deprecated from 2.4.0
*/
@Deprecated("This method has been deprecated from 2.4.0.", ReplaceWith("this.decodeFromAssets(assetsName, callback)"))
fun parse(assetsName: String, callback: ParseCompletion?) {
this.decodeFromAssets(assetsName, callback,null)
}
/**
* @deprecated from 2.4.0
*/
@Deprecated("This method has been deprecated from 2.4.0.", ReplaceWith("this.decodeFromURL(url, callback)"))
fun parse(url: URL, callback: ParseCompletion?) {
this.decodeFromURL(url, callback,null)
}
/**
* @deprecated from 2.4.0
*/
@Deprecated("This method has been deprecated from 2.4.0.", ReplaceWith("this.decodeFromInputStream(inputStream, cacheKey, callback, closeInputStream)"))
fun parse(
inputStream: InputStream,
cacheKey: String,
callback: ParseCompletion?,
closeInputStream: Boolean = false
) {
this.decodeFromInputStream(inputStream, cacheKey, callback, closeInputStream,null)
}
private fun invokeCompleteCallback(
videoItem: SVGAVideoEntity,
callback: ParseCompletion?,
alias: String?
) {
Handler(Looper.getMainLooper()).post {
LogUtils.info(TAG, "================ $alias parser complete ================")
callback?.onComplete(videoItem)
}
}
private fun invokeErrorCallback(
e: Exception,
callback: ParseCompletion?,
alias: String?
) {
e.printStackTrace()
LogUtils.error(TAG, "================ $alias parser error ================")
LogUtils.error(TAG, "$alias parse error", e)
Handler(Looper.getMainLooper()).post {
callback?.onError()
}
}
private fun decodeFromCacheKey(
cacheKey: String,
callback: ParseCompletion?,
alias: String?
) {
LogUtils.info(TAG, "================ decode $alias from cache ================")
LogUtils.debug(TAG, "decodeFromCacheKey called with cacheKey : $cacheKey")
if (mContext == null) {
LogUtils.error(TAG, "在配置 SVGAParser context 前, 无法解析 SVGA 文件。")
return
}
try {
val cacheDir = SVGACache.buildCacheDir(cacheKey)
File(cacheDir, "movie.binary").takeIf { it.isFile }?.let { binaryFile ->
try {
LogUtils.info(TAG, "binary change to entity")
FileInputStream(binaryFile).use {
LogUtils.info(TAG, "binary change to entity success")
this.invokeCompleteCallback(
SVGAVideoEntity(
MovieEntity.ADAPTER.decode(it),
cacheDir,
mFrameWidth,
mFrameHeight
),
callback,
alias
)
}
} catch (e: Exception) {
LogUtils.error(TAG, "binary change to entity fail", e)
cacheDir.delete()
binaryFile.delete()
throw e
}
}
File(cacheDir, "movie.spec").takeIf { it.isFile }?.let { jsonFile ->
try {
LogUtils.info(TAG, "spec change to entity")
FileInputStream(jsonFile).use { fileInputStream ->
ByteArrayOutputStream().use { byteArrayOutputStream ->
val buffer = ByteArray(2048)
while (true) {
val size = fileInputStream.read(buffer, 0, buffer.size)
if (size == -1) {
break
}
byteArrayOutputStream.write(buffer, 0, size)
}
byteArrayOutputStream.toString().let {
JSONObject(it).let {
LogUtils.info(TAG, "spec change to entity success")
this.invokeCompleteCallback(
SVGAVideoEntity(
it,
cacheDir,
mFrameWidth,
mFrameHeight
),
callback,
alias
)
}
}
}
}
} catch (e: Exception) {
LogUtils.error(TAG, "$alias movie.spec change to entity fail", e)
cacheDir.delete()
jsonFile.delete()
throw e
}
}
} catch (e: Exception) {
this.invokeErrorCallback(e, callback, alias)
}
}
private fun readAsBytes(inputStream: InputStream): ByteArray? {
ByteArrayOutputStream().use { byteArrayOutputStream ->
val byteArray = ByteArray(2048)
while (true) {
val count = inputStream.read(byteArray, 0, 2048)
if (count <= 0) {
break
} else {
byteArrayOutputStream.write(byteArray, 0, count)
}
}
return byteArrayOutputStream.toByteArray()
}
}
private fun inflate(byteArray: ByteArray): ByteArray? {
val inflater = Inflater()
inflater.setInput(byteArray, 0, byteArray.size)
val inflatedBytes = ByteArray(2048)
ByteArrayOutputStream().use { inflatedOutputStream ->
while (true) {
val count = inflater.inflate(inflatedBytes, 0, 2048)
if (count <= 0) {
break
} else {
inflatedOutputStream.write(inflatedBytes, 0, count)
}
}
inflater.end()
return inflatedOutputStream.toByteArray()
}
}
// 是否是 zip 文件
private fun isZipFile(bytes: ByteArray): Boolean {
return bytes.size > 4 && bytes[0].toInt() == 80 && bytes[1].toInt() == 75 && bytes[2].toInt() == 3 && bytes[3].toInt() == 4
}
// 解压
private fun unzip(inputStream: InputStream, cacheKey: String) {
LogUtils.info(TAG, "================ unzip prepare ================")
val cacheDir = SVGACache.buildCacheDir(cacheKey)
cacheDir.mkdirs()
try {
BufferedInputStream(inputStream).use {
ZipInputStream(it).use { zipInputStream ->
while (true) {
val zipItem = zipInputStream.nextEntry ?: break
if (zipItem.name.contains("../")) {
// 解压路径存在路径穿越问题,直接过滤
continue
}
if (zipItem.name.contains("/")) {
continue
}
val file = File(cacheDir, zipItem.name)
ensureUnzipSafety(file, cacheDir.absolutePath)
FileOutputStream(file).use { fileOutputStream ->
val buff = ByteArray(2048)
while (true) {
val readBytes = zipInputStream.read(buff)
if (readBytes <= 0) {
break
}
fileOutputStream.write(buff, 0, readBytes)
}
}
LogUtils.error(TAG, "================ unzip complete ================")
zipInputStream.closeEntry()
}
}
}
} catch (e: Exception) {
LogUtils.error(TAG, "================ unzip error ================")
LogUtils.error(TAG, "error", e)
SVGACache.clearDir(cacheDir.absolutePath)
cacheDir.delete()
throw e
}
}
// 检查 zip 路径穿透
private fun ensureUnzipSafety(outputFile: File, dstDirPath: String) {
val dstDirCanonicalPath = File(dstDirPath).canonicalPath
val outputFileCanonicalPath = outputFile.canonicalPath
if (!outputFileCanonicalPath.startsWith(dstDirCanonicalPath)) {
throw IOException("Found Zip Path Traversal Vulnerability with $dstDirCanonicalPath")
}
}
}

View File

@@ -1,19 +0,0 @@
package com.opensource.svgaplayer
import android.content.Context
import android.util.AttributeSet
/**
* Created by cuiminghui on 2017/3/30.
* @deprecated from 2.4.0
*/
@Deprecated("This class has been deprecated from 2.4.0. We don't recommend you to use it.")
class SVGAPlayer: SVGAImageView {
constructor(context: Context) : super(context) {}
constructor(context: Context, attrs: AttributeSet) : super(context, attrs) {}
constructor(context: Context, attrs: AttributeSet, defStyleAttr: Int) : super(context, attrs, defStyleAttr) {}
}

View File

@@ -1,194 +0,0 @@
package com.opensource.svgaplayer
/**
* @author Devin
*
* Created on 2/24/21.
*/
import android.media.AudioAttributes
import android.media.AudioManager
import android.media.SoundPool
import android.os.Build
import com.opensource.svgaplayer.utils.log.LogUtils
import java.io.FileDescriptor
/**
* Author : llk
* Time : 2020/10/24
* Description : svga 音频加载管理类
* 将 SoundPool 抽取到单例里边,规避 load 资源之后不回调 onLoadComplete 的问题。
*
* 需要对 SVGASoundManager 进行初始化
*
* 相关文章Android SoundPool 崩溃问题研究
* https://zhuanlan.zhihu.com/p/29985198
*/
object SVGASoundManager {
private val TAG = SVGASoundManager::class.java.simpleName
private var soundPool: SoundPool? = null
private val soundCallBackMap: MutableMap<Int, SVGASoundCallBack> = mutableMapOf()
/**
* 音量设置,范围在 [0, 1] 之间
*/
private var volume: Float = 1f
/**
* 音频回调
*/
internal interface SVGASoundCallBack {
// 音量发生变化
fun onVolumeChange(value: Float)
// 音频加载完成
fun onComplete()
}
fun init() {
init(20)
}
fun init(maxStreams: Int) {
LogUtils.debug(TAG, "**************** init **************** $maxStreams")
if (soundPool != null) {
return
}
soundPool = getSoundPool(maxStreams)
soundPool?.setOnLoadCompleteListener { _, soundId, status ->
LogUtils.debug(TAG, "SoundPool onLoadComplete soundId=$soundId status=$status")
if (status == 0) { //加载该声音成功
if (soundCallBackMap.containsKey(soundId)) {
soundCallBackMap[soundId]?.onComplete()
}
}
}
}
fun release() {
LogUtils.debug(TAG, "**************** release ****************")
if (soundCallBackMap.isNotEmpty()) {
soundCallBackMap.clear()
}
}
/**
* 根据当前播放实体,设置音量
*
* @param volume 范围在 [0, 1]
* @param entity 根据需要控制对应 entity 音量大小,若为空则控制所有正在播放的音频音量
*/
fun setVolume(volume: Float, entity: SVGAVideoEntity? = null) {
if (!checkInit()) {
return
}
if (volume < 0f || volume > 1f) {
LogUtils.error(TAG, "The volume level is in the range of 0 to 1 ")
return
}
if (entity == null) {
this.volume = volume
val iterator = soundCallBackMap.entries.iterator()
while (iterator.hasNext()) {
val e = iterator.next()
e.value.onVolumeChange(volume)
}
return
}
val soundPool = soundPool ?: return
entity.audioList.forEach { audio ->
val streamId = audio.playID ?: return
soundPool.setVolume(streamId, volume, volume)
}
}
/**
* 是否初始化
* 如果没有初始化就使用原来SvgaPlayer库的音频加载逻辑。
* @return true 则已初始化, 否则为 false
*/
internal fun isInit(): Boolean {
return soundPool != null
}
private fun checkInit(): Boolean {
val isInit = isInit()
if (!isInit) {
LogUtils.error(TAG, "soundPool is null, you need call init() !!!")
}
return isInit
}
private fun getSoundPool(maxStreams: Int) = if (Build.VERSION.SDK_INT >= 21) {
val attributes = AudioAttributes.Builder()
.setUsage(AudioAttributes.USAGE_MEDIA)
.build()
SoundPool.Builder().setAudioAttributes(attributes)
.setMaxStreams(maxStreams)
.build()
} else {
SoundPool(maxStreams, AudioManager.STREAM_MUSIC, 0)
}
internal fun load(callBack: SVGASoundCallBack?,
fd: FileDescriptor?,
offset: Long,
length: Long,
priority: Int): Int {
if (!checkInit()) return -1
val soundId = soundPool!!.load(fd, offset, length, priority)
LogUtils.debug(TAG, "load soundId=$soundId callBack=$callBack")
if (callBack != null && !soundCallBackMap.containsKey(soundId)) {
soundCallBackMap[soundId] = callBack
}
return soundId
}
internal fun unload(soundId: Int) {
if (!checkInit()) return
LogUtils.debug(TAG, "unload soundId=$soundId")
soundPool!!.unload(soundId)
soundCallBackMap.remove(soundId)
}
internal fun play(soundId: Int): Int {
if (!checkInit()) return -1
LogUtils.debug(TAG, "play soundId=$soundId")
return soundPool!!.play(soundId, volume, volume, 1, 0, 1.0f)
}
internal fun stop(soundId: Int) {
if (!checkInit()) return
LogUtils.debug(TAG, "stop soundId=$soundId")
soundPool!!.stop(soundId)
}
internal fun resume(soundId: Int) {
if (!checkInit()) return
LogUtils.debug(TAG, "stop soundId=$soundId")
soundPool!!.resume(soundId)
}
internal fun pause(soundId: Int) {
if (!checkInit()) return
LogUtils.debug(TAG, "pause soundId=$soundId")
soundPool!!.pause(soundId)
}
}

View File

@@ -1,347 +0,0 @@
package com.opensource.svgaplayer
import android.graphics.Bitmap
import android.media.AudioAttributes
import android.media.AudioManager
import android.media.SoundPool
import android.os.Build
import com.opensource.svgaplayer.bitmap.SVGABitmapByteArrayDecoder
import com.opensource.svgaplayer.bitmap.SVGABitmapFileDecoder
import com.opensource.svgaplayer.entities.SVGAAudioEntity
import com.opensource.svgaplayer.entities.SVGAVideoSpriteEntity
import com.opensource.svgaplayer.proto.AudioEntity
import com.opensource.svgaplayer.proto.MovieEntity
import com.opensource.svgaplayer.proto.MovieParams
import com.opensource.svgaplayer.utils.SVGARect
import com.opensource.svgaplayer.utils.log.LogUtils
import org.json.JSONObject
import java.io.File
import java.io.FileInputStream
import java.io.FileOutputStream
import java.util.*
import kotlin.collections.ArrayList
/**
* Created by PonyCui on 16/6/18.
*/
class SVGAVideoEntity {
private val TAG = "SVGAVideoEntity"
var antiAlias = true
var movieItem: MovieEntity? = null
var videoSize = SVGARect(0.0, 0.0, 0.0, 0.0)
private set
var FPS = 15
private set
var frames: Int = 0
private set
internal var spriteList: List<SVGAVideoSpriteEntity> = emptyList()
internal var audioList: List<SVGAAudioEntity> = emptyList()
internal var soundPool: SoundPool? = null
private var soundCallback: SVGASoundManager.SVGASoundCallBack? = null
internal var imageMap = HashMap<String, Bitmap>()
private var mCacheDir: File
private var mFrameHeight = 0
private var mFrameWidth = 0
private var mPlayCallback: SVGAParser.PlayCallback?=null
private lateinit var mCallback: () -> Unit
constructor(json: JSONObject, cacheDir: File) : this(json, cacheDir, 0, 0)
constructor(json: JSONObject, cacheDir: File, frameWidth: Int, frameHeight: Int) {
mFrameWidth = frameWidth
mFrameHeight = frameHeight
mCacheDir = cacheDir
val movieJsonObject = json.optJSONObject("movie") ?: return
setupByJson(movieJsonObject)
try {
parserImages(json)
} catch (e: Exception) {
e.printStackTrace()
} catch (e: OutOfMemoryError) {
e.printStackTrace()
}
resetSprites(json)
}
private fun setupByJson(movieObject: JSONObject) {
movieObject.optJSONObject("viewBox")?.let { viewBoxObject ->
val width = viewBoxObject.optDouble("width", 0.0)
val height = viewBoxObject.optDouble("height", 0.0)
videoSize = SVGARect(0.0, 0.0, width, height)
}
FPS = movieObject.optInt("fps", 20)
frames = movieObject.optInt("frames", 0)
}
constructor(entity: MovieEntity, cacheDir: File) : this(entity, cacheDir, 0, 0)
constructor(entity: MovieEntity, cacheDir: File, frameWidth: Int, frameHeight: Int) {
this.mFrameWidth = frameWidth
this.mFrameHeight = frameHeight
this.mCacheDir = cacheDir
this.movieItem = entity
entity.params?.let(this::setupByMovie)
try {
parserImages(entity)
} catch (e: Exception) {
e.printStackTrace()
} catch (e: OutOfMemoryError) {
e.printStackTrace()
}
resetSprites(entity)
}
private fun setupByMovie(movieParams: MovieParams) {
val width = (movieParams.viewBoxWidth ?: 0.0f).toDouble()
val height = (movieParams.viewBoxHeight ?: 0.0f).toDouble()
videoSize = SVGARect(0.0, 0.0, width, height)
FPS = movieParams.fps ?: 20
frames = movieParams.frames ?: 0
}
internal fun prepare(callback: () -> Unit, playCallback: SVGAParser.PlayCallback?) {
mCallback = callback
mPlayCallback = playCallback
if (movieItem == null) {
mCallback()
} else {
setupAudios(movieItem!!) {
mCallback()
}
}
}
private fun parserImages(json: JSONObject) {
val imgJson = json.optJSONObject("images") ?: return
imgJson.keys().forEach { imgKey ->
val filePath = generateBitmapFilePath(imgJson[imgKey].toString(), imgKey)
if (filePath.isEmpty()) {
return
}
val bitmapKey = imgKey.replace(".matte", "")
val bitmap = createBitmap(filePath)
if (bitmap != null) {
imageMap[bitmapKey] = bitmap
}
}
}
private fun generateBitmapFilePath(imgName: String, imgKey: String): String {
val path = mCacheDir.absolutePath + "/" + imgName
val path1 = "$path.png"
val path2 = mCacheDir.absolutePath + "/" + imgKey + ".png"
return when {
File(path).exists() -> path
File(path1).exists() -> path1
File(path2).exists() -> path2
else -> ""
}
}
private fun createBitmap(filePath: String): Bitmap? {
return SVGABitmapFileDecoder.decodeBitmapFrom(filePath, mFrameWidth, mFrameHeight)
}
private fun parserImages(obj: MovieEntity) {
obj.images?.entries?.forEach { entry ->
val byteArray = entry.value.toByteArray()
if (byteArray.count() < 4) {
return@forEach
}
val fileTag = byteArray.slice(IntRange(0, 3))
if (fileTag[0].toInt() == 73 && fileTag[1].toInt() == 68 && fileTag[2].toInt() == 51) {
return@forEach
}
val filePath = generateBitmapFilePath(entry.value.utf8(), entry.key)
createBitmap(byteArray, filePath)?.let { bitmap ->
imageMap[entry.key] = bitmap
}
}
}
private fun createBitmap(byteArray: ByteArray, filePath: String): Bitmap? {
val bitmap = SVGABitmapByteArrayDecoder.decodeBitmapFrom(byteArray, mFrameWidth, mFrameHeight)
return bitmap ?: createBitmap(filePath)
}
private fun resetSprites(json: JSONObject) {
val mutableList: MutableList<SVGAVideoSpriteEntity> = mutableListOf()
json.optJSONArray("sprites")?.let { item ->
for (i in 0 until item.length()) {
item.optJSONObject(i)?.let { entryJson ->
mutableList.add(SVGAVideoSpriteEntity(entryJson))
}
}
}
spriteList = mutableList.toList()
}
private fun resetSprites(entity: MovieEntity) {
spriteList = entity.sprites?.map {
return@map SVGAVideoSpriteEntity(it)
} ?: listOf()
}
private fun setupAudios(entity: MovieEntity, completionBlock: () -> Unit) {
if (entity.audios == null || entity.audios.isEmpty()) {
run(completionBlock)
return
}
setupSoundPool(entity, completionBlock)
val audiosFileMap = generateAudioFileMap(entity)
//repair when audioEntity error can not callback
//如果audiosFileMap为空 soundPool?.load 不会走 导致 setOnLoadCompleteListener 不会回调 导致外层prepare不回调卡住
if (audiosFileMap.size == 0) {
run(completionBlock)
return
}
this.audioList = entity.audios.map { audio ->
return@map createSvgaAudioEntity(audio, audiosFileMap)
}
}
private fun createSvgaAudioEntity(audio: AudioEntity, audiosFileMap: HashMap<String, File>): SVGAAudioEntity {
val item = SVGAAudioEntity(audio)
val startTime = (audio.startTime ?: 0).toDouble()
val totalTime = (audio.totalTime ?: 0).toDouble()
if (totalTime.toInt() == 0) {
// 除数不能为 0
return item
}
// 直接回调文件,后续播放都不走
mPlayCallback?.let {
val fileList: MutableList<File> = ArrayList()
audiosFileMap.forEach { entity ->
fileList.add(entity.value)
}
it.onPlay(fileList)
mCallback()
return item
}
audiosFileMap[audio.audioKey]?.let { file ->
FileInputStream(file).use {
val length = it.available().toDouble()
val offset = ((startTime / totalTime) * length).toLong()
if (SVGASoundManager.isInit()) {
item.soundID = SVGASoundManager.load(soundCallback,
it.fd,
offset,
length.toLong(),
1)
} else {
item.soundID = soundPool?.load(it.fd, offset, length.toLong(), 1)
}
}
}
return item
}
private fun generateAudioFile(audioCache: File, value: ByteArray): File {
audioCache.createNewFile()
FileOutputStream(audioCache).write(value)
return audioCache
}
private fun generateAudioFileMap(entity: MovieEntity): HashMap<String, File> {
val audiosDataMap = generateAudioMap(entity)
val audiosFileMap = HashMap<String, File>()
if (audiosDataMap.count() > 0) {
audiosDataMap.forEach {
val audioCache = SVGACache.buildAudioFile(it.key)
audiosFileMap[it.key] =
audioCache.takeIf { file -> file.exists() } ?: generateAudioFile(
audioCache,
it.value
)
}
}
return audiosFileMap
}
private fun generateAudioMap(entity: MovieEntity): HashMap<String, ByteArray> {
val audiosDataMap = HashMap<String, ByteArray>()
entity.images?.entries?.forEach {
val imageKey = it.key
val byteArray = it.value.toByteArray()
if (byteArray.count() < 4) {
return@forEach
}
val fileTag = byteArray.slice(IntRange(0, 3))
if (fileTag[0].toInt() == 73 && fileTag[1].toInt() == 68 && fileTag[2].toInt() == 51) {
audiosDataMap[imageKey] = byteArray
}else if(fileTag[0].toInt() == -1 && fileTag[1].toInt() == -5 && fileTag[2].toInt() == -108){
audiosDataMap[imageKey] = byteArray
}
}
return audiosDataMap
}
private fun setupSoundPool(entity: MovieEntity, completionBlock: () -> Unit) {
var soundLoaded = 0
if (SVGASoundManager.isInit()) {
soundCallback = object : SVGASoundManager.SVGASoundCallBack {
override fun onVolumeChange(value: Float) {
SVGASoundManager.setVolume(value, this@SVGAVideoEntity)
}
override fun onComplete() {
soundLoaded++
if (soundLoaded >= entity.audios.count()) {
completionBlock()
}
}
}
return
}
soundPool = generateSoundPool(entity)
LogUtils.info("SVGAParser", "pool_start")
soundPool?.setOnLoadCompleteListener { _, _, _ ->
LogUtils.info("SVGAParser", "pool_complete")
soundLoaded++
if (soundLoaded >= entity.audios.count()) {
completionBlock()
}
}
}
private fun generateSoundPool(entity: MovieEntity): SoundPool? {
return try {
if (Build.VERSION.SDK_INT >= 21) {
val attributes = AudioAttributes.Builder()
.setUsage(AudioAttributes.USAGE_MEDIA)
.build()
SoundPool.Builder().setAudioAttributes(attributes)
.setMaxStreams(12.coerceAtMost(entity.audios.count()))
.build()
} else {
SoundPool(12.coerceAtMost(entity.audios.count()), AudioManager.STREAM_MUSIC, 0)
}
} catch (e: Exception) {
LogUtils.error(TAG, e)
null
}
}
fun clear() {
if (SVGASoundManager.isInit()) {
this.audioList.forEach {
it.soundID?.let { id -> SVGASoundManager.unload(id) }
}
soundCallback = null
}
soundPool?.release()
soundPool = null
audioList = emptyList()
spriteList = emptyList()
imageMap.clear()
}
}

View File

@@ -1,33 +0,0 @@
package com.opensource.svgaplayer.bitmap
import android.graphics.BitmapFactory
/**
*
* Create by im_dsd 2020/7/7 17:59
*/
internal object BitmapSampleSizeCalculator {
fun calculate(options: BitmapFactory.Options, reqWidth: Int, reqHeight: Int): Int {
// Raw height and width of image
val (height: Int, width: Int) = options.run { outHeight to outWidth }
var inSampleSize = 1
if (reqHeight <= 0 || reqWidth <= 0) {
return inSampleSize
}
if (height > reqHeight || width > reqWidth) {
val halfHeight: Int = height / 2
val halfWidth: Int = width / 2
// Calculate the largest inSampleSize value that is a power of 2 and keeps both
// height and width larger than the requested height and width.
while (halfHeight / inSampleSize >= reqHeight && halfWidth / inSampleSize >= reqWidth) {
inSampleSize *= 2
}
}
return inSampleSize
}
}

View File

@@ -1,16 +0,0 @@
package com.opensource.svgaplayer.bitmap
import android.graphics.Bitmap
import android.graphics.BitmapFactory
/**
* 通过字节码解码 Bitmap
*
* Create by im_dsd 2020/7/7 17:50
*/
internal object SVGABitmapByteArrayDecoder : SVGABitmapDecoder<ByteArray>() {
override fun onDecode(data: ByteArray, ops: BitmapFactory.Options): Bitmap? {
return BitmapFactory.decodeByteArray(data, 0, data.count(), ops)
}
}

View File

@@ -1,35 +0,0 @@
package com.opensource.svgaplayer.bitmap
import android.graphics.Bitmap
import android.graphics.BitmapFactory
/**
* Bitmap 解码器
*
* <T> 需要加载的数据类型
*
* Create by im_dsd 2020/7/7 17:39
*/
internal abstract class SVGABitmapDecoder<T> {
fun decodeBitmapFrom(data: T, reqWidth: Int, reqHeight: Int): Bitmap? {
return BitmapFactory.Options().run {
// 如果期望的宽高是合法的, 则开启检测尺寸模式
inJustDecodeBounds = (reqWidth > 0 && reqHeight > 0)
inPreferredConfig = Bitmap.Config.RGB_565
val bitmap = onDecode(data, this)
if (!inJustDecodeBounds) {
return bitmap
}
// Calculate inSampleSize
inSampleSize = BitmapSampleSizeCalculator.calculate(this, reqWidth, reqHeight)
// Decode bitmap with inSampleSize set
inJustDecodeBounds = false
onDecode(data, this)
}
}
abstract fun onDecode(data: T, ops: BitmapFactory.Options): Bitmap?
}

View File

@@ -1,16 +0,0 @@
package com.opensource.svgaplayer.bitmap
import android.graphics.Bitmap
import android.graphics.BitmapFactory
/**
* 通过文件解码 Bitmap
*
* Create by im_dsd 2020/7/7 17:50
*/
internal object SVGABitmapFileDecoder : SVGABitmapDecoder<String>() {
override fun onDecode(data: String, ops: BitmapFactory.Options): Bitmap? {
return BitmapFactory.decodeFile(data, ops)
}
}

View File

@@ -1,53 +0,0 @@
package com.opensource.svgaplayer.drawer
import android.graphics.Canvas
import android.widget.ImageView
import com.opensource.svgaplayer.SVGAVideoEntity
import com.opensource.svgaplayer.entities.SVGAVideoSpriteFrameEntity
import com.opensource.svgaplayer.utils.Pools
import com.opensource.svgaplayer.utils.SVGAScaleInfo
import kotlin.math.max
/**
* Created by cuiminghui on 2017/3/29.
*/
open internal class SGVADrawer(val videoItem: SVGAVideoEntity) {
val scaleInfo = SVGAScaleInfo()
private val spritePool = Pools.SimplePool<SVGADrawerSprite>(max(1, videoItem.spriteList.size))
inner class SVGADrawerSprite(var _matteKey: String? = null, var _imageKey: String? = null, var _frameEntity: SVGAVideoSpriteFrameEntity? = null) {
val matteKey get() = _matteKey
val imageKey get() = _imageKey
val frameEntity get() = _frameEntity!!
}
internal fun requestFrameSprites(frameIndex: Int): List<SVGADrawerSprite> {
return videoItem.spriteList.mapNotNull {
if (frameIndex >= 0 && frameIndex < it.frames.size) {
it.imageKey?.let { imageKey ->
if (!imageKey.endsWith(".matte") && it.frames[frameIndex].alpha <= 0.0) {
return@mapNotNull null
}
return@mapNotNull (spritePool.acquire() ?: SVGADrawerSprite()).apply {
_matteKey = it.matteKey
_imageKey = it.imageKey
_frameEntity = it.frames[frameIndex]
}
}
}
return@mapNotNull null
}
}
internal fun releaseFrameSprites(sprites: List<SVGADrawerSprite>) {
sprites.forEach { spritePool.release(it) }
}
open fun drawFrame(canvas : Canvas, frameIndex: Int, scaleType: ImageView.ScaleType) {
scaleInfo.performScaleType(canvas.width.toFloat(),canvas.height.toFloat(), videoItem.videoSize.width.toFloat(), videoItem.videoSize.height.toFloat(), scaleType)
}
}

View File

@@ -1,559 +0,0 @@
package com.opensource.svgaplayer.drawer
import android.graphics.*
import android.os.Build
import android.text.StaticLayout
import android.text.TextUtils
import android.widget.ImageView
import com.opensource.svgaplayer.SVGADynamicEntity
import com.opensource.svgaplayer.SVGASoundManager
import com.opensource.svgaplayer.SVGAVideoEntity
import com.opensource.svgaplayer.entities.SVGAVideoShapeEntity
/**
* Created by cuiminghui on 2017/3/29.
*/
internal class SVGACanvasDrawer(videoItem: SVGAVideoEntity, val dynamicItem: SVGADynamicEntity) : SGVADrawer(videoItem) {
private val sharedValues = ShareValues()
private val drawTextCache: HashMap<String, Bitmap> = hashMapOf()
private val pathCache = PathCache()
private var beginIndexList: Array<Boolean>? = null
private var endIndexList: Array<Boolean>? = null
override fun drawFrame(canvas: Canvas, frameIndex: Int, scaleType: ImageView.ScaleType) {
super.drawFrame(canvas, frameIndex, scaleType)
playAudio(frameIndex)
this.pathCache.onSizeChanged(canvas)
val sprites = requestFrameSprites(frameIndex)
// Filter null sprites
if (sprites.count() <= 0) return
val matteSprites = mutableMapOf<String, SVGADrawerSprite>()
var saveID = -1
beginIndexList = null
endIndexList = null
// Filter no matte layer
var hasMatteLayer = false
sprites.get(0).imageKey?.let {
if (it.endsWith(".matte")) {
hasMatteLayer = true
}
}
sprites.forEachIndexed { index, svgaDrawerSprite ->
// Save matte sprite
svgaDrawerSprite.imageKey?.let {
/// No matte layer included or VERSION Unsopport matte
if (!hasMatteLayer || Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
// Normal sprite
drawSprite(svgaDrawerSprite, canvas, frameIndex)
// Continue
return@forEachIndexed
}
/// Cache matte sprite
if (it.endsWith(".matte")) {
matteSprites.put(it, svgaDrawerSprite)
// Continue
return@forEachIndexed
}
}
/// Is matte begin
if (isMatteBegin(index, sprites)) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
saveID = canvas.saveLayer(0f, 0f, canvas.width.toFloat(), canvas.height.toFloat(), null)
} else {
canvas.save()
}
}
/// Normal matte
drawSprite(svgaDrawerSprite, canvas, frameIndex)
/// Is matte end
if (isMatteEnd(index, sprites)) {
matteSprites.get(svgaDrawerSprite.matteKey)?.let {
drawSprite(it, this.sharedValues.shareMatteCanvas(canvas.width, canvas.height), frameIndex)
canvas.drawBitmap(this.sharedValues.sharedMatteBitmap(), 0f, 0f, this.sharedValues.shareMattePaint())
if (saveID != -1) {
canvas.restoreToCount(saveID)
} else {
canvas.restore()
}
// Continue
return@forEachIndexed
}
}
}
releaseFrameSprites(sprites)
}
private fun isMatteBegin(spriteIndex: Int, sprites: List<SVGADrawerSprite>): Boolean {
if (beginIndexList == null) {
val boolArray = Array(sprites.count()) { false }
sprites.forEachIndexed { index, svgaDrawerSprite ->
svgaDrawerSprite.imageKey?.let {
/// Filter matte sprite
if (it.endsWith(".matte")) {
// Continue
return@forEachIndexed
}
}
svgaDrawerSprite.matteKey?.let {
if (it.length > 0) {
sprites.get(index - 1)?.let { lastSprite ->
if (lastSprite.matteKey.isNullOrEmpty()) {
boolArray[index] = true
} else {
if (lastSprite.matteKey != svgaDrawerSprite.matteKey) {
boolArray[index] = true
}
}
}
}
}
}
beginIndexList = boolArray
}
return beginIndexList?.get(spriteIndex) ?: false
}
private fun isMatteEnd(spriteIndex: Int, sprites: List<SVGADrawerSprite>): Boolean {
if (endIndexList == null) {
val boolArray = Array(sprites.count()) { false }
sprites.forEachIndexed { index, svgaDrawerSprite ->
svgaDrawerSprite.imageKey?.let {
/// Filter matte sprite
if (it.endsWith(".matte")) {
// Continue
return@forEachIndexed
}
}
svgaDrawerSprite.matteKey?.let {
if (it.length > 0) {
// Last one
if (index == sprites.count() - 1) {
boolArray[index] = true
} else {
sprites.get(index + 1)?.let { nextSprite ->
if (nextSprite.matteKey.isNullOrEmpty()) {
boolArray[index] = true
} else {
if (nextSprite.matteKey != svgaDrawerSprite.matteKey) {
boolArray[index] = true
}
}
}
}
}
}
}
endIndexList = boolArray
}
return endIndexList?.get(spriteIndex) ?: false
}
private fun playAudio(frameIndex: Int) {
this.videoItem.audioList.forEach { audio ->
if (audio.startFrame == frameIndex) {
if (SVGASoundManager.isInit()) {
audio.soundID?.let { soundID ->
audio.playID = SVGASoundManager.play(soundID)
}
} else {
this.videoItem.soundPool?.let { soundPool ->
audio.soundID?.let { soundID ->
audio.playID = soundPool.play(soundID, 1.0f, 1.0f, 1, 0, 1.0f)
}
}
}
}
if (audio.endFrame <= frameIndex) {
audio.playID?.let {
if (SVGASoundManager.isInit()) {
SVGASoundManager.stop(it)
} else {
this.videoItem.soundPool?.stop(it)
}
}
audio.playID = null
}
}
}
private fun shareFrameMatrix(transform: Matrix): Matrix {
val matrix = this.sharedValues.sharedMatrix()
matrix.postScale(scaleInfo.scaleFx, scaleInfo.scaleFy)
matrix.postTranslate(scaleInfo.tranFx, scaleInfo.tranFy)
matrix.preConcat(transform)
return matrix
}
private fun drawSprite(sprite: SVGADrawerSprite, canvas: Canvas, frameIndex: Int) {
drawImage(sprite, canvas)
drawShape(sprite, canvas)
drawDynamic(sprite, canvas, frameIndex)
}
private fun drawImage(sprite: SVGADrawerSprite, canvas: Canvas) {
val imageKey = sprite.imageKey ?: return
val isHidden = dynamicItem.dynamicHidden[imageKey] == true
if (isHidden) {
return
}
val bitmapKey = if (imageKey.endsWith(".matte")) imageKey.substring(0, imageKey.length - 6) else imageKey
val drawingBitmap = (dynamicItem.dynamicImage[bitmapKey] ?: videoItem.imageMap[bitmapKey])
?: return
val frameMatrix = shareFrameMatrix(sprite.frameEntity.transform)
val paint = this.sharedValues.sharedPaint()
paint.isAntiAlias = videoItem.antiAlias
paint.isFilterBitmap = videoItem.antiAlias
paint.alpha = (sprite.frameEntity.alpha * 255).toInt()
if (sprite.frameEntity.maskPath != null) {
val maskPath = sprite.frameEntity.maskPath ?: return
canvas.save()
val path = this.sharedValues.sharedPath()
maskPath.buildPath(path)
path.transform(frameMatrix)
canvas.clipPath(path)
frameMatrix.preScale((sprite.frameEntity.layout.width / drawingBitmap.width).toFloat(), (sprite.frameEntity.layout.height / drawingBitmap.height).toFloat())
if (!drawingBitmap.isRecycled) {
canvas.drawBitmap(drawingBitmap, frameMatrix, paint)
}
canvas.restore()
} else {
frameMatrix.preScale((sprite.frameEntity.layout.width / drawingBitmap.width).toFloat(), (sprite.frameEntity.layout.height / drawingBitmap.height).toFloat())
if (!drawingBitmap.isRecycled) {
canvas.drawBitmap(drawingBitmap, frameMatrix, paint)
}
}
dynamicItem.dynamicIClickArea.let {
it.get(imageKey)?.let { listener ->
val matrixArray = floatArrayOf(0f, 0f, 0f, 0f, 0f, 0f, 0f, 0f, 0f)
frameMatrix.getValues(matrixArray)
listener.onResponseArea(imageKey, matrixArray[2].toInt()
, matrixArray[5].toInt()
, (drawingBitmap.width * matrixArray[0] + matrixArray[2]).toInt()
, (drawingBitmap.height * matrixArray[4] + matrixArray[5]).toInt())
}
}
drawTextOnBitmap(canvas, drawingBitmap, sprite, frameMatrix)
}
private fun drawTextOnBitmap(canvas: Canvas, drawingBitmap: Bitmap, sprite: SVGADrawerSprite, frameMatrix: Matrix) {
if (dynamicItem.isTextDirty) {
this.drawTextCache.clear()
dynamicItem.isTextDirty = false
}
val imageKey = sprite.imageKey ?: return
var textBitmap: Bitmap? = null
dynamicItem.dynamicText[imageKey]?.let { drawingText ->
dynamicItem.dynamicTextPaint[imageKey]?.let { drawingTextPaint ->
drawTextCache[imageKey]?.let {
textBitmap = it
} ?: kotlin.run {
textBitmap = Bitmap.createBitmap(drawingBitmap.width, drawingBitmap.height, Bitmap.Config.ARGB_8888)
val drawRect = Rect(0, 0, drawingBitmap.width, drawingBitmap.height)
val textCanvas = Canvas(textBitmap)
drawingTextPaint.isAntiAlias = true
val fontMetrics = drawingTextPaint.getFontMetrics();
val top = fontMetrics.top
val bottom = fontMetrics.bottom
val baseLineY = drawRect.centerY() - top / 2 - bottom / 2
textCanvas.drawText(drawingText, drawRect.centerX().toFloat(), baseLineY, drawingTextPaint);
drawTextCache.put(imageKey, textBitmap as Bitmap)
}
}
}
dynamicItem.dynamicBoringLayoutText[imageKey]?.let {
drawTextCache[imageKey]?.let {
textBitmap = it
} ?: kotlin.run {
it.paint.isAntiAlias = true
textBitmap = Bitmap.createBitmap(drawingBitmap.width, drawingBitmap.height, Bitmap.Config.ARGB_8888)
val textCanvas = Canvas(textBitmap)
textCanvas.translate(0f, ((drawingBitmap.height - it.height) / 2).toFloat())
it.draw(textCanvas)
drawTextCache.put(imageKey, textBitmap as Bitmap)
}
}
dynamicItem.dynamicStaticLayoutText[imageKey]?.let {
drawTextCache[imageKey]?.let {
textBitmap = it
} ?: kotlin.run {
it.paint.isAntiAlias = true
var layout = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
var lineMax = try {
val field = StaticLayout::class.java.getDeclaredField("mMaximumVisibleLineCount")
field.isAccessible = true
field.getInt(it)
} catch (e: Exception) {
Int.MAX_VALUE
}
StaticLayout.Builder
.obtain(it.text, 0, it.text.length, it.paint, drawingBitmap.width)
.setAlignment(it.alignment)
.setMaxLines(lineMax)
.setEllipsize(TextUtils.TruncateAt.END)
.build()
} else {
StaticLayout(it.text, 0, it.text.length, it.paint, drawingBitmap.width, it.alignment, it.spacingMultiplier, it.spacingAdd, false)
}
textBitmap = Bitmap.createBitmap(drawingBitmap.width, drawingBitmap.height, Bitmap.Config.ARGB_8888)
val textCanvas = Canvas(textBitmap)
textCanvas.translate(0f, ((drawingBitmap.height - layout.height) / 2).toFloat())
layout.draw(textCanvas)
drawTextCache.put(imageKey, textBitmap as Bitmap)
}
}
textBitmap?.let { textBitmap ->
val paint = this.sharedValues.sharedPaint()
paint.isAntiAlias = videoItem.antiAlias
paint.alpha = (sprite.frameEntity.alpha * 255).toInt()
if (sprite.frameEntity.maskPath != null) {
val maskPath = sprite.frameEntity.maskPath ?: return@let
canvas.save()
canvas.concat(frameMatrix)
canvas.clipRect(0, 0, drawingBitmap.width, drawingBitmap.height)
val bitmapShader = BitmapShader(textBitmap, Shader.TileMode.REPEAT, Shader.TileMode.REPEAT)
paint.shader = bitmapShader
val path = this.sharedValues.sharedPath()
maskPath.buildPath(path)
canvas.drawPath(path, paint)
canvas.restore()
} else {
paint.isFilterBitmap = videoItem.antiAlias
canvas.drawBitmap(textBitmap, frameMatrix, paint)
}
}
}
private fun drawShape(sprite: SVGADrawerSprite, canvas: Canvas) {
val frameMatrix = shareFrameMatrix(sprite.frameEntity.transform)
sprite.frameEntity.shapes.forEach { shape ->
shape.buildPath()
shape.shapePath?.let {
val paint = this.sharedValues.sharedPaint()
paint.reset()
paint.isAntiAlias = videoItem.antiAlias
paint.alpha = (sprite.frameEntity.alpha * 255).toInt()
val path = this.sharedValues.sharedPath()
path.reset()
path.addPath(this.pathCache.buildPath(shape))
val shapeMatrix = this.sharedValues.sharedMatrix2()
shapeMatrix.reset()
shape.transform?.let {
shapeMatrix.postConcat(it)
}
shapeMatrix.postConcat(frameMatrix)
path.transform(shapeMatrix)
shape.styles?.fill?.let {
if (it != 0x00000000) {
paint.style = Paint.Style.FILL
paint.color = it
val alpha = Math.min(255, Math.max(0, (sprite.frameEntity.alpha * 255).toInt()))
if (alpha != 255) {
paint.alpha = alpha
}
if (sprite.frameEntity.maskPath !== null) canvas.save()
sprite.frameEntity.maskPath?.let { maskPath ->
val path2 = this.sharedValues.sharedPath2()
maskPath.buildPath(path2)
path2.transform(frameMatrix)
canvas.clipPath(path2)
}
canvas.drawPath(path, paint)
if (sprite.frameEntity.maskPath !== null) canvas.restore()
}
}
shape.styles?.strokeWidth?.let {
if (it > 0) {
paint.alpha = (sprite.frameEntity.alpha * 255).toInt()
paint.style = Paint.Style.STROKE
shape.styles?.stroke?.let {
paint.color = it
val alpha = Math.min(255, Math.max(0, (sprite.frameEntity.alpha * 255).toInt()))
if (alpha != 255) {
paint.alpha = alpha
}
}
val scale = matrixScale(frameMatrix)
shape.styles?.strokeWidth?.let {
paint.strokeWidth = it * scale
}
shape.styles?.lineCap?.let {
when {
it.equals("butt", true) -> paint.strokeCap = Paint.Cap.BUTT
it.equals("round", true) -> paint.strokeCap = Paint.Cap.ROUND
it.equals("square", true) -> paint.strokeCap = Paint.Cap.SQUARE
}
}
shape.styles?.lineJoin?.let {
when {
it.equals("miter", true) -> paint.strokeJoin = Paint.Join.MITER
it.equals("round", true) -> paint.strokeJoin = Paint.Join.ROUND
it.equals("bevel", true) -> paint.strokeJoin = Paint.Join.BEVEL
}
}
shape.styles?.miterLimit?.let {
paint.strokeMiter = it.toFloat() * scale
}
shape.styles?.lineDash?.let {
if (it.size == 3 && (it[0] > 0 || it[1] > 0)) {
paint.pathEffect = DashPathEffect(floatArrayOf(
(if (it[0] < 1.0f) 1.0f else it[0]) * scale,
(if (it[1] < 0.1f) 0.1f else it[1]) * scale
), it[2] * scale)
}
}
if (sprite.frameEntity.maskPath !== null) canvas.save()
sprite.frameEntity.maskPath?.let { maskPath ->
val path2 = this.sharedValues.sharedPath2()
maskPath.buildPath(path2)
path2.transform(frameMatrix)
canvas.clipPath(path2)
}
canvas.drawPath(path, paint)
if (sprite.frameEntity.maskPath !== null) canvas.restore()
}
}
}
}
}
private val matrixScaleTempValues = FloatArray(16)
private fun matrixScale(matrix: Matrix): Float {
matrix.getValues(matrixScaleTempValues)
if (matrixScaleTempValues[0] == 0f) {
return 0f
}
var A = matrixScaleTempValues[0].toDouble()
var B = matrixScaleTempValues[3].toDouble()
var C = matrixScaleTempValues[1].toDouble()
var D = matrixScaleTempValues[4].toDouble()
if (A * D == B * C) return 0f
var scaleX = Math.sqrt(A * A + B * B)
A /= scaleX
B /= scaleX
var skew = A * C + B * D
C -= A * skew
D -= B * skew
var scaleY = Math.sqrt(C * C + D * D)
C /= scaleY
D /= scaleY
skew /= scaleY
if (A * D < B * C) {
scaleX = -scaleX
}
return if (scaleInfo.ratioX) Math.abs(scaleX.toFloat()) else Math.abs(scaleY.toFloat())
}
private fun drawDynamic(sprite: SVGADrawerSprite, canvas: Canvas, frameIndex: Int) {
val imageKey = sprite.imageKey ?: return
dynamicItem.dynamicDrawer[imageKey]?.let {
val frameMatrix = shareFrameMatrix(sprite.frameEntity.transform)
canvas.save()
canvas.concat(frameMatrix)
it.invoke(canvas, frameIndex)
canvas.restore()
}
dynamicItem.dynamicDrawerSized[imageKey]?.let {
val frameMatrix = shareFrameMatrix(sprite.frameEntity.transform)
canvas.save()
canvas.concat(frameMatrix)
it.invoke(canvas, frameIndex, sprite.frameEntity.layout.width.toInt(), sprite.frameEntity.layout.height.toInt())
canvas.restore()
}
}
class ShareValues {
private val sharedPaint = Paint()
private val sharedPath = Path()
private val sharedPath2 = Path()
private val sharedMatrix = Matrix()
private val sharedMatrix2 = Matrix()
private val shareMattePaint = Paint()
private var shareMatteCanvas: Canvas? = null
private var sharedMatteBitmap: Bitmap? = null
fun sharedPaint(): Paint {
sharedPaint.reset()
return sharedPaint
}
fun sharedPath(): Path {
sharedPath.reset()
return sharedPath
}
fun sharedPath2(): Path {
sharedPath2.reset()
return sharedPath2
}
fun sharedMatrix(): Matrix {
sharedMatrix.reset()
return sharedMatrix
}
fun sharedMatrix2(): Matrix {
sharedMatrix2.reset()
return sharedMatrix2
}
fun shareMattePaint(): Paint {
shareMattePaint.setXfermode(PorterDuffXfermode(PorterDuff.Mode.DST_IN))
return shareMattePaint
}
fun sharedMatteBitmap(): Bitmap {
return sharedMatteBitmap as Bitmap
}
fun shareMatteCanvas(width: Int, height: Int): Canvas {
if (shareMatteCanvas == null) {
sharedMatteBitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ALPHA_8)
// shareMatteCanvas = Canvas(sharedMatteBitmap)
}
// val matteCanvas = shareMatteCanvas as Canvas
// matteCanvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR)
// return matteCanvas
return Canvas(sharedMatteBitmap)
}
}
class PathCache {
private var canvasWidth: Int = 0
private var canvasHeight: Int = 0
private val cache = HashMap<SVGAVideoShapeEntity, Path>()
fun onSizeChanged(canvas: Canvas) {
if (this.canvasWidth != canvas.width || this.canvasHeight != canvas.height) {
this.cache.clear()
}
this.canvasWidth = canvas.width
this.canvasHeight = canvas.height
}
fun buildPath(shape: SVGAVideoShapeEntity): Path {
if (!this.cache.containsKey(shape)) {
val path = Path()
path.set(shape.shapePath)
this.cache[shape] = path
}
return this.cache[shape]!!
}
}
}

View File

@@ -1,24 +0,0 @@
package com.opensource.svgaplayer.entities
import com.opensource.svgaplayer.proto.AudioEntity
import java.io.FileInputStream
internal class SVGAAudioEntity {
val audioKey: String?
val startFrame: Int
val endFrame: Int
val startTime: Int
val totalTime: Int
var soundID: Int? = null
var playID: Int? = null
constructor(audioItem: AudioEntity) {
this.audioKey = audioItem.audioKey
this.startFrame = audioItem.startFrame ?: 0
this.endFrame = audioItem.endFrame ?: 0
this.startTime = audioItem.startTime ?: 0
this.totalTime = audioItem.totalTime ?: 0
}
}

View File

@@ -1,100 +0,0 @@
package com.opensource.svgaplayer.entities
import android.graphics.Path
import com.opensource.svgaplayer.utils.SVGAPoint
import java.util.*
private val VALID_METHODS: Set<String> = setOf("M", "L", "H", "V", "C", "S", "Q", "R", "A", "Z", "m", "l", "h", "v", "c", "s", "q", "r", "a", "z")
class SVGAPathEntity(originValue: String) {
private val replacedValue: String = if (originValue.contains(",")) originValue.replace(",", " ") else originValue
private var cachedPath: Path? = null
fun buildPath(toPath: Path) {
cachedPath?.let {
toPath.set(it)
return
}
val cachedPath = Path()
val segments = StringTokenizer(this.replacedValue, "MLHVCSQRAZmlhvcsqraz", true)
var currentMethod = ""
while (segments.hasMoreTokens()) {
val segment = segments.nextToken()
if (segment.isEmpty()) { continue }
if (VALID_METHODS.contains(segment)) {
currentMethod = segment
if (currentMethod == "Z" || currentMethod == "z") { operate(cachedPath, currentMethod, StringTokenizer("", "")) }
}
else {
operate(cachedPath, currentMethod, StringTokenizer(segment, " "))
}
}
this.cachedPath = cachedPath
toPath.set(cachedPath)
}
private fun operate(finalPath: Path, method: String, args: StringTokenizer) {
var x0 = 0.0f
var y0 = 0.0f
var x1 = 0.0f
var y1 = 0.0f
var x2 = 0.0f
var y2 = 0.0f
try {
var index = 0
while (args.hasMoreTokens()) {
val s = args.nextToken()
if (s.isEmpty()) {continue}
if (index == 0) { x0 = s.toFloat() }
if (index == 1) { y0 = s.toFloat() }
if (index == 2) { x1 = s.toFloat() }
if (index == 3) { y1 = s.toFloat() }
if (index == 4) { x2 = s.toFloat() }
if (index == 5) { y2 = s.toFloat() }
index++
}
} catch (e: Exception) {}
var currentPoint = SVGAPoint(0.0f, 0.0f, 0.0f)
if (method == "M") {
finalPath.moveTo(x0, y0)
currentPoint = SVGAPoint(x0, y0, 0.0f)
} else if (method == "m") {
finalPath.rMoveTo(x0, y0)
currentPoint = SVGAPoint(currentPoint.x + x0, currentPoint.y + y0, 0.0f)
}
if (method == "L") {
finalPath.lineTo(x0, y0)
} else if (method == "l") {
finalPath.rLineTo(x0, y0)
}
if (method == "C") {
finalPath.cubicTo(x0, y0, x1, y1, x2, y2)
} else if (method == "c") {
finalPath.rCubicTo(x0, y0, x1, y1, x2, y2)
}
if (method == "Q") {
finalPath.quadTo(x0, y0, x1, y1)
} else if (method == "q") {
finalPath.rQuadTo(x0, y0, x1, y1)
}
if (method == "H") {
finalPath.lineTo(x0, currentPoint.y)
} else if (method == "h") {
finalPath.rLineTo(x0, 0f)
}
if (method == "V") {
finalPath.lineTo(currentPoint.x, x0)
} else if (method == "v") {
finalPath.rLineTo(0f, x0)
}
if (method == "Z") {
finalPath.close()
}
else if (method == "z") {
finalPath.close()
}
}
}

View File

@@ -1,356 +0,0 @@
package com.opensource.svgaplayer.entities
import android.graphics.Color
import android.graphics.Matrix
import android.graphics.Path
import android.graphics.RectF
import com.opensource.svgaplayer.proto.ShapeEntity
import org.json.JSONArray
import org.json.JSONObject
import java.util.*
/**
* Created by cuiminghui on 2017/2/22.
*/
val sharedPath = Path()
internal class SVGAVideoShapeEntity {
enum class Type {
shape,
rect,
ellipse,
keep
}
class Styles {
var fill = 0x00000000
internal set
var stroke = 0x00000000
internal set
var strokeWidth = 0.0f
internal set
var lineCap = "butt"
internal set
var lineJoin = "miter"
internal set
var miterLimit = 0
internal set
var lineDash = FloatArray(0)
internal set
}
var type = Type.shape
private set
var args: Map<String, Any>? = null
private set
var styles: Styles? = null
private set
var transform: Matrix? = null
private set
constructor(obj: JSONObject) {
parseType(obj)
parseArgs(obj)
parseStyles(obj)
parseTransform(obj)
}
constructor(obj: ShapeEntity) {
parseType(obj)
parseArgs(obj)
parseStyles(obj)
parseTransform(obj)
}
val isKeep: Boolean
get() = type == Type.keep
var shapePath: Path? = null
private fun parseType(obj: JSONObject) {
obj.optString("type")?.let {
when {
it.equals("shape", ignoreCase = true) -> type = Type.shape
it.equals("rect", ignoreCase = true) -> type = Type.rect
it.equals("ellipse", ignoreCase = true) -> type = Type.ellipse
it.equals("keep", ignoreCase = true) -> type = Type.keep
}
}
}
private fun parseType(obj: ShapeEntity) {
obj.type?.let {
type = when (it) {
ShapeEntity.ShapeType.SHAPE -> Type.shape
ShapeEntity.ShapeType.RECT -> Type.rect
ShapeEntity.ShapeType.ELLIPSE -> Type.ellipse
ShapeEntity.ShapeType.KEEP -> Type.keep
}
}
}
private fun parseArgs(obj: JSONObject) {
val args = HashMap<String, Any>()
obj.optJSONObject("args")?.let { values ->
values.keys().forEach { key ->
values.get(key)?.let {
args.put(key, it)
}
}
this.args = args
}
}
private fun parseArgs(obj: ShapeEntity) {
val args = HashMap<String, Any>()
obj.shape?.let {
it.d?.let { args.put("d", it) }
}
obj.ellipse?.let {
args.put("x", it.x ?: 0.0f)
args.put("y", it.y ?: 0.0f)
args.put("radiusX", it.radiusX ?: 0.0f)
args.put("radiusY", it.radiusY ?: 0.0f)
}
obj.rect?.let {
args.put("x", it.x ?: 0.0f)
args.put("y", it.y ?: 0.0f)
args.put("width", it.width ?: 0.0f)
args.put("height", it.height ?: 0.0f)
args.put("cornerRadius", it.cornerRadius ?: 0.0f)
}
this.args = args
}
// 检查色域范围是否是 [0f, 1f],或者是 [0f, 255f]
private fun checkValueRange(obj: JSONArray): Float {
return if (
obj.optDouble(0) <= 1 &&
obj.optDouble(1) <= 1 &&
obj.optDouble(2) <= 1
) {
255f
} else {
1f
}
}
// 检查 alpha 的范围是否是 [0f, 1f],或者是 [0f, 255f]
private fun checkAlphaValueRange(obj: JSONArray): Float {
return if (obj.optDouble(3) <= 1) {
255f
} else {
1f
}
}
private fun parseStyles(obj: JSONObject) {
obj.optJSONObject("styles")?.let {
val styles = Styles()
it.optJSONArray("fill")?.let {
if (it.length() == 4) {
val mulValue = checkValueRange(it)
val alphaRangeValue = checkAlphaValueRange(it)
styles.fill = Color.argb(
(it.optDouble(3) * alphaRangeValue).toInt(),
(it.optDouble(0) * mulValue).toInt(),
(it.optDouble(1) * mulValue).toInt(),
(it.optDouble(2) * mulValue).toInt()
)
}
}
it.optJSONArray("stroke")?.let {
if (it.length() == 4) {
val mulValue = checkValueRange(it)
val alphaRangeValue = checkAlphaValueRange(it)
styles.stroke = Color.argb(
(it.optDouble(3) * alphaRangeValue).toInt(),
(it.optDouble(0) * mulValue).toInt(),
(it.optDouble(1) * mulValue).toInt(),
(it.optDouble(2) * mulValue).toInt()
)
}
}
styles.strokeWidth = it.optDouble("strokeWidth", 0.0).toFloat()
styles.lineCap = it.optString("lineCap", "butt")
styles.lineJoin = it.optString("lineJoin", "miter")
styles.miterLimit = it.optInt("miterLimit", 0)
it.optJSONArray("lineDash")?.let {
styles.lineDash = FloatArray(it.length())
for (i in 0 until it.length()) {
styles.lineDash[i] = it.optDouble(i, 0.0).toFloat()
}
}
this.styles = styles
}
}
// 检查色域范围是否是 [0f, 1f],或者是 [0f, 255f]
private fun checkValueRange(color: ShapeEntity.ShapeStyle.RGBAColor): Float {
return if (
(color.r ?: 0f) <= 1 &&
(color.g ?: 0f) <= 1 &&
(color.b ?: 0f) <= 1
) {
255f
} else {
1f
}
}
// 检查 alpha 范围是否是 [0f, 1f],有可能是 [0f, 255f]
private fun checkAlphaValueRange(color: ShapeEntity.ShapeStyle.RGBAColor): Float {
return if (color.a <= 1f) {
255f
} else {
1f
}
}
private fun parseStyles(obj: ShapeEntity) {
obj.styles?.let {
val styles = Styles()
it.fill?.let {
val mulValue = checkValueRange(it)
val alphaRangeValue = checkAlphaValueRange(it)
styles.fill = Color.argb(
((it.a ?: 0f) * alphaRangeValue).toInt(),
((it.r ?: 0f) * mulValue).toInt(),
((it.g ?: 0f) * mulValue).toInt(),
((it.b ?: 0f) * mulValue).toInt()
)
}
it.stroke?.let {
val mulValue = checkValueRange(it)
val alphaRangeValue = checkAlphaValueRange(it)
styles.stroke = Color.argb(
((it.a ?: 0f) * alphaRangeValue).toInt(),
((it.r ?: 0f) * mulValue).toInt(),
((it.g ?: 0f) * mulValue).toInt(),
((it.b ?: 0f) * mulValue).toInt()
)
}
styles.strokeWidth = it.strokeWidth ?: 0.0f
it.lineCap?.let {
when (it) {
ShapeEntity.ShapeStyle.LineCap.LineCap_BUTT -> styles.lineCap = "butt"
ShapeEntity.ShapeStyle.LineCap.LineCap_ROUND -> styles.lineCap = "round"
ShapeEntity.ShapeStyle.LineCap.LineCap_SQUARE -> styles.lineCap = "square"
}
}
it.lineJoin?.let {
when (it) {
ShapeEntity.ShapeStyle.LineJoin.LineJoin_BEVEL -> styles.lineJoin = "bevel"
ShapeEntity.ShapeStyle.LineJoin.LineJoin_MITER -> styles.lineJoin = "miter"
ShapeEntity.ShapeStyle.LineJoin.LineJoin_ROUND -> styles.lineJoin = "round"
}
}
styles.miterLimit = (it.miterLimit ?: 0.0f).toInt()
styles.lineDash = kotlin.FloatArray(3)
it.lineDashI?.let { styles.lineDash[0] = it }
it.lineDashII?.let { styles.lineDash[1] = it }
it.lineDashIII?.let { styles.lineDash[2] = it }
this.styles = styles
}
}
private fun parseTransform(obj: JSONObject) {
obj.optJSONObject("transform")?.let {
val transform = Matrix()
val arr = FloatArray(9)
val a = it.optDouble("a", 1.0)
val b = it.optDouble("b", 0.0)
val c = it.optDouble("c", 0.0)
val d = it.optDouble("d", 1.0)
val tx = it.optDouble("tx", 0.0)
val ty = it.optDouble("ty", 0.0)
arr[0] = a.toFloat() // a
arr[1] = c.toFloat() // c
arr[2] = tx.toFloat() // tx
arr[3] = b.toFloat() // b
arr[4] = d.toFloat() // d
arr[5] = ty.toFloat() // ty
arr[6] = 0.0.toFloat()
arr[7] = 0.0.toFloat()
arr[8] = 1.0.toFloat()
transform.setValues(arr)
this.transform = transform
}
}
private fun parseTransform(obj: ShapeEntity) {
obj.transform?.let {
val transform = Matrix()
val arr = FloatArray(9)
val a = it.a ?: 1.0f
val b = it.b ?: 0.0f
val c = it.c ?: 0.0f
val d = it.d ?: 1.0f
val tx = it.tx ?: 0.0f
val ty = it.ty ?: 0.0f
arr[0] = a
arr[1] = c
arr[2] = tx
arr[3] = b
arr[4] = d
arr[5] = ty
arr[6] = 0.0f
arr[7] = 0.0f
arr[8] = 1.0f
transform.setValues(arr)
this.transform = transform
}
}
fun buildPath() {
if (this.shapePath != null) {
return
}
sharedPath.reset()
if (this.type == Type.shape) {
(this.args?.get("d") as? String)?.let {
SVGAPathEntity(it).buildPath(sharedPath)
}
} else if (this.type == Type.ellipse) {
val xv = this.args?.get("x") as? Number ?: return
val yv = this.args?.get("y") as? Number ?: return
val rxv = this.args?.get("radiusX") as? Number ?: return
val ryv = this.args?.get("radiusY") as? Number ?: return
val x = xv.toFloat()
val y = yv.toFloat()
val rx = rxv.toFloat()
val ry = ryv.toFloat()
sharedPath.addOval(RectF(x - rx, y - ry, x + rx, y + ry), Path.Direction.CW)
} else if (this.type == Type.rect) {
val xv = this.args?.get("x") as? Number ?: return
val yv = this.args?.get("y") as? Number ?: return
val wv = this.args?.get("width") as? Number ?: return
val hv = this.args?.get("height") as? Number ?: return
val crv = this.args?.get("cornerRadius") as? Number ?: return
val x = xv.toFloat()
val y = yv.toFloat()
val width = wv.toFloat()
val height = hv.toFloat()
val cornerRadius = crv.toFloat()
sharedPath.addRoundRect(RectF(x, y, x + width, y + height), cornerRadius, cornerRadius, Path.Direction.CW)
}
this.shapePath = Path()
this.shapePath?.set(sharedPath)
}
}

View File

@@ -1,60 +0,0 @@
package com.opensource.svgaplayer.entities
import com.opensource.svgaplayer.proto.SpriteEntity
import org.json.JSONObject
/**
* Created by cuiminghui on 2016/10/17.
*/
internal class SVGAVideoSpriteEntity {
val imageKey: String?
val matteKey: String?
val frames: List<SVGAVideoSpriteFrameEntity>
constructor(obj: JSONObject) {
this.imageKey = obj.optString("imageKey")
this.matteKey = obj.optString("matteKey")
val mutableFrames: MutableList<SVGAVideoSpriteFrameEntity> = mutableListOf()
obj.optJSONArray("frames")?.let {
for (i in 0 until it.length()) {
it.optJSONObject(i)?.let {
val frameItem = SVGAVideoSpriteFrameEntity(it)
if (frameItem.shapes.isNotEmpty()) {
frameItem.shapes.first().let {
if (it.isKeep && mutableFrames.size > 0) {
frameItem.shapes = mutableFrames.last().shapes
}
}
}
mutableFrames.add(frameItem)
}
}
}
frames = mutableFrames.toList()
}
constructor(obj: SpriteEntity) {
this.imageKey = obj.imageKey
this.matteKey = obj.matteKey
var lastFrame: SVGAVideoSpriteFrameEntity? = null
frames = obj.frames?.map {
val frameItem = SVGAVideoSpriteFrameEntity(it)
if (frameItem.shapes.isNotEmpty()) {
frameItem.shapes.first().let {
if (it.isKeep) {
lastFrame?.let {
frameItem.shapes = it.shapes
}
}
}
}
lastFrame = frameItem
return@map frameItem
} ?: listOf()
}
}

View File

@@ -1,94 +0,0 @@
package com.opensource.svgaplayer.entities
import android.graphics.Matrix
import com.opensource.svgaplayer.proto.FrameEntity
import com.opensource.svgaplayer.utils.SVGARect
import org.json.JSONObject
/**
* Created by cuiminghui on 2016/10/17.
*/
internal class SVGAVideoSpriteFrameEntity {
var alpha: Double
var layout = SVGARect(0.0, 0.0, 0.0, 0.0)
var transform = Matrix()
var maskPath: SVGAPathEntity? = null
var shapes: List<SVGAVideoShapeEntity> = listOf()
constructor(obj: JSONObject) {
this.alpha = obj.optDouble("alpha", 0.0)
obj.optJSONObject("layout")?.let {
layout = SVGARect(it.optDouble("x", 0.0), it.optDouble("y", 0.0), it.optDouble("width", 0.0), it.optDouble("height", 0.0))
}
obj.optJSONObject("transform")?.let {
val arr = FloatArray(9)
val a = it.optDouble("a", 1.0)
val b = it.optDouble("b", 0.0)
val c = it.optDouble("c", 0.0)
val d = it.optDouble("d", 1.0)
val tx = it.optDouble("tx", 0.0)
val ty = it.optDouble("ty", 0.0)
arr[0] = a.toFloat()
arr[1] = c.toFloat()
arr[2] = tx.toFloat()
arr[3] = b.toFloat()
arr[4] = d.toFloat()
arr[5] = ty.toFloat()
arr[6] = 0.0.toFloat()
arr[7] = 0.0.toFloat()
arr[8] = 1.0.toFloat()
transform.setValues(arr)
}
obj.optString("clipPath")?.let { d ->
if (d.isNotEmpty()) {
maskPath = SVGAPathEntity(d)
}
}
obj.optJSONArray("shapes")?.let {
val mutableList: MutableList<SVGAVideoShapeEntity> = mutableListOf()
for (i in 0 until it.length()) {
it.optJSONObject(i)?.let {
mutableList.add(SVGAVideoShapeEntity(it))
}
}
shapes = mutableList.toList()
}
}
constructor(obj: FrameEntity) {
this.alpha = (obj.alpha ?: 0.0f).toDouble()
obj.layout?.let {
this.layout = SVGARect((it.x ?: 0.0f).toDouble(), (it.y
?: 0.0f).toDouble(), (it.width ?: 0.0f).toDouble(), (it.height
?: 0.0f).toDouble())
}
obj.transform?.let {
val arr = FloatArray(9)
val a = it.a ?: 1.0f
val b = it.b ?: 0.0f
val c = it.c ?: 0.0f
val d = it.d ?: 1.0f
val tx = it.tx ?: 0.0f
val ty = it.ty ?: 0.0f
arr[0] = a
arr[1] = c
arr[2] = tx
arr[3] = b
arr[4] = d
arr[5] = ty
arr[6] = 0.0f
arr[7] = 0.0f
arr[8] = 1.0f
transform.setValues(arr)
}
obj.clipPath?.takeIf { it.isNotEmpty() }?.let {
maskPath = SVGAPathEntity(it)
}
this.shapes = obj.shapes.map {
return@map SVGAVideoShapeEntity(it)
}
}
}

View File

@@ -1,258 +0,0 @@
// Code generated by Wire protocol buffer compiler, do not edit.
// Source file: svga.proto at 19:1
package com.opensource.svgaplayer.proto;
import com.squareup.wire.FieldEncoding;
import com.squareup.wire.Message;
import com.squareup.wire.ProtoAdapter;
import com.squareup.wire.ProtoReader;
import com.squareup.wire.ProtoWriter;
import com.squareup.wire.WireField;
import com.squareup.wire.internal.Internal;
import java.io.IOException;
import java.lang.Integer;
import java.lang.Object;
import java.lang.Override;
import java.lang.String;
import java.lang.StringBuilder;
import okio.ByteString;
public final class AudioEntity extends Message<AudioEntity, AudioEntity.Builder> {
public static final ProtoAdapter<AudioEntity> ADAPTER = new ProtoAdapter_AudioEntity();
private static final long serialVersionUID = 0L;
public static final String DEFAULT_AUDIOKEY = "";
public static final Integer DEFAULT_STARTFRAME = 0;
public static final Integer DEFAULT_ENDFRAME = 0;
public static final Integer DEFAULT_STARTTIME = 0;
public static final Integer DEFAULT_TOTALTIME = 0;
/**
* 音频文件名
*/
@WireField(
tag = 1,
adapter = "com.squareup.wire.ProtoAdapter#STRING"
)
public final String audioKey;
/**
* 音频播放起始帧
*/
@WireField(
tag = 2,
adapter = "com.squareup.wire.ProtoAdapter#INT32"
)
public final Integer startFrame;
/**
* 音频播放结束帧
*/
@WireField(
tag = 3,
adapter = "com.squareup.wire.ProtoAdapter#INT32"
)
public final Integer endFrame;
/**
* 音频播放起始时间(相对音频长度)
*/
@WireField(
tag = 4,
adapter = "com.squareup.wire.ProtoAdapter#INT32"
)
public final Integer startTime;
/**
* 音频总长度
*/
@WireField(
tag = 5,
adapter = "com.squareup.wire.ProtoAdapter#INT32"
)
public final Integer totalTime;
public AudioEntity(String audioKey, Integer startFrame, Integer endFrame, Integer startTime, Integer totalTime) {
this(audioKey, startFrame, endFrame, startTime, totalTime, ByteString.EMPTY);
}
public AudioEntity(String audioKey, Integer startFrame, Integer endFrame, Integer startTime, Integer totalTime, ByteString unknownFields) {
super(ADAPTER, unknownFields);
this.audioKey = audioKey;
this.startFrame = startFrame;
this.endFrame = endFrame;
this.startTime = startTime;
this.totalTime = totalTime;
}
@Override
public Builder newBuilder() {
Builder builder = new Builder();
builder.audioKey = audioKey;
builder.startFrame = startFrame;
builder.endFrame = endFrame;
builder.startTime = startTime;
builder.totalTime = totalTime;
builder.addUnknownFields(unknownFields());
return builder;
}
@Override
public boolean equals(Object other) {
if (other == this) return true;
if (!(other instanceof AudioEntity)) return false;
AudioEntity o = (AudioEntity) other;
return unknownFields().equals(o.unknownFields())
&& Internal.equals(audioKey, o.audioKey)
&& Internal.equals(startFrame, o.startFrame)
&& Internal.equals(endFrame, o.endFrame)
&& Internal.equals(startTime, o.startTime)
&& Internal.equals(totalTime, o.totalTime);
}
@Override
public int hashCode() {
int result = super.hashCode;
if (result == 0) {
result = unknownFields().hashCode();
result = result * 37 + (audioKey != null ? audioKey.hashCode() : 0);
result = result * 37 + (startFrame != null ? startFrame.hashCode() : 0);
result = result * 37 + (endFrame != null ? endFrame.hashCode() : 0);
result = result * 37 + (startTime != null ? startTime.hashCode() : 0);
result = result * 37 + (totalTime != null ? totalTime.hashCode() : 0);
super.hashCode = result;
}
return result;
}
@Override
public String toString() {
StringBuilder builder = new StringBuilder();
if (audioKey != null) builder.append(", audioKey=").append(audioKey);
if (startFrame != null) builder.append(", startFrame=").append(startFrame);
if (endFrame != null) builder.append(", endFrame=").append(endFrame);
if (startTime != null) builder.append(", startTime=").append(startTime);
if (totalTime != null) builder.append(", totalTime=").append(totalTime);
return builder.replace(0, 2, "AudioEntity{").append('}').toString();
}
public static final class Builder extends Message.Builder<AudioEntity, Builder> {
public String audioKey;
public Integer startFrame;
public Integer endFrame;
public Integer startTime;
public Integer totalTime;
public Builder() {
}
/**
* 音频文件名
*/
public Builder audioKey(String audioKey) {
this.audioKey = audioKey;
return this;
}
/**
* 音频播放起始帧
*/
public Builder startFrame(Integer startFrame) {
this.startFrame = startFrame;
return this;
}
/**
* 音频播放结束帧
*/
public Builder endFrame(Integer endFrame) {
this.endFrame = endFrame;
return this;
}
/**
* 音频播放起始时间(相对音频长度)
*/
public Builder startTime(Integer startTime) {
this.startTime = startTime;
return this;
}
/**
* 音频总长度
*/
public Builder totalTime(Integer totalTime) {
this.totalTime = totalTime;
return this;
}
@Override
public AudioEntity build() {
return new AudioEntity(audioKey, startFrame, endFrame, startTime, totalTime, super.buildUnknownFields());
}
}
private static final class ProtoAdapter_AudioEntity extends ProtoAdapter<AudioEntity> {
ProtoAdapter_AudioEntity() {
super(FieldEncoding.LENGTH_DELIMITED, AudioEntity.class);
}
@Override
public int encodedSize(AudioEntity value) {
return (value.audioKey != null ? ProtoAdapter.STRING.encodedSizeWithTag(1, value.audioKey) : 0)
+ (value.startFrame != null ? ProtoAdapter.INT32.encodedSizeWithTag(2, value.startFrame) : 0)
+ (value.endFrame != null ? ProtoAdapter.INT32.encodedSizeWithTag(3, value.endFrame) : 0)
+ (value.startTime != null ? ProtoAdapter.INT32.encodedSizeWithTag(4, value.startTime) : 0)
+ (value.totalTime != null ? ProtoAdapter.INT32.encodedSizeWithTag(5, value.totalTime) : 0)
+ value.unknownFields().size();
}
@Override
public void encode(ProtoWriter writer, AudioEntity value) throws IOException {
if (value.audioKey != null) ProtoAdapter.STRING.encodeWithTag(writer, 1, value.audioKey);
if (value.startFrame != null) ProtoAdapter.INT32.encodeWithTag(writer, 2, value.startFrame);
if (value.endFrame != null) ProtoAdapter.INT32.encodeWithTag(writer, 3, value.endFrame);
if (value.startTime != null) ProtoAdapter.INT32.encodeWithTag(writer, 4, value.startTime);
if (value.totalTime != null) ProtoAdapter.INT32.encodeWithTag(writer, 5, value.totalTime);
writer.writeBytes(value.unknownFields());
}
@Override
public AudioEntity decode(ProtoReader reader) throws IOException {
Builder builder = new Builder();
long token = reader.beginMessage();
for (int tag; (tag = reader.nextTag()) != -1;) {
switch (tag) {
case 1: builder.audioKey(ProtoAdapter.STRING.decode(reader)); break;
case 2: builder.startFrame(ProtoAdapter.INT32.decode(reader)); break;
case 3: builder.endFrame(ProtoAdapter.INT32.decode(reader)); break;
case 4: builder.startTime(ProtoAdapter.INT32.decode(reader)); break;
case 5: builder.totalTime(ProtoAdapter.INT32.decode(reader)); break;
default: {
FieldEncoding fieldEncoding = reader.peekFieldEncoding();
Object value = fieldEncoding.rawProtoAdapter().decode(reader);
builder.addUnknownField(tag, fieldEncoding, value);
}
}
}
reader.endMessage(token);
return builder.build();
}
@Override
public AudioEntity redact(AudioEntity value) {
Builder builder = value.newBuilder();
builder.clearUnknownFields();
return builder.build();
}
}
}

View File

@@ -1,259 +0,0 @@
// Code generated by Wire protocol buffer compiler, do not edit.
// Source file: svga.proto at 115:1
package com.opensource.svgaplayer.proto;
import com.squareup.wire.FieldEncoding;
import com.squareup.wire.Message;
import com.squareup.wire.ProtoAdapter;
import com.squareup.wire.ProtoReader;
import com.squareup.wire.ProtoWriter;
import com.squareup.wire.WireField;
import com.squareup.wire.internal.Internal;
import java.io.IOException;
import java.lang.Float;
import java.lang.Object;
import java.lang.Override;
import java.lang.String;
import java.lang.StringBuilder;
import java.util.List;
import okio.ByteString;
public final class FrameEntity extends Message<FrameEntity, FrameEntity.Builder> {
public static final ProtoAdapter<FrameEntity> ADAPTER = new ProtoAdapter_FrameEntity();
private static final long serialVersionUID = 0L;
public static final Float DEFAULT_ALPHA = 0.0f;
public static final String DEFAULT_CLIPPATH = "";
/**
* 透明度
*/
@WireField(
tag = 1,
adapter = "com.squareup.wire.ProtoAdapter#FLOAT"
)
public final Float alpha;
/**
* 初始约束大小
*/
@WireField(
tag = 2,
adapter = "com.opensource.svgaplayer.proto.Layout#ADAPTER"
)
public final Layout layout;
/**
* 2D 变换矩阵
*/
@WireField(
tag = 3,
adapter = "com.opensource.svgaplayer.proto.Transform#ADAPTER"
)
public final Transform transform;
/**
* 遮罩路径,使用 SVG 标准 Path 绘制图案进行 Mask 遮罩。
*/
@WireField(
tag = 4,
adapter = "com.squareup.wire.ProtoAdapter#STRING"
)
public final String clipPath;
/**
* 矢量元素列表
*/
@WireField(
tag = 5,
adapter = "com.opensource.svgaplayer.proto.ShapeEntity#ADAPTER",
label = WireField.Label.REPEATED
)
public final List<ShapeEntity> shapes;
public FrameEntity(Float alpha, Layout layout, Transform transform, String clipPath, List<ShapeEntity> shapes) {
this(alpha, layout, transform, clipPath, shapes, ByteString.EMPTY);
}
public FrameEntity(Float alpha, Layout layout, Transform transform, String clipPath, List<ShapeEntity> shapes, ByteString unknownFields) {
super(ADAPTER, unknownFields);
this.alpha = alpha;
this.layout = layout;
this.transform = transform;
this.clipPath = clipPath;
this.shapes = Internal.immutableCopyOf("shapes", shapes);
}
@Override
public Builder newBuilder() {
Builder builder = new Builder();
builder.alpha = alpha;
builder.layout = layout;
builder.transform = transform;
builder.clipPath = clipPath;
builder.shapes = Internal.copyOf("shapes", shapes);
builder.addUnknownFields(unknownFields());
return builder;
}
@Override
public boolean equals(Object other) {
if (other == this) return true;
if (!(other instanceof FrameEntity)) return false;
FrameEntity o = (FrameEntity) other;
return unknownFields().equals(o.unknownFields())
&& Internal.equals(alpha, o.alpha)
&& Internal.equals(layout, o.layout)
&& Internal.equals(transform, o.transform)
&& Internal.equals(clipPath, o.clipPath)
&& shapes.equals(o.shapes);
}
@Override
public int hashCode() {
int result = super.hashCode;
if (result == 0) {
result = unknownFields().hashCode();
result = result * 37 + (alpha != null ? alpha.hashCode() : 0);
result = result * 37 + (layout != null ? layout.hashCode() : 0);
result = result * 37 + (transform != null ? transform.hashCode() : 0);
result = result * 37 + (clipPath != null ? clipPath.hashCode() : 0);
result = result * 37 + shapes.hashCode();
super.hashCode = result;
}
return result;
}
@Override
public String toString() {
StringBuilder builder = new StringBuilder();
if (alpha != null) builder.append(", alpha=").append(alpha);
if (layout != null) builder.append(", layout=").append(layout);
if (transform != null) builder.append(", transform=").append(transform);
if (clipPath != null) builder.append(", clipPath=").append(clipPath);
if (!shapes.isEmpty()) builder.append(", shapes=").append(shapes);
return builder.replace(0, 2, "FrameEntity{").append('}').toString();
}
public static final class Builder extends Message.Builder<FrameEntity, Builder> {
public Float alpha;
public Layout layout;
public Transform transform;
public String clipPath;
public List<ShapeEntity> shapes;
public Builder() {
shapes = Internal.newMutableList();
}
/**
* 透明度
*/
public Builder alpha(Float alpha) {
this.alpha = alpha;
return this;
}
/**
* 初始约束大小
*/
public Builder layout(Layout layout) {
this.layout = layout;
return this;
}
/**
* 2D 变换矩阵
*/
public Builder transform(Transform transform) {
this.transform = transform;
return this;
}
/**
* 遮罩路径,使用 SVG 标准 Path 绘制图案进行 Mask 遮罩。
*/
public Builder clipPath(String clipPath) {
this.clipPath = clipPath;
return this;
}
/**
* 矢量元素列表
*/
public Builder shapes(List<ShapeEntity> shapes) {
Internal.checkElementsNotNull(shapes);
this.shapes = shapes;
return this;
}
@Override
public FrameEntity build() {
return new FrameEntity(alpha, layout, transform, clipPath, shapes, super.buildUnknownFields());
}
}
private static final class ProtoAdapter_FrameEntity extends ProtoAdapter<FrameEntity> {
ProtoAdapter_FrameEntity() {
super(FieldEncoding.LENGTH_DELIMITED, FrameEntity.class);
}
@Override
public int encodedSize(FrameEntity value) {
return (value.alpha != null ? ProtoAdapter.FLOAT.encodedSizeWithTag(1, value.alpha) : 0)
+ (value.layout != null ? Layout.ADAPTER.encodedSizeWithTag(2, value.layout) : 0)
+ (value.transform != null ? Transform.ADAPTER.encodedSizeWithTag(3, value.transform) : 0)
+ (value.clipPath != null ? ProtoAdapter.STRING.encodedSizeWithTag(4, value.clipPath) : 0)
+ ShapeEntity.ADAPTER.asRepeated().encodedSizeWithTag(5, value.shapes)
+ value.unknownFields().size();
}
@Override
public void encode(ProtoWriter writer, FrameEntity value) throws IOException {
if (value.alpha != null) ProtoAdapter.FLOAT.encodeWithTag(writer, 1, value.alpha);
if (value.layout != null) Layout.ADAPTER.encodeWithTag(writer, 2, value.layout);
if (value.transform != null) Transform.ADAPTER.encodeWithTag(writer, 3, value.transform);
if (value.clipPath != null) ProtoAdapter.STRING.encodeWithTag(writer, 4, value.clipPath);
ShapeEntity.ADAPTER.asRepeated().encodeWithTag(writer, 5, value.shapes);
writer.writeBytes(value.unknownFields());
}
@Override
public FrameEntity decode(ProtoReader reader) throws IOException {
Builder builder = new Builder();
long token = reader.beginMessage();
for (int tag; (tag = reader.nextTag()) != -1;) {
switch (tag) {
case 1: builder.alpha(ProtoAdapter.FLOAT.decode(reader)); break;
case 2: builder.layout(Layout.ADAPTER.decode(reader)); break;
case 3: builder.transform(Transform.ADAPTER.decode(reader)); break;
case 4: builder.clipPath(ProtoAdapter.STRING.decode(reader)); break;
case 5: builder.shapes.add(ShapeEntity.ADAPTER.decode(reader)); break;
default: {
FieldEncoding fieldEncoding = reader.peekFieldEncoding();
Object value = fieldEncoding.rawProtoAdapter().decode(reader);
builder.addUnknownField(tag, fieldEncoding, value);
}
}
}
reader.endMessage(token);
return builder.build();
}
@Override
public FrameEntity redact(FrameEntity value) {
Builder builder = value.newBuilder();
if (builder.layout != null) builder.layout = Layout.ADAPTER.redact(builder.layout);
if (builder.transform != null) builder.transform = Transform.ADAPTER.redact(builder.transform);
Internal.redactElements(builder.shapes, ShapeEntity.ADAPTER);
builder.clearUnknownFields();
return builder.build();
}
}
}

View File

@@ -1,205 +0,0 @@
// Code generated by Wire protocol buffer compiler, do not edit.
// Source file: svga.proto at 27:1
package com.opensource.svgaplayer.proto;
import com.squareup.wire.FieldEncoding;
import com.squareup.wire.Message;
import com.squareup.wire.ProtoAdapter;
import com.squareup.wire.ProtoReader;
import com.squareup.wire.ProtoWriter;
import com.squareup.wire.WireField;
import com.squareup.wire.internal.Internal;
import java.io.IOException;
import java.lang.Float;
import java.lang.Object;
import java.lang.Override;
import java.lang.String;
import java.lang.StringBuilder;
import okio.ByteString;
public final class Layout extends Message<Layout, Layout.Builder> {
public static final ProtoAdapter<Layout> ADAPTER = new ProtoAdapter_Layout();
private static final long serialVersionUID = 0L;
public static final Float DEFAULT_X = 0.0f;
public static final Float DEFAULT_Y = 0.0f;
public static final Float DEFAULT_WIDTH = 0.0f;
public static final Float DEFAULT_HEIGHT = 0.0f;
@WireField(
tag = 1,
adapter = "com.squareup.wire.ProtoAdapter#FLOAT"
)
public final Float x;
@WireField(
tag = 2,
adapter = "com.squareup.wire.ProtoAdapter#FLOAT"
)
public final Float y;
@WireField(
tag = 3,
adapter = "com.squareup.wire.ProtoAdapter#FLOAT"
)
public final Float width;
@WireField(
tag = 4,
adapter = "com.squareup.wire.ProtoAdapter#FLOAT"
)
public final Float height;
public Layout(Float x, Float y, Float width, Float height) {
this(x, y, width, height, ByteString.EMPTY);
}
public Layout(Float x, Float y, Float width, Float height, ByteString unknownFields) {
super(ADAPTER, unknownFields);
this.x = x;
this.y = y;
this.width = width;
this.height = height;
}
@Override
public Builder newBuilder() {
Builder builder = new Builder();
builder.x = x;
builder.y = y;
builder.width = width;
builder.height = height;
builder.addUnknownFields(unknownFields());
return builder;
}
@Override
public boolean equals(Object other) {
if (other == this) return true;
if (!(other instanceof Layout)) return false;
Layout o = (Layout) other;
return unknownFields().equals(o.unknownFields())
&& Internal.equals(x, o.x)
&& Internal.equals(y, o.y)
&& Internal.equals(width, o.width)
&& Internal.equals(height, o.height);
}
@Override
public int hashCode() {
int result = super.hashCode;
if (result == 0) {
result = unknownFields().hashCode();
result = result * 37 + (x != null ? x.hashCode() : 0);
result = result * 37 + (y != null ? y.hashCode() : 0);
result = result * 37 + (width != null ? width.hashCode() : 0);
result = result * 37 + (height != null ? height.hashCode() : 0);
super.hashCode = result;
}
return result;
}
@Override
public String toString() {
StringBuilder builder = new StringBuilder();
if (x != null) builder.append(", x=").append(x);
if (y != null) builder.append(", y=").append(y);
if (width != null) builder.append(", width=").append(width);
if (height != null) builder.append(", height=").append(height);
return builder.replace(0, 2, "Layout{").append('}').toString();
}
public static final class Builder extends Message.Builder<Layout, Builder> {
public Float x;
public Float y;
public Float width;
public Float height;
public Builder() {
}
public Builder x(Float x) {
this.x = x;
return this;
}
public Builder y(Float y) {
this.y = y;
return this;
}
public Builder width(Float width) {
this.width = width;
return this;
}
public Builder height(Float height) {
this.height = height;
return this;
}
@Override
public Layout build() {
return new Layout(x, y, width, height, super.buildUnknownFields());
}
}
private static final class ProtoAdapter_Layout extends ProtoAdapter<Layout> {
ProtoAdapter_Layout() {
super(FieldEncoding.LENGTH_DELIMITED, Layout.class);
}
@Override
public int encodedSize(Layout value) {
return (value.x != null ? ProtoAdapter.FLOAT.encodedSizeWithTag(1, value.x) : 0)
+ (value.y != null ? ProtoAdapter.FLOAT.encodedSizeWithTag(2, value.y) : 0)
+ (value.width != null ? ProtoAdapter.FLOAT.encodedSizeWithTag(3, value.width) : 0)
+ (value.height != null ? ProtoAdapter.FLOAT.encodedSizeWithTag(4, value.height) : 0)
+ value.unknownFields().size();
}
@Override
public void encode(ProtoWriter writer, Layout value) throws IOException {
if (value.x != null) ProtoAdapter.FLOAT.encodeWithTag(writer, 1, value.x);
if (value.y != null) ProtoAdapter.FLOAT.encodeWithTag(writer, 2, value.y);
if (value.width != null) ProtoAdapter.FLOAT.encodeWithTag(writer, 3, value.width);
if (value.height != null) ProtoAdapter.FLOAT.encodeWithTag(writer, 4, value.height);
writer.writeBytes(value.unknownFields());
}
@Override
public Layout decode(ProtoReader reader) throws IOException {
Builder builder = new Builder();
long token = reader.beginMessage();
for (int tag; (tag = reader.nextTag()) != -1;) {
switch (tag) {
case 1: builder.x(ProtoAdapter.FLOAT.decode(reader)); break;
case 2: builder.y(ProtoAdapter.FLOAT.decode(reader)); break;
case 3: builder.width(ProtoAdapter.FLOAT.decode(reader)); break;
case 4: builder.height(ProtoAdapter.FLOAT.decode(reader)); break;
default: {
FieldEncoding fieldEncoding = reader.peekFieldEncoding();
Object value = fieldEncoding.rawProtoAdapter().decode(reader);
builder.addUnknownField(tag, fieldEncoding, value);
}
}
}
reader.endMessage(token);
return builder.build();
}
@Override
public Layout redact(Layout value) {
Builder builder = value.newBuilder();
builder.clearUnknownFields();
return builder.build();
}
}
}

View File

@@ -1,265 +0,0 @@
// Code generated by Wire protocol buffer compiler, do not edit.
// Source file: svga.proto at 123:1
package com.opensource.svgaplayer.proto;
import com.squareup.wire.FieldEncoding;
import com.squareup.wire.Message;
import com.squareup.wire.ProtoAdapter;
import com.squareup.wire.ProtoReader;
import com.squareup.wire.ProtoWriter;
import com.squareup.wire.WireField;
import com.squareup.wire.internal.Internal;
import java.io.IOException;
import java.lang.Object;
import java.lang.Override;
import java.lang.String;
import java.lang.StringBuilder;
import java.util.List;
import java.util.Map;
import okio.ByteString;
public final class MovieEntity extends Message<MovieEntity, MovieEntity.Builder> {
public static final ProtoAdapter<MovieEntity> ADAPTER = new ProtoAdapter_MovieEntity();
private static final long serialVersionUID = 0L;
public static final String DEFAULT_VERSION = "";
/**
* SVGA 格式版本号
*/
@WireField(
tag = 1,
adapter = "com.squareup.wire.ProtoAdapter#STRING"
)
public final String version;
/**
* 动画参数
*/
@WireField(
tag = 2,
adapter = "com.opensource.svgaplayer.proto.MovieParams#ADAPTER"
)
public final MovieParams params;
/**
* Key 是位图键名Value 是位图文件名或二进制 PNG 数据。
*/
@WireField(
tag = 3,
keyAdapter = "com.squareup.wire.ProtoAdapter#STRING",
adapter = "com.squareup.wire.ProtoAdapter#BYTES"
)
public final Map<String, ByteString> images;
/**
* 元素列表
*/
@WireField(
tag = 4,
adapter = "com.opensource.svgaplayer.proto.SpriteEntity#ADAPTER",
label = WireField.Label.REPEATED
)
public final List<SpriteEntity> sprites;
/**
* 音频列表
*/
@WireField(
tag = 5,
adapter = "com.opensource.svgaplayer.proto.AudioEntity#ADAPTER",
label = WireField.Label.REPEATED
)
public final List<AudioEntity> audios;
public MovieEntity(String version, MovieParams params, Map<String, ByteString> images, List<SpriteEntity> sprites, List<AudioEntity> audios) {
this(version, params, images, sprites, audios, ByteString.EMPTY);
}
public MovieEntity(String version, MovieParams params, Map<String, ByteString> images, List<SpriteEntity> sprites, List<AudioEntity> audios, ByteString unknownFields) {
super(ADAPTER, unknownFields);
this.version = version;
this.params = params;
this.images = Internal.immutableCopyOf("images", images);
this.sprites = Internal.immutableCopyOf("sprites", sprites);
this.audios = Internal.immutableCopyOf("audios", audios);
}
@Override
public Builder newBuilder() {
Builder builder = new Builder();
builder.version = version;
builder.params = params;
builder.images = Internal.copyOf("images", images);
builder.sprites = Internal.copyOf("sprites", sprites);
builder.audios = Internal.copyOf("audios", audios);
builder.addUnknownFields(unknownFields());
return builder;
}
@Override
public boolean equals(Object other) {
if (other == this) return true;
if (!(other instanceof MovieEntity)) return false;
MovieEntity o = (MovieEntity) other;
return unknownFields().equals(o.unknownFields())
&& Internal.equals(version, o.version)
&& Internal.equals(params, o.params)
&& images.equals(o.images)
&& sprites.equals(o.sprites)
&& audios.equals(o.audios);
}
@Override
public int hashCode() {
int result = super.hashCode;
if (result == 0) {
result = unknownFields().hashCode();
result = result * 37 + (version != null ? version.hashCode() : 0);
result = result * 37 + (params != null ? params.hashCode() : 0);
result = result * 37 + images.hashCode();
result = result * 37 + sprites.hashCode();
result = result * 37 + audios.hashCode();
super.hashCode = result;
}
return result;
}
@Override
public String toString() {
StringBuilder builder = new StringBuilder();
if (version != null) builder.append(", version=").append(version);
if (params != null) builder.append(", params=").append(params);
if (!images.isEmpty()) builder.append(", images=").append(images);
if (!sprites.isEmpty()) builder.append(", sprites=").append(sprites);
if (!audios.isEmpty()) builder.append(", audios=").append(audios);
return builder.replace(0, 2, "MovieEntity{").append('}').toString();
}
public static final class Builder extends Message.Builder<MovieEntity, Builder> {
public String version;
public MovieParams params;
public Map<String, ByteString> images;
public List<SpriteEntity> sprites;
public List<AudioEntity> audios;
public Builder() {
images = Internal.newMutableMap();
sprites = Internal.newMutableList();
audios = Internal.newMutableList();
}
/**
* SVGA 格式版本号
*/
public Builder version(String version) {
this.version = version;
return this;
}
/**
* 动画参数
*/
public Builder params(MovieParams params) {
this.params = params;
return this;
}
/**
* Key 是位图键名Value 是位图文件名或二进制 PNG 数据。
*/
public Builder images(Map<String, ByteString> images) {
Internal.checkElementsNotNull(images);
this.images = images;
return this;
}
/**
* 元素列表
*/
public Builder sprites(List<SpriteEntity> sprites) {
Internal.checkElementsNotNull(sprites);
this.sprites = sprites;
return this;
}
/**
* 音频列表
*/
public Builder audios(List<AudioEntity> audios) {
Internal.checkElementsNotNull(audios);
this.audios = audios;
return this;
}
@Override
public MovieEntity build() {
return new MovieEntity(version, params, images, sprites, audios, super.buildUnknownFields());
}
}
private static final class ProtoAdapter_MovieEntity extends ProtoAdapter<MovieEntity> {
private final ProtoAdapter<Map<String, ByteString>> images = ProtoAdapter.newMapAdapter(ProtoAdapter.STRING, ProtoAdapter.BYTES);
ProtoAdapter_MovieEntity() {
super(FieldEncoding.LENGTH_DELIMITED, MovieEntity.class);
}
@Override
public int encodedSize(MovieEntity value) {
return (value.version != null ? ProtoAdapter.STRING.encodedSizeWithTag(1, value.version) : 0)
+ (value.params != null ? MovieParams.ADAPTER.encodedSizeWithTag(2, value.params) : 0)
+ images.encodedSizeWithTag(3, value.images)
+ SpriteEntity.ADAPTER.asRepeated().encodedSizeWithTag(4, value.sprites)
+ AudioEntity.ADAPTER.asRepeated().encodedSizeWithTag(5, value.audios)
+ value.unknownFields().size();
}
@Override
public void encode(ProtoWriter writer, MovieEntity value) throws IOException {
if (value.version != null) ProtoAdapter.STRING.encodeWithTag(writer, 1, value.version);
if (value.params != null) MovieParams.ADAPTER.encodeWithTag(writer, 2, value.params);
images.encodeWithTag(writer, 3, value.images);
SpriteEntity.ADAPTER.asRepeated().encodeWithTag(writer, 4, value.sprites);
AudioEntity.ADAPTER.asRepeated().encodeWithTag(writer, 5, value.audios);
writer.writeBytes(value.unknownFields());
}
@Override
public MovieEntity decode(ProtoReader reader) throws IOException {
Builder builder = new Builder();
long token = reader.beginMessage();
for (int tag; (tag = reader.nextTag()) != -1;) {
switch (tag) {
case 1: builder.version(ProtoAdapter.STRING.decode(reader)); break;
case 2: builder.params(MovieParams.ADAPTER.decode(reader)); break;
case 3: builder.images.putAll(images.decode(reader)); break;
case 4: builder.sprites.add(SpriteEntity.ADAPTER.decode(reader)); break;
case 5: builder.audios.add(AudioEntity.ADAPTER.decode(reader)); break;
default: {
FieldEncoding fieldEncoding = reader.peekFieldEncoding();
Object value = fieldEncoding.rawProtoAdapter().decode(reader);
builder.addUnknownField(tag, fieldEncoding, value);
}
}
}
reader.endMessage(token);
return builder.build();
}
@Override
public MovieEntity redact(MovieEntity value) {
Builder builder = value.newBuilder();
if (builder.params != null) builder.params = MovieParams.ADAPTER.redact(builder.params);
Internal.redactElements(builder.sprites, SpriteEntity.ADAPTER);
Internal.redactElements(builder.audios, AudioEntity.ADAPTER);
builder.clearUnknownFields();
return builder.build();
}
}
}

View File

@@ -1,230 +0,0 @@
// Code generated by Wire protocol buffer compiler, do not edit.
// Source file: svga.proto at 6:1
package com.opensource.svgaplayer.proto;
import com.squareup.wire.FieldEncoding;
import com.squareup.wire.Message;
import com.squareup.wire.ProtoAdapter;
import com.squareup.wire.ProtoReader;
import com.squareup.wire.ProtoWriter;
import com.squareup.wire.WireField;
import com.squareup.wire.internal.Internal;
import java.io.IOException;
import java.lang.Float;
import java.lang.Integer;
import java.lang.Object;
import java.lang.Override;
import java.lang.String;
import java.lang.StringBuilder;
import okio.ByteString;
public final class MovieParams extends Message<MovieParams, MovieParams.Builder> {
public static final ProtoAdapter<MovieParams> ADAPTER = new ProtoAdapter_MovieParams();
private static final long serialVersionUID = 0L;
public static final Float DEFAULT_VIEWBOXWIDTH = 0.0f;
public static final Float DEFAULT_VIEWBOXHEIGHT = 0.0f;
public static final Integer DEFAULT_FPS = 0;
public static final Integer DEFAULT_FRAMES = 0;
/**
* 画布宽
*/
@WireField(
tag = 1,
adapter = "com.squareup.wire.ProtoAdapter#FLOAT"
)
public final Float viewBoxWidth;
/**
* 画布高
*/
@WireField(
tag = 2,
adapter = "com.squareup.wire.ProtoAdapter#FLOAT"
)
public final Float viewBoxHeight;
/**
* 动画每秒播放帧数,合法值是 [1, 2, 3, 5, 6, 10, 12, 15, 20, 30, 60] 中的任意一个。
*/
@WireField(
tag = 3,
adapter = "com.squareup.wire.ProtoAdapter#INT32"
)
public final Integer fps;
/**
* 动画总帧数
*/
@WireField(
tag = 4,
adapter = "com.squareup.wire.ProtoAdapter#INT32"
)
public final Integer frames;
public MovieParams(Float viewBoxWidth, Float viewBoxHeight, Integer fps, Integer frames) {
this(viewBoxWidth, viewBoxHeight, fps, frames, ByteString.EMPTY);
}
public MovieParams(Float viewBoxWidth, Float viewBoxHeight, Integer fps, Integer frames, ByteString unknownFields) {
super(ADAPTER, unknownFields);
this.viewBoxWidth = viewBoxWidth;
this.viewBoxHeight = viewBoxHeight;
this.fps = fps;
this.frames = frames;
}
@Override
public Builder newBuilder() {
Builder builder = new Builder();
builder.viewBoxWidth = viewBoxWidth;
builder.viewBoxHeight = viewBoxHeight;
builder.fps = fps;
builder.frames = frames;
builder.addUnknownFields(unknownFields());
return builder;
}
@Override
public boolean equals(Object other) {
if (other == this) return true;
if (!(other instanceof MovieParams)) return false;
MovieParams o = (MovieParams) other;
return unknownFields().equals(o.unknownFields())
&& Internal.equals(viewBoxWidth, o.viewBoxWidth)
&& Internal.equals(viewBoxHeight, o.viewBoxHeight)
&& Internal.equals(fps, o.fps)
&& Internal.equals(frames, o.frames);
}
@Override
public int hashCode() {
int result = super.hashCode;
if (result == 0) {
result = unknownFields().hashCode();
result = result * 37 + (viewBoxWidth != null ? viewBoxWidth.hashCode() : 0);
result = result * 37 + (viewBoxHeight != null ? viewBoxHeight.hashCode() : 0);
result = result * 37 + (fps != null ? fps.hashCode() : 0);
result = result * 37 + (frames != null ? frames.hashCode() : 0);
super.hashCode = result;
}
return result;
}
@Override
public String toString() {
StringBuilder builder = new StringBuilder();
if (viewBoxWidth != null) builder.append(", viewBoxWidth=").append(viewBoxWidth);
if (viewBoxHeight != null) builder.append(", viewBoxHeight=").append(viewBoxHeight);
if (fps != null) builder.append(", fps=").append(fps);
if (frames != null) builder.append(", frames=").append(frames);
return builder.replace(0, 2, "MovieParams{").append('}').toString();
}
public static final class Builder extends Message.Builder<MovieParams, Builder> {
public Float viewBoxWidth;
public Float viewBoxHeight;
public Integer fps;
public Integer frames;
public Builder() {
}
/**
* 画布宽
*/
public Builder viewBoxWidth(Float viewBoxWidth) {
this.viewBoxWidth = viewBoxWidth;
return this;
}
/**
* 画布高
*/
public Builder viewBoxHeight(Float viewBoxHeight) {
this.viewBoxHeight = viewBoxHeight;
return this;
}
/**
* 动画每秒播放帧数,合法值是 [1, 2, 3, 5, 6, 10, 12, 15, 20, 30, 60] 中的任意一个。
*/
public Builder fps(Integer fps) {
this.fps = fps;
return this;
}
/**
* 动画总帧数
*/
public Builder frames(Integer frames) {
this.frames = frames;
return this;
}
@Override
public MovieParams build() {
return new MovieParams(viewBoxWidth, viewBoxHeight, fps, frames, super.buildUnknownFields());
}
}
private static final class ProtoAdapter_MovieParams extends ProtoAdapter<MovieParams> {
ProtoAdapter_MovieParams() {
super(FieldEncoding.LENGTH_DELIMITED, MovieParams.class);
}
@Override
public int encodedSize(MovieParams value) {
return (value.viewBoxWidth != null ? ProtoAdapter.FLOAT.encodedSizeWithTag(1, value.viewBoxWidth) : 0)
+ (value.viewBoxHeight != null ? ProtoAdapter.FLOAT.encodedSizeWithTag(2, value.viewBoxHeight) : 0)
+ (value.fps != null ? ProtoAdapter.INT32.encodedSizeWithTag(3, value.fps) : 0)
+ (value.frames != null ? ProtoAdapter.INT32.encodedSizeWithTag(4, value.frames) : 0)
+ value.unknownFields().size();
}
@Override
public void encode(ProtoWriter writer, MovieParams value) throws IOException {
if (value.viewBoxWidth != null) ProtoAdapter.FLOAT.encodeWithTag(writer, 1, value.viewBoxWidth);
if (value.viewBoxHeight != null) ProtoAdapter.FLOAT.encodeWithTag(writer, 2, value.viewBoxHeight);
if (value.fps != null) ProtoAdapter.INT32.encodeWithTag(writer, 3, value.fps);
if (value.frames != null) ProtoAdapter.INT32.encodeWithTag(writer, 4, value.frames);
writer.writeBytes(value.unknownFields());
}
@Override
public MovieParams decode(ProtoReader reader) throws IOException {
Builder builder = new Builder();
long token = reader.beginMessage();
for (int tag; (tag = reader.nextTag()) != -1;) {
switch (tag) {
case 1: builder.viewBoxWidth(ProtoAdapter.FLOAT.decode(reader)); break;
case 2: builder.viewBoxHeight(ProtoAdapter.FLOAT.decode(reader)); break;
case 3: builder.fps(ProtoAdapter.INT32.decode(reader)); break;
case 4: builder.frames(ProtoAdapter.INT32.decode(reader)); break;
default: {
FieldEncoding fieldEncoding = reader.peekFieldEncoding();
Object value = fieldEncoding.rawProtoAdapter().decode(reader);
builder.addUnknownField(tag, fieldEncoding, value);
}
}
}
reader.endMessage(token);
return builder.build();
}
@Override
public MovieParams redact(MovieParams value) {
Builder builder = value.newBuilder();
builder.clearUnknownFields();
return builder.build();
}
}
}

View File

@@ -1,202 +0,0 @@
// Code generated by Wire protocol buffer compiler, do not edit.
// Source file: svga.proto at 13:1
package com.opensource.svgaplayer.proto;
import com.squareup.wire.FieldEncoding;
import com.squareup.wire.Message;
import com.squareup.wire.ProtoAdapter;
import com.squareup.wire.ProtoReader;
import com.squareup.wire.ProtoWriter;
import com.squareup.wire.WireField;
import com.squareup.wire.internal.Internal;
import java.io.IOException;
import java.lang.Object;
import java.lang.Override;
import java.lang.String;
import java.lang.StringBuilder;
import java.util.List;
import okio.ByteString;
public final class SpriteEntity extends Message<SpriteEntity, SpriteEntity.Builder> {
public static final ProtoAdapter<SpriteEntity> ADAPTER = new ProtoAdapter_SpriteEntity();
private static final long serialVersionUID = 0L;
public static final String DEFAULT_IMAGEKEY = "";
public static final String DEFAULT_MATTEKEY = "";
/**
* 元件所对应的位图键名, 如果 imageKey 含有 .vector 后缀,该 sprite 为矢量图层 含有 .matte 后缀,该 sprite 为遮罩图层。
*/
@WireField(
tag = 1,
adapter = "com.squareup.wire.ProtoAdapter#STRING"
)
public final String imageKey;
/**
* 帧列表
*/
@WireField(
tag = 2,
adapter = "com.opensource.svgaplayer.proto.FrameEntity#ADAPTER",
label = WireField.Label.REPEATED
)
public final List<FrameEntity> frames;
/**
* 被遮罩图层的 matteKey 对应的是其遮罩图层的 imageKey.
*/
@WireField(
tag = 3,
adapter = "com.squareup.wire.ProtoAdapter#STRING"
)
public final String matteKey;
public SpriteEntity(String imageKey, List<FrameEntity> frames, String matteKey) {
this(imageKey, frames, matteKey, ByteString.EMPTY);
}
public SpriteEntity(String imageKey, List<FrameEntity> frames, String matteKey, ByteString unknownFields) {
super(ADAPTER, unknownFields);
this.imageKey = imageKey;
this.frames = Internal.immutableCopyOf("frames", frames);
this.matteKey = matteKey;
}
@Override
public Builder newBuilder() {
Builder builder = new Builder();
builder.imageKey = imageKey;
builder.frames = Internal.copyOf("frames", frames);
builder.matteKey = matteKey;
builder.addUnknownFields(unknownFields());
return builder;
}
@Override
public boolean equals(Object other) {
if (other == this) return true;
if (!(other instanceof SpriteEntity)) return false;
SpriteEntity o = (SpriteEntity) other;
return unknownFields().equals(o.unknownFields())
&& Internal.equals(imageKey, o.imageKey)
&& frames.equals(o.frames)
&& Internal.equals(matteKey, o.matteKey);
}
@Override
public int hashCode() {
int result = super.hashCode;
if (result == 0) {
result = unknownFields().hashCode();
result = result * 37 + (imageKey != null ? imageKey.hashCode() : 0);
result = result * 37 + frames.hashCode();
result = result * 37 + (matteKey != null ? matteKey.hashCode() : 0);
super.hashCode = result;
}
return result;
}
@Override
public String toString() {
StringBuilder builder = new StringBuilder();
if (imageKey != null) builder.append(", imageKey=").append(imageKey);
if (!frames.isEmpty()) builder.append(", frames=").append(frames);
if (matteKey != null) builder.append(", matteKey=").append(matteKey);
return builder.replace(0, 2, "SpriteEntity{").append('}').toString();
}
public static final class Builder extends Message.Builder<SpriteEntity, Builder> {
public String imageKey;
public List<FrameEntity> frames;
public String matteKey;
public Builder() {
frames = Internal.newMutableList();
}
/**
* 元件所对应的位图键名, 如果 imageKey 含有 .vector 后缀,该 sprite 为矢量图层 含有 .matte 后缀,该 sprite 为遮罩图层。
*/
public Builder imageKey(String imageKey) {
this.imageKey = imageKey;
return this;
}
/**
* 帧列表
*/
public Builder frames(List<FrameEntity> frames) {
Internal.checkElementsNotNull(frames);
this.frames = frames;
return this;
}
/**
* 被遮罩图层的 matteKey 对应的是其遮罩图层的 imageKey.
*/
public Builder matteKey(String matteKey) {
this.matteKey = matteKey;
return this;
}
@Override
public SpriteEntity build() {
return new SpriteEntity(imageKey, frames, matteKey, super.buildUnknownFields());
}
}
private static final class ProtoAdapter_SpriteEntity extends ProtoAdapter<SpriteEntity> {
ProtoAdapter_SpriteEntity() {
super(FieldEncoding.LENGTH_DELIMITED, SpriteEntity.class);
}
@Override
public int encodedSize(SpriteEntity value) {
return (value.imageKey != null ? ProtoAdapter.STRING.encodedSizeWithTag(1, value.imageKey) : 0)
+ FrameEntity.ADAPTER.asRepeated().encodedSizeWithTag(2, value.frames)
+ (value.matteKey != null ? ProtoAdapter.STRING.encodedSizeWithTag(3, value.matteKey) : 0)
+ value.unknownFields().size();
}
@Override
public void encode(ProtoWriter writer, SpriteEntity value) throws IOException {
if (value.imageKey != null) ProtoAdapter.STRING.encodeWithTag(writer, 1, value.imageKey);
FrameEntity.ADAPTER.asRepeated().encodeWithTag(writer, 2, value.frames);
if (value.matteKey != null) ProtoAdapter.STRING.encodeWithTag(writer, 3, value.matteKey);
writer.writeBytes(value.unknownFields());
}
@Override
public SpriteEntity decode(ProtoReader reader) throws IOException {
Builder builder = new Builder();
long token = reader.beginMessage();
for (int tag; (tag = reader.nextTag()) != -1;) {
switch (tag) {
case 1: builder.imageKey(ProtoAdapter.STRING.decode(reader)); break;
case 2: builder.frames.add(FrameEntity.ADAPTER.decode(reader)); break;
case 3: builder.matteKey(ProtoAdapter.STRING.decode(reader)); break;
default: {
FieldEncoding fieldEncoding = reader.peekFieldEncoding();
Object value = fieldEncoding.rawProtoAdapter().decode(reader);
builder.addUnknownField(tag, fieldEncoding, value);
}
}
}
reader.endMessage(token);
return builder.build();
}
@Override
public SpriteEntity redact(SpriteEntity value) {
Builder builder = value.newBuilder();
Internal.redactElements(builder.frames, FrameEntity.ADAPTER);
builder.clearUnknownFields();
return builder.build();
}
}
}

View File

@@ -1,251 +0,0 @@
// Code generated by Wire protocol buffer compiler, do not edit.
// Source file: svga.proto at 34:1
package com.opensource.svgaplayer.proto;
import com.squareup.wire.FieldEncoding;
import com.squareup.wire.Message;
import com.squareup.wire.ProtoAdapter;
import com.squareup.wire.ProtoReader;
import com.squareup.wire.ProtoWriter;
import com.squareup.wire.WireField;
import com.squareup.wire.internal.Internal;
import java.io.IOException;
import java.lang.Float;
import java.lang.Object;
import java.lang.Override;
import java.lang.String;
import java.lang.StringBuilder;
import okio.ByteString;
public final class Transform extends Message<Transform, Transform.Builder> {
public static final ProtoAdapter<Transform> ADAPTER = new ProtoAdapter_Transform();
private static final long serialVersionUID = 0L;
public static final Float DEFAULT_A = 0.0f;
public static final Float DEFAULT_B = 0.0f;
public static final Float DEFAULT_C = 0.0f;
public static final Float DEFAULT_D = 0.0f;
public static final Float DEFAULT_TX = 0.0f;
public static final Float DEFAULT_TY = 0.0f;
@WireField(
tag = 1,
adapter = "com.squareup.wire.ProtoAdapter#FLOAT"
)
public final Float a;
@WireField(
tag = 2,
adapter = "com.squareup.wire.ProtoAdapter#FLOAT"
)
public final Float b;
@WireField(
tag = 3,
adapter = "com.squareup.wire.ProtoAdapter#FLOAT"
)
public final Float c;
@WireField(
tag = 4,
adapter = "com.squareup.wire.ProtoAdapter#FLOAT"
)
public final Float d;
@WireField(
tag = 5,
adapter = "com.squareup.wire.ProtoAdapter#FLOAT"
)
public final Float tx;
@WireField(
tag = 6,
adapter = "com.squareup.wire.ProtoAdapter#FLOAT"
)
public final Float ty;
public Transform(Float a, Float b, Float c, Float d, Float tx, Float ty) {
this(a, b, c, d, tx, ty, ByteString.EMPTY);
}
public Transform(Float a, Float b, Float c, Float d, Float tx, Float ty, ByteString unknownFields) {
super(ADAPTER, unknownFields);
this.a = a;
this.b = b;
this.c = c;
this.d = d;
this.tx = tx;
this.ty = ty;
}
@Override
public Builder newBuilder() {
Builder builder = new Builder();
builder.a = a;
builder.b = b;
builder.c = c;
builder.d = d;
builder.tx = tx;
builder.ty = ty;
builder.addUnknownFields(unknownFields());
return builder;
}
@Override
public boolean equals(Object other) {
if (other == this) return true;
if (!(other instanceof Transform)) return false;
Transform o = (Transform) other;
return unknownFields().equals(o.unknownFields())
&& Internal.equals(a, o.a)
&& Internal.equals(b, o.b)
&& Internal.equals(c, o.c)
&& Internal.equals(d, o.d)
&& Internal.equals(tx, o.tx)
&& Internal.equals(ty, o.ty);
}
@Override
public int hashCode() {
int result = super.hashCode;
if (result == 0) {
result = unknownFields().hashCode();
result = result * 37 + (a != null ? a.hashCode() : 0);
result = result * 37 + (b != null ? b.hashCode() : 0);
result = result * 37 + (c != null ? c.hashCode() : 0);
result = result * 37 + (d != null ? d.hashCode() : 0);
result = result * 37 + (tx != null ? tx.hashCode() : 0);
result = result * 37 + (ty != null ? ty.hashCode() : 0);
super.hashCode = result;
}
return result;
}
@Override
public String toString() {
StringBuilder builder = new StringBuilder();
if (a != null) builder.append(", a=").append(a);
if (b != null) builder.append(", b=").append(b);
if (c != null) builder.append(", c=").append(c);
if (d != null) builder.append(", d=").append(d);
if (tx != null) builder.append(", tx=").append(tx);
if (ty != null) builder.append(", ty=").append(ty);
return builder.replace(0, 2, "Transform{").append('}').toString();
}
public static final class Builder extends Message.Builder<Transform, Builder> {
public Float a;
public Float b;
public Float c;
public Float d;
public Float tx;
public Float ty;
public Builder() {
}
public Builder a(Float a) {
this.a = a;
return this;
}
public Builder b(Float b) {
this.b = b;
return this;
}
public Builder c(Float c) {
this.c = c;
return this;
}
public Builder d(Float d) {
this.d = d;
return this;
}
public Builder tx(Float tx) {
this.tx = tx;
return this;
}
public Builder ty(Float ty) {
this.ty = ty;
return this;
}
@Override
public Transform build() {
return new Transform(a, b, c, d, tx, ty, super.buildUnknownFields());
}
}
private static final class ProtoAdapter_Transform extends ProtoAdapter<Transform> {
ProtoAdapter_Transform() {
super(FieldEncoding.LENGTH_DELIMITED, Transform.class);
}
@Override
public int encodedSize(Transform value) {
return (value.a != null ? ProtoAdapter.FLOAT.encodedSizeWithTag(1, value.a) : 0)
+ (value.b != null ? ProtoAdapter.FLOAT.encodedSizeWithTag(2, value.b) : 0)
+ (value.c != null ? ProtoAdapter.FLOAT.encodedSizeWithTag(3, value.c) : 0)
+ (value.d != null ? ProtoAdapter.FLOAT.encodedSizeWithTag(4, value.d) : 0)
+ (value.tx != null ? ProtoAdapter.FLOAT.encodedSizeWithTag(5, value.tx) : 0)
+ (value.ty != null ? ProtoAdapter.FLOAT.encodedSizeWithTag(6, value.ty) : 0)
+ value.unknownFields().size();
}
@Override
public void encode(ProtoWriter writer, Transform value) throws IOException {
if (value.a != null) ProtoAdapter.FLOAT.encodeWithTag(writer, 1, value.a);
if (value.b != null) ProtoAdapter.FLOAT.encodeWithTag(writer, 2, value.b);
if (value.c != null) ProtoAdapter.FLOAT.encodeWithTag(writer, 3, value.c);
if (value.d != null) ProtoAdapter.FLOAT.encodeWithTag(writer, 4, value.d);
if (value.tx != null) ProtoAdapter.FLOAT.encodeWithTag(writer, 5, value.tx);
if (value.ty != null) ProtoAdapter.FLOAT.encodeWithTag(writer, 6, value.ty);
writer.writeBytes(value.unknownFields());
}
@Override
public Transform decode(ProtoReader reader) throws IOException {
Builder builder = new Builder();
long token = reader.beginMessage();
for (int tag; (tag = reader.nextTag()) != -1;) {
switch (tag) {
case 1: builder.a(ProtoAdapter.FLOAT.decode(reader)); break;
case 2: builder.b(ProtoAdapter.FLOAT.decode(reader)); break;
case 3: builder.c(ProtoAdapter.FLOAT.decode(reader)); break;
case 4: builder.d(ProtoAdapter.FLOAT.decode(reader)); break;
case 5: builder.tx(ProtoAdapter.FLOAT.decode(reader)); break;
case 6: builder.ty(ProtoAdapter.FLOAT.decode(reader)); break;
default: {
FieldEncoding fieldEncoding = reader.peekFieldEncoding();
Object value = fieldEncoding.rawProtoAdapter().decode(reader);
builder.addUnknownField(tag, fieldEncoding, value);
}
}
}
reader.endMessage(token);
return builder.build();
}
@Override
public Transform redact(Transform value) {
Builder builder = value.newBuilder();
builder.clearUnknownFields();
return builder.build();
}
}
}

View File

@@ -1,102 +0,0 @@
package com.opensource.svgaplayer.utils
/**
* Helper class for creating pools of objects. An example use looks like this:
* <pre>
* public class MyPooledClass {
*
* private static final SynchronizedPool<MyPooledClass> sPool =
* new SynchronizedPool<MyPooledClass>(10);
*
* public static MyPooledClass obtain() {
* MyPooledClass instance = sPool.acquire();
* return (instance != null) ? instance : new MyPooledClass();
* }
*
* public void recycle() {
* // Clear state if needed.
* sPool.release(this);
* }
*
* . . .
* }
* </pre>
*
*/
class Pools private constructor() {
/**
* Interface for managing a pool of objects.
*
* @param <T> The pooled type.
*/
interface Pool<T> {
/**
* @return An instance from the pool if such, null otherwise.
*/
fun acquire(): T?
/**
* Release an instance to the pool.
*
* @param instance The instance to release.
* @return Whether the instance was put in the pool.
*
* @throws IllegalStateException If the instance is already in the pool.
*/
fun release(instance: T): Boolean
}
/**
* Simple (non-synchronized) pool of objects.
*
* @param maxPoolSize The max pool size.
*
* @throws IllegalArgumentException If the max pool size is less than zero.
*
* @param <T> The pooled type.
*/
open class SimplePool<T>(maxPoolSize: Int) : Pool<T> {
private val mPool: Array<Any?>
private var mPoolSize = 0
init {
require(maxPoolSize > 0) { "The max pool size must be > 0" }
mPool = arrayOfNulls(maxPoolSize)
}
@Suppress("UNCHECKED_CAST")
override fun acquire(): T? {
if (mPoolSize > 0) {
val lastPooledIndex = mPoolSize - 1
val instance = mPool[lastPooledIndex] as T?
mPool[lastPooledIndex] = null
mPoolSize--
return instance
}
return null
}
override fun release(instance: T): Boolean {
check(!isInPool(instance)) { "Already in the pool!" }
if (mPoolSize < mPool.size) {
mPool[mPoolSize] = instance
mPoolSize++
return true
}
return false
}
private fun isInPool(instance: T): Boolean {
for (i in 0 until mPoolSize) {
if (mPool[i] === instance) {
return true
}
}
return false
}
}
}

View File

@@ -1,146 +0,0 @@
package com.opensource.svgaplayer.utils
import android.widget.ImageView
/**
* Created by ubt on 2018/1/19.
*/
class SVGAScaleInfo {
var tranFx : Float = 0.0f
var tranFy : Float = 0.0f
var scaleFx : Float = 1.0f
var scaleFy : Float = 1.0f
var ratio = 1.0f
var ratioX = false
private fun resetVar(){
tranFx = 0.0f
tranFy = 0.0f
scaleFx = 1.0f
scaleFy = 1.0f
ratio = 1.0f
ratioX = false
}
fun performScaleType(canvasWidth : Float, canvasHeight: Float, videoWidth : Float, videoHeight : Float, scaleType: ImageView.ScaleType) {
if (canvasWidth == 0.0f || canvasHeight == 0.0f || videoWidth == 0.0f || videoHeight == 0.0f) {
return
}
resetVar()
val canW_vidW_f = (canvasWidth - videoWidth) / 2.0f
val canH_vidH_f = (canvasHeight - videoHeight) / 2.0f
val videoRatio = videoWidth / videoHeight
val canvasRatio = canvasWidth / canvasHeight
val canH_d_vidH = canvasHeight / videoHeight
val canW_d_vidW = canvasWidth / videoWidth
when (scaleType) {
ImageView.ScaleType.CENTER -> {
tranFx = canW_vidW_f
tranFy = canH_vidH_f
}
ImageView.ScaleType.CENTER_CROP -> {
if (videoRatio > canvasRatio) {
ratio = canH_d_vidH
ratioX = false
scaleFx = canH_d_vidH
scaleFy = canH_d_vidH
tranFx = (canvasWidth - videoWidth * (canH_d_vidH)) / 2.0f
}
else {
ratio = canW_d_vidW
ratioX = true
scaleFx = canW_d_vidW
scaleFy = canW_d_vidW
tranFy = (canvasHeight - videoHeight * (canW_d_vidW)) / 2.0f
}
}
ImageView.ScaleType.CENTER_INSIDE -> {
if (videoWidth < canvasWidth && videoHeight < canvasHeight) {
tranFx = canW_vidW_f
tranFy = canH_vidH_f
}
else {
if (videoRatio > canvasRatio) {
ratio = canW_d_vidW
ratioX = true
scaleFx = canW_d_vidW
scaleFy = canW_d_vidW
tranFy = (canvasHeight - videoHeight * (canW_d_vidW)) / 2.0f
}
else {
ratio = canH_d_vidH
ratioX = false
scaleFx = canH_d_vidH
scaleFy = canH_d_vidH
tranFx = (canvasWidth - videoWidth * (canH_d_vidH)) / 2.0f
}
}
}
ImageView.ScaleType.FIT_CENTER -> {
if (videoRatio > canvasRatio) {
ratio = canW_d_vidW
ratioX = true
scaleFx = canW_d_vidW
scaleFy = canW_d_vidW
tranFy = (canvasHeight - videoHeight * (canW_d_vidW)) / 2.0f
}
else {
ratio = canH_d_vidH
ratioX = false
scaleFx = canH_d_vidH
scaleFy = canH_d_vidH
tranFx = (canvasWidth - videoWidth * (canH_d_vidH)) / 2.0f
}
}
ImageView.ScaleType.FIT_START -> {
if (videoRatio > canvasRatio) {
ratio = canW_d_vidW
ratioX = true
scaleFx = canW_d_vidW
scaleFy = canW_d_vidW
}
else {
ratio = canH_d_vidH
ratioX = false
scaleFx = canH_d_vidH
scaleFy = canH_d_vidH
}
}
ImageView.ScaleType.FIT_END -> {
if (videoRatio > canvasRatio) {
ratio = canW_d_vidW
ratioX = true
scaleFx = canW_d_vidW
scaleFy = canW_d_vidW
tranFy= canvasHeight - videoHeight * (canW_d_vidW)
}
else {
ratio = canH_d_vidH
ratioX = false
scaleFx = canH_d_vidH
scaleFy = canH_d_vidH
tranFx = canvasWidth - videoWidth * (canH_d_vidH)
}
}
ImageView.ScaleType.FIT_XY -> {
ratio = Math.max(canW_d_vidW, canH_d_vidH)
ratioX = canW_d_vidW > canH_d_vidH
scaleFx = canW_d_vidW
scaleFy = canH_d_vidH
}
else -> {
ratio = canW_d_vidW
ratioX = true
scaleFx = canW_d_vidW
scaleFy = canW_d_vidW
}
}
}
}

View File

@@ -1,11 +0,0 @@
package com.opensource.svgaplayer.utils
/**
* Created by cuiminghui on 2017/3/29.
*/
class SVGAPoint(val x: Float, val y: Float, val value: Float)
class SVGARect(val x: Double, val y: Double, val width: Double, val height: Double)
class SVGARange(val location: Int, val length: Int)

View File

@@ -1,28 +0,0 @@
package com.opensource.svgaplayer.utils.log
import android.util.Log
/**
* 内部默认 ILogger 接口实现
*/
class DefaultLogCat : ILogger {
override fun verbose(tag: String, msg: String) {
Log.v(tag, msg)
}
override fun info(tag: String, msg: String) {
Log.i(tag, msg)
}
override fun debug(tag: String, msg: String) {
Log.d(tag, msg)
}
override fun warn(tag: String, msg: String) {
Log.w(tag, msg)
}
override fun error(tag: String, msg: String?, error: Throwable?) {
Log.e(tag, msg, error)
}
}

View File

@@ -1,12 +0,0 @@
package com.opensource.svgaplayer.utils.log
/**
* log 外部接管接口
**/
interface ILogger {
fun verbose(tag: String, msg: String)
fun info(tag: String, msg: String)
fun debug(tag: String, msg: String)
fun warn(tag: String, msg: String)
fun error(tag: String, msg: String?, error: Throwable?)
}

View File

@@ -1,57 +0,0 @@
package com.opensource.svgaplayer.utils.log
/**
* 日志输出
*/
internal object LogUtils {
private const val TAG = "SVGALog"
fun verbose(tag: String = TAG, msg: String) {
if (!SVGALogger.isLogEnabled()) {
return
}
SVGALogger.getSVGALogger()?.verbose(tag, msg)
}
fun info(tag: String = TAG, msg: String) {
if (!SVGALogger.isLogEnabled()) {
return
}
SVGALogger.getSVGALogger()?.info(tag, msg)
}
fun debug(tag: String = TAG, msg: String) {
if (!SVGALogger.isLogEnabled()) {
return
}
SVGALogger.getSVGALogger()?.debug(tag, msg)
}
fun warn(tag: String = TAG, msg: String) {
if (!SVGALogger.isLogEnabled()) {
return
}
SVGALogger.getSVGALogger()?.warn(tag, msg)
}
fun error(tag: String = TAG, msg: String) {
if (!SVGALogger.isLogEnabled()) {
return
}
SVGALogger.getSVGALogger()?.error(tag, msg, null)
}
fun error(tag: String, error: Throwable) {
if (!SVGALogger.isLogEnabled()) {
return
}
SVGALogger.getSVGALogger()?.error(tag, error.message, error)
}
fun error(tag: String = TAG, msg: String, error: Throwable) {
if (!SVGALogger.isLogEnabled()) {
return
}
SVGALogger.getSVGALogger()?.error(tag, msg, error)
}
}

View File

@@ -1,40 +0,0 @@
package com.opensource.svgaplayer.utils.log
/**
* SVGA logger 配置管理
**/
object SVGALogger {
private var mLogger: ILogger? = DefaultLogCat()
private var isLogEnabled = false
/**
* log 接管注入
*/
fun injectSVGALoggerImp(logImp: ILogger): SVGALogger {
mLogger = logImp
return this
}
/**
* 设置是否开启 log
*/
fun setLogEnabled(isEnabled: Boolean): SVGALogger {
isLogEnabled = isEnabled
return this
}
/**
* 获取当前 ILogger 实现类
*/
fun getSVGALogger(): ILogger? {
return mLogger
}
/**
* 是否开启 log
*/
fun isLogEnabled(): Boolean {
return isLogEnabled
}
}

View File

@@ -1,16 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="SVGAImageView">
<attr name="source" format="string" />
<attr name="autoPlay" format="boolean" />
<attr name="antiAlias" format="boolean" />
<attr name="loopCount" format="integer" />
<attr name="clearsAfterStop" format="boolean" />
<attr name="clearsAfterDetached" format="boolean" />
<attr name="fillMode" format="enum">
<enum name="Backward" value="0" />
<enum name="Forward" value="1" />
<enum name="Clear" value="2"/>
</attr>
</declare-styleable>
</resources>

View File

@@ -1,3 +0,0 @@
<resources>
<string name="app_name">SVGAPlayer</string>
</resources>

1
Share/.gitignore vendored
View File

@@ -1 +0,0 @@
/build

View File

@@ -1,74 +0,0 @@
apply plugin: 'com.android.library'
apply plugin: 'img-optimizer'
apply plugin: 'kotlin-android'
apply plugin: 'kotlin-parcelize'
apply from: "../package_config.gradle"
android {
namespace "com.newpdlive.sy"
compileSdkVersion rootProject.ext.android.compileSdkVersion
buildToolsVersion rootProject.ext.android.buildToolsVersion
packagingOptions {
pickFirst "lib/armeabi/libyuvutils.so"
pickFirst "lib/arm64-v8a/libyuvutils.so"
pickFirst "lib/armeabi-v7a/libyuvutils.so"
pickFirst "lib/armeabi/libyuvtools.so"
pickFirst "lib/arm64-v8a/libyuvtools.so"
pickFirst "lib/armeabi-v7a/libyuvtools.so"
exclude "lib/arm64-v8a/libmmcv_api_handgesture.so"
exclude "lib/arm64-v8a/libmmcv_api_express.so"
exclude "lib/arm64-v8a/libMediaEncoder.so"
exclude "lib/arm64-v8a/libarcore_sdk_c.so"
exclude "lib/arm64-v8a/libmediadecoder.so"
exclude "lib/arm64-v8a/libMediaMuxer.so"
exclude "lib/arm64-v8a/libarcore_sdk_jni.so"
exclude "lib/arm64-v8a/libMediaUtils.so"
exclude "lib/arm64-v8a/libcosmosffmpeg.so"
}
defaultConfig {
minSdkVersion rootProject.ext.android.minSdkVersion
targetSdkVersion rootProject.ext.android.targetSdkVersion
versionCode rootProject.ext.android.versionCode
versionName rootProject.ext.android.versionName
manifestPlaceholders = rootProject.ext.manifestPlaceholders
ndk {
abiFilters "armeabi-v7a", "arm64-v8a","x86","x86_64"
}
}
aaptOptions {
cruncherEnabled = false
useNewCruncher = false
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
}
repositories {
flatDir {
dirs 'libs', '../libs'
}
}
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation rootProject.ext.dependencies["appcompat-androidx"]
implementation rootProject.ext.dependencies["recyclerview-androidx"]
//common
implementation project(path: ':common')
//Twitter
implementation 'com.twitter.sdk.android:twitter:3.1.1'
//facebook & Messenger
implementation 'com.facebook.android:facebook-share:15.2.0'
}

View File

@@ -1,21 +0,0 @@
# Add project specific ProGuard rules here.
# You can control the set of applied configuration files using the
# proguardFiles setting in build.gradle.
#
# For more details, see
# http://developer.android.com/guide/developing/tools/proguard.html
# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
# public *;
#}
# Uncomment this to preserve the line number information for
# debugging stack traces.
#-keepattributes SourceFile,LineNumberTable
# If you keep the line number information, uncomment this to
# hide the original source file name.
#-renamesourcefileattribute SourceFile

View File

@@ -1,26 +0,0 @@
package com.yunbao.share;
import android.content.Context;
import androidx.test.platform.app.InstrumentationRegistry;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import org.junit.Test;
import org.junit.runner.RunWith;
import static org.junit.Assert.*;
/**
* Instrumented test, which will execute on an Android device.
*
* @see <a href="http://d.android.com/tools/testing">Testing documentation</a>
*/
@RunWith(AndroidJUnit4.class)
public class ExampleInstrumentedTest {
@Test
public void useAppContext() {
// Context of the app under test.
Context appContext = InstrumentationRegistry.getInstrumentation().getTargetContext();
assertEquals("com.yunbao.share.test", appContext.getPackageName());
}
}

View File

@@ -1,43 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
<queries>
<package android:name="com.newpdlive.sy"/>
<package android:name="com.facebook.orca"/>
<package
android:name="com.facebook.composer" />
<package
android:name="com.facebook.katana" />
<package
android:name="com.facebook.messenger" />
<provider
android:authorities="com.facebook.katana.provider.PlatformProvider"
tools:ignore="ExportedContentProvider" />
</queries>
<application android:allowBackup="true">
<activity
android:name="com.facebook.FacebookActivity"
android:configChanges="keyboard|keyboardHidden|screenLayout|screenSize|orientation"
android:label="@string/app_name"
android:screenOrientation="portrait" />
<!-- <provider
android:name="com.facebook.FacebookContentProvider"
android:authorities="com.facebook.app.FacebookContentProvider2011402032399020"
android:exported="true" />-->
<receiver
android:name="com.yunbao.share.receiver.TwitterResultReceiver"
android:exported="false">
<intent-filter>
<action android:name="com.twitter.sdk.android.tweetcomposer.UPLOAD_SUCCESS" />
<action android:name="com.twitter.sdk.android.tweetcomposer.UPLOAD_FAILURE" />
<action android:name="com.twitter.sdk.android.tweetcomposer.TWEET_COMPOSE_CANCEL" />
</intent-filter>
</receiver>
</application>
</manifest>

View File

@@ -1,34 +0,0 @@
package com.yunbao.share;
import android.content.Context;
import android.content.IntentFilter;
import android.net.Uri;
import androidx.core.content.FileProvider;
import com.twitter.sdk.android.tweetcomposer.TweetUploadService;
import com.yunbao.share.bean.ShareBuilder;
import com.yunbao.share.receiver.TwitterResultReceiver;
import java.io.File;
public abstract class AbsShareInterface {
protected final Context mContext;
public AbsShareInterface(Context context) {
this.mContext = context;
IntentFilter filter = new IntentFilter(TweetUploadService.UPLOAD_SUCCESS);
filter.addAction(TweetUploadService.UPLOAD_FAILURE);
filter.addAction(TweetUploadService.TWEET_COMPOSE_CANCEL);
context.registerReceiver(new TwitterResultReceiver(), filter);
}
public abstract void share(ShareBuilder builder, ICallback callback);
public Uri fileToUri(File file){
return FileProvider.getUriForFile(mContext,
mContext.getPackageName() + ".fileprovider",
file
);
}
}

View File

@@ -1,6 +0,0 @@
package com.yunbao.share;
public interface ICallback {
void onSuccess();
void onFailure();
}

View File

@@ -1,130 +0,0 @@
package com.yunbao.share.adapters;
import android.content.Context;
import android.os.Environment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.TextView;
import androidx.annotation.DrawableRes;
import androidx.annotation.NonNull;
import androidx.annotation.StringRes;
import androidx.recyclerview.widget.RecyclerView;
import com.newpdlive.sy.R;
import com.yunbao.share.ICallback;
import com.yunbao.share.bean.ShareBuilder;
import com.yunbao.share.platform.FacebookShare;
import com.yunbao.share.platform.Instagram;
import com.yunbao.share.platform.Line;
import com.yunbao.share.platform.MessengerShare;
import com.yunbao.share.platform.TwitterShare;
import com.yunbao.share.platform.WhatsApp;
import java.io.File;
import java.util.ArrayList;
import java.util.List;
public class ShareAppAdapter extends RecyclerView.Adapter<ShareAppAdapter.AppViewHolder> {
private Context mContext;
private List<ShareBuilder> list;
public ShareAppAdapter(Context mContext) {
list = new ArrayList<>();
this.mContext = mContext;
}
public void setList(List<ShareBuilder> list) {
this.list = list;
notifyDataSetChanged();
}
@NonNull
@Override
public AppViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
return new AppViewHolder(LayoutInflater.from(mContext).inflate(R.layout.item_share_app, parent, false));
}
@Override
public void onBindViewHolder(@NonNull AppViewHolder holder, int position) {
ShareBuilder builder = list.get(position);
switch (builder.getType()) {
case ShareBuilder.APP_FACEBOOK:
holder.setData(builder, R.mipmap.icon_share_facebook, R.string.dialog_share_app_facebook);
break;
case ShareBuilder.APP_LINE:
holder.setData(builder, R.mipmap.icon_share_line, R.string.dialog_share_app_line);
break;
case ShareBuilder.APP_TWITTER:
holder.setData(builder, R.mipmap.icon_share_twitter, R.string.dialog_share_app_twitter);
break;
case ShareBuilder.APP_WHATSAPP:
holder.setData(builder, R.mipmap.icon_share_whatsapp, R.string.dialog_share_app_whatsapp);
break;
case ShareBuilder.APP_MESSENGER:
holder.setData(builder, R.mipmap.icon_share_messenger, R.string.dialog_share_app_messenger);
break;
case ShareBuilder.APP_INSTAGRAM:
holder.setData(builder, R.mipmap.icon_share_instagram, R.string.dialog_share_app_instagram);
break;
}
}
@Override
public int getItemCount() {
return list.size();
}
public static class AppViewHolder extends RecyclerView.ViewHolder {
ImageView icon;
TextView title;
public AppViewHolder(@NonNull View itemView) {
super(itemView);
icon = itemView.findViewById(R.id.share_app_icon);
title = itemView.findViewById(R.id.share_app_name);
}
public void setData(ShareBuilder bean, @DrawableRes int iconId, @StringRes int appName) {
icon.setImageResource(iconId);
title.setText(appName);
itemView.setOnClickListener(v -> {
switch (bean.getType()) {
case ShareBuilder.APP_FACEBOOK:
new FacebookShare(itemView.getContext()).share(bean, new ShareCallback());
break;
case ShareBuilder.APP_LINE:
new Line(itemView.getContext()).share(bean, new ShareCallback());
break;
case ShareBuilder.APP_TWITTER:
new TwitterShare(itemView.getContext()).share(bean, new ShareCallback());
break;
case ShareBuilder.APP_WHATSAPP:
new WhatsApp(itemView.getContext()).share(bean, new ShareCallback());
break;
case ShareBuilder.APP_MESSENGER:
new MessengerShare(itemView.getContext()).share(bean, new ShareCallback());
break;
case ShareBuilder.APP_INSTAGRAM:
new Instagram(itemView.getContext()).share(bean, new ShareCallback());
break;
}
});
}
private static class ShareCallback implements ICallback {
@Override
public void onSuccess() {
}
@Override
public void onFailure() {
}
}
}
}

View File

@@ -1,134 +0,0 @@
package com.yunbao.share.bean;
import androidx.annotation.NonNull;
import com.yunbao.common.CommonAppConfig;
import com.yunbao.common.CommonAppContext;
import com.yunbao.common.manager.IMLoginManager;
import com.yunbao.common.utils.StringUtil;
import java.io.File;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.util.Locale;
public class ShareBuilder {
public static final int APP_FACEBOOK = 0;
public static final int APP_LINE = 1;
public static final int APP_TWITTER = 2;
public static final int APP_WHATSAPP = 3;
public static final int APP_MESSENGER = 4;
public static final int APP_INSTAGRAM = 5;
private String text;
private String link;
private File file;
private int type;
private String uid;
private String anchorId;
private String anchorName;
private String anchorAvatar;
public static String createLiveShareLink(String shareUid, String anchorId, String anchorName, String anchorAvatar) {
return String.format(CommonAppConfig.HOST +
"/index.php?g=Appapi&m=home&a=share&uid=%s&user_id=%s&isGoogle=%s",
anchorId,
shareUid,
CommonAppConfig.IS_GOOGLE_PLAY
);
}
public static String createInviteLink(String shareUid) {
return String.format("https://www.pdlive.com/public/app/download/index.html?user_id=%s&isGoogle=%s",
shareUid,
CommonAppConfig.IS_GOOGLE_PLAY
);
}
public static ShareBuilder builder(int type) {
return new ShareBuilder(type);
}
private ShareBuilder(int type) {
this.type = type;
}
public int getType() {
return type;
}
public String getUid() {
return uid;
}
public void setUid(String uid) {
this.uid = uid;
}
public String getAnchorId() {
return anchorId;
}
public void setAnchorId(String anchorId) {
this.anchorId = anchorId;
}
public String getAnchorName() {
return anchorName;
}
public void setAnchorName(String anchorName) {
this.anchorName = anchorName;
}
public String getAnchorAvatar() {
return anchorAvatar;
}
public void setAnchorAvatar(String anchorAvatar) {
this.anchorAvatar = anchorAvatar;
}
public ShareBuilder setText(String text) {
this.text = text;
return this;
}
public ShareBuilder setLink(String link) {
this.link = link;
return this;
}
public ShareBuilder setFile(File file) {
this.file = file;
return this;
}
public String getText() {
if (StringUtil.isEmpty(text)) {
return getLink();
}
return text + "\n" + getLink();
}
public String getLink() {
if (StringUtil.isEmpty(link)) {
link = createLiveShareLink(uid, anchorId, anchorName, anchorAvatar);
}
return link;
}
public File getFile() {
return file;
}
@NonNull
@Override
public String toString() {
return "ShareBuilder{" +
"text='" + text + '\'' +
", link='" + link + '\'' +
", file=" + file +
'}';
}
}

View File

@@ -1,48 +0,0 @@
package com.yunbao.share.platform;
import android.app.Activity;
import android.content.Context;
import android.net.Uri;
import androidx.annotation.NonNull;
import com.facebook.CallbackManager;
import com.facebook.FacebookCallback;
import com.facebook.FacebookException;
import com.facebook.share.Sharer;
import com.facebook.share.model.ShareLinkContent;
import com.facebook.share.widget.ShareDialog;
import com.yunbao.common.utils.ToastUtil;
import com.yunbao.share.AbsShareInterface;
import com.yunbao.share.ICallback;
import com.yunbao.share.bean.ShareBuilder;
public class FacebookShare extends AbsShareInterface {
public static CallbackManager callbackManager;
public FacebookShare(Context context) {
super(context);
}
@Override
public void share(ShareBuilder builder, ICallback callback) {
callbackManager= CallbackManager.Factory.create();
ShareLinkContent content = new ShareLinkContent.Builder()
.setContentUrl(Uri.parse(builder.getLink()))
.build();
ShareDialog dialog=new ShareDialog((Activity) mContext);
dialog.registerCallback(callbackManager, new FacebookCallback<Sharer.Result>() {
@Override
public void onSuccess(Sharer.Result result) {
}
@Override
public void onCancel() {
}
@Override
public void onError(@NonNull FacebookException e) {
}
});
dialog.show(content);
}
}

View File

@@ -1,27 +0,0 @@
package com.yunbao.share.platform;
import android.content.Context;
import android.content.Intent;
import android.net.Uri;
import com.yunbao.share.AbsShareInterface;
import com.yunbao.share.ICallback;
import com.yunbao.share.bean.ShareBuilder;
public class Instagram extends AbsShareInterface {
public Instagram(Context context) {
super(context);
}
@Override
public void share(ShareBuilder builder, ICallback callback) {
String type = "image/*";
Intent share = new Intent(Intent.ACTION_SEND);
Uri uri = fileToUri(builder.getFile());
share.setType(type);
share.putExtra(Intent.EXTRA_STREAM, uri);
share.setPackage("com.instagram.android");
mContext.startActivity(Intent.createChooser(share, "Share to"));
callback.onSuccess();
}
}

View File

@@ -1,31 +0,0 @@
package com.yunbao.share.platform;
import android.content.Context;
import android.content.Intent;
import android.net.Uri;
import com.yunbao.share.AbsShareInterface;
import com.yunbao.share.ICallback;
import com.yunbao.share.bean.ShareBuilder;
import java.net.URLEncoder;
public class Line extends AbsShareInterface {
public Line(Context context) {
super(context);
}
@Override
public void share(ShareBuilder builder, ICallback callback) {
try {
Intent share = new Intent(Intent.ACTION_VIEW, Uri.parse("https://line.me/R/share?text=" + URLEncoder.encode(builder.getText(), "UTF-8")));
mContext.startActivity(share);
callback.onSuccess();
} catch (Exception e) {
callback.onFailure();
throw new RuntimeException(e);
}
}
}

View File

@@ -1,57 +0,0 @@
package com.yunbao.share.platform;
import android.app.Activity;
import android.content.Context;
import android.net.Uri;
import androidx.annotation.NonNull;
import com.facebook.CallbackManager;
import com.facebook.FacebookCallback;
import com.facebook.FacebookException;
import com.facebook.FacebookSdk;
import com.facebook.share.Sharer;
import com.facebook.share.model.ShareLinkContent;
import com.facebook.share.widget.MessageDialog;
import com.yunbao.common.utils.ToastUtil;
import com.yunbao.share.AbsShareInterface;
import com.yunbao.share.ICallback;
import com.yunbao.share.bean.ShareBuilder;
public class MessengerShare extends AbsShareInterface {
public static CallbackManager callbackManager;
public MessengerShare(Context context) {
super(context);
FacebookSdk.setIsDebugEnabled(true);
}
@Override
public void share(ShareBuilder builder, ICallback callback) {
callbackManager = CallbackManager.Factory.create();
ShareLinkContent content = new ShareLinkContent.Builder()
.setContentUrl(Uri.parse(builder.getLink()))
.build();
MessageDialog dialog = new MessageDialog((Activity) mContext);
dialog.registerCallback(callbackManager, new FacebookCallback<Sharer.Result>() {
@Override
public void onSuccess(Sharer.Result result) {
callback.onSuccess();
}
@Override
public void onCancel() {
}
@Override
public void onError(@NonNull FacebookException e) {
e.printStackTrace();
}
});
if(dialog.canShow(content)) {
dialog.show(content);
}else{
callback.onFailure();
}
}
}

View File

@@ -1,73 +0,0 @@
package com.yunbao.share.platform;
import android.content.Context;
import android.content.Intent;
import android.net.Uri;
import com.twitter.sdk.android.core.Twitter;
import com.yunbao.share.AbsShareInterface;
import com.yunbao.share.ICallback;
import com.yunbao.share.bean.ShareBuilder;
import java.net.URLEncoder;
public class TwitterShare extends AbsShareInterface {
public TwitterShare(Context context) {
super(context);
Twitter.initialize(context);
}
@Override
public void share(ShareBuilder date, ICallback callback) {
/*
new TwitterAuthClient().authorize((Activity) mContext, new Callback<TwitterSession>() {
@Override
public void success(Result<TwitterSession> result) {
//获取以下登录成功返回信息进行登录验证
//获取登录用户信息
final TwitterSession activeSession=TwitterCore.getInstance().getSessionManager().getActiveSession();
Intent intent = new ComposerActivity.Builder(mContext)
.session(activeSession)
.image(fileToUri(date.getFile()))
.text(date.getText())
.hashtags("#twitter")
.createIntent();
mContext.startActivity(intent);
}
@Override
public void failure(TwitterException e) {
}
});*/
/* TweetComposer.Builder builder;
if (date.getFile() == null) {
try {
builder=new TweetComposer.Builder(mContext)
.text(date.getText())
.url(new URL(date.getLink()));
} catch (MalformedURLException e) {
throw new RuntimeException(e);
}
} else {
builder = new TweetComposer.Builder(mContext)
.text(date.getText())
.image(fileToUri(date.getFile()));
}
builder.show();*/
try {
Intent sendIntent = new Intent();
sendIntent.setAction(Intent.ACTION_VIEW);
sendIntent.putExtra(Intent.EXTRA_TEXT, date.getText());
// sendIntent.setType("text/plain");
//sendIntent.setPackage("com.twitter.composer.ComposerShareActivity");
sendIntent.setData(Uri.parse("https://twitter.com/intent/tweet?text=" +URLEncoder.encode(date.getText(),"UTF-8")));
mContext.startActivity(sendIntent);
callback.onSuccess();
} catch (Exception e) {
callback.onFailure();
}
}
}

View File

@@ -1,30 +0,0 @@
package com.yunbao.share.platform;
import android.content.Context;
import android.content.Intent;
import com.yunbao.share.AbsShareInterface;
import com.yunbao.share.ICallback;
import com.yunbao.share.bean.ShareBuilder;
public class WhatsApp extends AbsShareInterface {
public WhatsApp(Context context) {
super(context);
}
@Override
public void share(ShareBuilder builder, ICallback callback) {
try {
Intent sendIntent = new Intent();
sendIntent.setAction(Intent.ACTION_SEND);
sendIntent.putExtra(Intent.EXTRA_TEXT, builder.getText());
sendIntent.setType("text/plain");
sendIntent.setPackage("com.whatsapp");
mContext.startActivity(sendIntent);
callback.onSuccess();
} catch (Exception e) {
callback.onFailure();
}
}
}

View File

@@ -1,28 +0,0 @@
package com.yunbao.share.receiver;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.util.Log;
import com.twitter.sdk.android.tweetcomposer.TweetUploadService;
import com.yunbao.common.utils.ToastUtil;
public class TwitterResultReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
Log.i("分享log", "onReceive: "+intent.getAction());
if (TweetUploadService.UPLOAD_SUCCESS.equals(intent.getAction())) {
ToastUtil.show("推特分享成功");
// success
final Long tweetId = intent.getExtras().getLong(TweetUploadService.EXTRA_TWEET_ID);
} else if (TweetUploadService.UPLOAD_FAILURE.equals(intent.getAction())) {
// failure
ToastUtil.show("推特分享失败");
final Intent retryIntent = intent.getExtras().getParcelable(TweetUploadService.EXTRA_RETRY_INTENT);
} else if (TweetUploadService.TWEET_COMPOSE_CANCEL.equals(intent.getAction())) {
// cancel
ToastUtil.show("推特分享取消");
}
}
}

View File

@@ -1,158 +0,0 @@
package com.yunbao.share.ui;
import static android.content.Context.CLIPBOARD_SERVICE;
import android.app.Dialog;
import android.content.ClipData;
import android.content.ClipboardManager;
import android.content.Context;
import android.graphics.Color;
import android.graphics.LinearGradient;
import android.graphics.Shader;
import android.view.ViewTreeObserver;
import android.widget.ImageView;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.GridLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import com.lxj.xpopup.XPopup;
import com.makeramen.roundedimageview.RoundedImageView;
import com.newpdlive.sy.R;
import com.yunbao.common.CommonAppConfig;
import com.yunbao.common.dialog.AbsDialogPopupWindow;
import com.yunbao.common.utils.DialogUitl;
import com.yunbao.common.utils.ToastUtil;
import com.yunbao.common.utils.WordUtil;
import com.yunbao.share.bean.ShareBuilder;
import com.yunbao.share.adapters.ShareAppAdapter;
import java.util.ArrayList;
import java.util.List;
public class InvitePopDialog extends AbsDialogPopupWindow {
private ShareAppAdapter adapter;
private RecyclerView list;
private RoundedImageView avatar;
private TextView info;
private TextView link;
private TextView title;
private List<ShareBuilder> data;
private String uid;
private String anchorId;
private String anchorName;
private String anchorAvatar;
private String url;
public InvitePopDialog(@NonNull Context context) {
super(context);
}
public InvitePopDialog setUid(String uid) {
this.uid = uid;
return this;
}
public InvitePopDialog setAnchorId(String anchorId) {
this.anchorId = anchorId;
return this;
}
public InvitePopDialog setAnchorName(String anchorName) {
this.anchorName = anchorName;
return this;
}
public InvitePopDialog setAnchorAvatar(String anchorAvatar) {
this.anchorAvatar = anchorAvatar;
return this;
}
@Override
public void buildDialog(XPopup.Builder builder) {
}
@Override
public int bindLayoutId() {
return R.layout.dialog_share;
}
@Override
protected void onCreate() {
super.onCreate();
findViewById(R.id.close).setOnClickListener(v -> dismiss());
((ImageView) findViewById(R.id.close)).setImageResource(R.mipmap.icon_invite_close);
findViewById(R.id.share_copy).setOnClickListener(v -> copyLink());
// findViewById(R.id.share_preview).setVisibility(GONE);
findViewById(R.id.share_dialog).setBackgroundResource(R.mipmap.bg_dialog_inviet);
title = findViewById(R.id.share_title);
list = findViewById(R.id.share_apps_list);
avatar = findViewById(R.id.share_avatar);
info = findViewById(R.id.share_info);
link = findViewById(R.id.share_link);
adapter = new ShareAppAdapter(getContext());
list.setLayoutManager(new GridLayoutManager(getContext(), 3));
list.setAdapter(adapter);
initData();
link.setText(url.substring(0, 40));
info.setText(mContext.getString(R.string.dialog_invite_info));
avatar.setImageResource(R.mipmap.ic_launcher);
//title.setTextColor(getContext().getResources().getColorStateList(R.drawable.bg_invite_title));
title.setText(R.string.dialog_invite_title);
title.getViewTreeObserver().addOnGlobalLayoutListener(this::setTitleColor);
}
private void setTitleColor() {
int[] colors = {
Color.parseColor("#3377FF"),
Color.parseColor("#7F66FF"),
};
float[] position = {
0f,
1.0f
};
float height = title.getMeasuredHeight();
LinearGradient mLinearGradient = new LinearGradient(0, 0, 0, height, colors, position, Shader.TileMode.CLAMP);
title.getPaint().setShader(mLinearGradient);
title.invalidate();
}
private void initData() {
data = new ArrayList<>();
data.add(builder(ShareBuilder.APP_FACEBOOK));
data.add(builder(ShareBuilder.APP_LINE));
data.add(builder(ShareBuilder.APP_TWITTER));
data.add(builder(ShareBuilder.APP_WHATSAPP));
data.add(builder(ShareBuilder.APP_MESSENGER));
// data.add(builder(ShareBuilder.APP_INSTAGRAM));
adapter.setList(data);
}
private ShareBuilder builder(int type) {
ShareBuilder builder = ShareBuilder.builder(type);
builder.setText(getContext().getString(R.string.dialog_invite_info));
builder.setLink(url);
builder.setUid(uid);
builder.setAnchorId(anchorId);
builder.setAnchorName(anchorName);
builder.setAnchorAvatar(anchorAvatar);
return builder;
}
private void copyLink() {
ClipboardManager cm = (ClipboardManager) getContext().getSystemService(CLIPBOARD_SERVICE);
ClipData clipData = ClipData.newPlainText("text", info.getText() + "\n" + url);
cm.setPrimaryClip(clipData);
ToastUtil.show(getContext().getString(com.yunbao.common.R.string.copy_success));
}
public InvitePopDialog setUrl(String data) {
this.url = data + "&isGoogle=" + CommonAppConfig.IS_GOOGLE_PLAY;
return this;
}
}

View File

@@ -1,147 +0,0 @@
package com.yunbao.share.ui;
import static android.content.Context.CLIPBOARD_SERVICE;
import android.content.ClipData;
import android.content.ClipboardManager;
import android.content.Context;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.GridLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import com.lxj.xpopup.XPopup;
import com.makeramen.roundedimageview.RoundedImageView;
import com.newpdlive.sy.R;
import com.yunbao.common.dialog.AbsDialogPopupWindow;
import com.yunbao.common.glide.ImgLoader;
import com.yunbao.common.utils.StringUtil;
import com.yunbao.common.utils.ToastUtil;
import com.yunbao.common.utils.WordUtil;
import com.yunbao.share.adapters.ShareAppAdapter;
import com.yunbao.share.bean.ShareBuilder;
import java.util.ArrayList;
import java.util.List;
public class SharePopDialog extends AbsDialogPopupWindow {
private ShareAppAdapter adapter;
private RecyclerView list;
private RoundedImageView avatar;
private TextView info;
private TextView link;
private List<ShareBuilder> data;
private String uid;
private String anchorId;
private String anchorName;
private String anchorAvatar;
private String shareLink;
public SharePopDialog(@NonNull Context context) {
super(context);
}
public SharePopDialog setUid(String uid) {
this.uid = uid;
return this;
}
public SharePopDialog setAnchorId(String anchorId) {
this.anchorId = anchorId;
return this;
}
public SharePopDialog setAnchorName(String anchorName) {
this.anchorName = anchorName;
return this;
}
public SharePopDialog setAnchorAvatar(String anchorAvatar) {
this.anchorAvatar = anchorAvatar;
return this;
}
public SharePopDialog setShareLink(String link) {
this.shareLink = link + "&isZh=" + (WordUtil.isNewZh() ? "1" : 0);
return this;
}
@Override
public void buildDialog(XPopup.Builder builder) {
}
@Override
public int bindLayoutId() {
return R.layout.dialog_share;
}
@Override
protected void onCreate() {
super.onCreate();
findViewById(R.id.close).setOnClickListener(v -> dismiss());
findViewById(R.id.share_copy).setOnClickListener(v -> copyLink());
list = findViewById(R.id.share_apps_list);
avatar = findViewById(R.id.share_avatar);
info = findViewById(R.id.share_info);
link = findViewById(R.id.share_link);
adapter = new ShareAppAdapter(getContext());
list.setLayoutManager(new GridLayoutManager(getContext(), 3));
list.setAdapter(adapter);
initData();
}
private void initData() {
data = new ArrayList<>();
data.add(builder(ShareBuilder.APP_FACEBOOK));
data.add(builder(ShareBuilder.APP_LINE));
data.add(builder(ShareBuilder.APP_TWITTER));
data.add(builder(ShareBuilder.APP_WHATSAPP));
data.add(builder(ShareBuilder.APP_MESSENGER));
//data.add(builder(ShareBuilder.APP_INSTAGRAM));
adapter.setList(data);
String url;
if (shareLink == null) {
url = ShareBuilder.createLiveShareLink(uid, anchorId, anchorName, anchorAvatar).substring(0, 40) + "...";
} else {
if (shareLink.length() > 40) {
url = shareLink.substring(0, 40) + "...";
} else {
url = shareLink;
}
}
url = url + "&isZh=" + (WordUtil.isNewZh() ? "1" : 0);
link.setText(url);
info.setText(String.format(getContext().getString(R.string.dialog_share_info), StringUtil.isEmpty(anchorName) ? "" : anchorName));
ImgLoader.display(getContext(), anchorAvatar, avatar);
}
private ShareBuilder builder(int type) {
ShareBuilder builder = ShareBuilder.builder(type);
builder.setText(String.format(getContext().getString(R.string.dialog_share_info), StringUtil.isEmpty(anchorName) ? "" : anchorName));
builder.setUid(uid);
builder.setAnchorId(anchorId);
builder.setAnchorName(anchorName);
builder.setAnchorAvatar(anchorAvatar);
if (shareLink != null) {
builder.setLink(shareLink);
}
return builder;
}
private void copyLink() {
String url;
if (shareLink != null) {
url = shareLink;
} else {
url = ShareBuilder.createLiveShareLink(uid, anchorId, anchorName, anchorAvatar);
}
ClipboardManager cm = (ClipboardManager) getContext().getSystemService(CLIPBOARD_SERVICE);
ClipData clipData = ClipData.newPlainText("text", info.getText().toString() + "\n" + url + "&isZh=" + (WordUtil.isNewZh() ? "1" : 0));
cm.setPrimaryClip(clipData);
ToastUtil.show(getContext().getString(com.yunbao.common.R.string.copy_success));
}
}

View File

@@ -1,9 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:width="189dp" android:height="42dp">
<shape android:shape="rectangle">
<gradient android:type="linear" android:useLevel="true" android:startColor="#ffffc621" android:endColor="#ffffae05" android:angle="135" />
<corners android:radius="21dp" />
</shape>
</item>
</selector>

View File

@@ -1,10 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:width="112dp" android:height="42dp">
<shape android:shape="rectangle">
<stroke android:width="1dp" android:color="#ffffffff" />
<gradient android:type="linear" android:useLevel="true" android:startColor="#3377FF" android:endColor="#7F66FF" android:angle="90" />
<corners android:topLeftRadius="22dp" android:topRightRadius="22dp" android:bottomLeftRadius="22dp" android:bottomRightRadius="22dp" />
</shape>
</item>
</selector>

View File

@@ -1,12 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item>
<shape android:shape="rectangle">
<corners android:radius="15dp" />
<solid android:color="#ffffff" />
<stroke
android:width="1dp"
android:color="#EBEBEB" />
</shape>
</item>
</selector>

View File

@@ -1,56 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:id="@+id/share_dialog"
android:layout_height="wrap_content"
android:background="@mipmap/bg_dialog_share">
<LinearLayout
android:id="@+id/linearLayout2"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_marginTop="16dp"
android:orientation="horizontal"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">
<TextView
android:id="@+id/share_title"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_weight="1"
android:text="@string/dialog_invite_title"
android:textColor="@drawable/bg_invite_title"
android:textSize="16sp"
android:textStyle="bold" />
<ImageView
android:id="@+id/close"
android:layout_width="24dp"
android:layout_height="24dp"
android:layout_marginEnd="16dp"
app:srcCompat="@mipmap/icon_dialog_charge_close" />
</LinearLayout>
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/share_apps_list"
android:layout_width="match_parent"
android:layout_height="200dp"
android:layout_marginTop="16dp"
android:layout_marginBottom="16dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/linearLayout2"
tools:itemCount="6"
tools:layoutManager="GridLayoutManager"
tools:listitem="@layout/item_share_app"
tools:spanCount="3" />
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@@ -1,66 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:id="@+id/share_dialog"
android:layout_height="wrap_content"
android:background="@mipmap/bg_dialog_share">
<LinearLayout
android:id="@+id/linearLayout2"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_marginTop="16dp"
android:orientation="horizontal"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">
<TextView
android:id="@+id/share_title"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_weight="1"
android:text="@string/dialog_share_title"
android:textColor="#333333"
android:textSize="16sp"
android:textStyle="bold" />
<ImageView
android:id="@+id/close"
android:layout_width="24dp"
android:layout_height="24dp"
android:layout_marginEnd="16dp"
app:srcCompat="@mipmap/icon_share_close" />
</LinearLayout>
<include
android:id="@+id/share_preview"
layout="@layout/view_share_preview"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginTop="14dp"
android:layout_marginEnd="16dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/linearLayout2" />
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/share_apps_list"
android:layout_width="match_parent"
android:layout_height="200dp"
android:layout_marginTop="16dp"
android:layout_marginBottom="16dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/share_preview"
tools:itemCount="6"
tools:layoutManager="GridLayoutManager"
tools:listitem="@layout/item_share_app"
tools:spanCount="3" />
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@@ -1,34 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/linearLayout"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
<ImageView
android:id="@+id/share_app_icon"
android:layout_width="56dp"
android:layout_height="56dp"
android:layout_marginStart="35dp"
android:layout_marginTop="12dp"
android:layout_marginEnd="35dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:src="@tools:sample/avatars" />
<TextView
android:id="@+id/share_app_name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:layout_marginBottom="12dp"
android:text="TextView"
android:textColor="#666666"
android:textSize="12sp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/share_app_icon" />
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@@ -1,75 +0,0 @@
<?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"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@drawable/bg_preview"
android:orientation="vertical">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="12dp"
android:layout_marginTop="12dp"
android:layout_marginEnd="2dp"
android:orientation="horizontal">
<com.makeramen.roundedimageview.RoundedImageView
android:id="@+id/share_avatar"
android:layout_width="40dp"
android:layout_height="40dp"
android:scaleType="centerCrop"
android:src="@drawable/m_chu_xia"
app:riv_oval="true" />
<TextView
android:id="@+id/share_info"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="12dp"
android:layout_weight="1"
android:text="@string/dialog_share_info"
android:textColor="#333333"
android:textSize="12sp" />
<TextView
android:id="@+id/share_copy"
android:layout_width="70dp"
android:layout_height="32dp"
android:layout_gravity="center"
android:layout_marginEnd="16dp"
android:background="@drawable/bg_btn"
android:gravity="center"
android:layout_marginStart="25dp"
android:text="@string/dialog_share_copy"
android:textColor="@color/white"
android:textSize="16sp" />
</LinearLayout>
<LinearLayout
android:id="@+id/share_layout_link"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="12dp"
android:layout_marginTop="8dp"
android:layout_marginEnd="12dp"
android:layout_marginBottom="12dp"
android:orientation="horizontal">
<ImageView
android:layout_width="16dp"
android:layout_height="16dp"
android:src="@mipmap/icon_share_url" />
<TextView
android:id="@+id/share_link"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:text="TextView"
android:textColor="#999999"
android:textSize="12sp" />
</LinearLayout>
</LinearLayout>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 315 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 83 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 548 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 484 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.8 KiB

Some files were not shown because too many files have changed in this diff Show More