「Update」
1、增加动态皮肤加载SDK开发
This commit is contained in:
@@ -65,6 +65,7 @@ dependencies {
|
|||||||
implementation "com.mogo.cloud:realtime:${MOGO_REALTIME_VERSION}"
|
implementation "com.mogo.cloud:realtime:${MOGO_REALTIME_VERSION}"
|
||||||
implementation "com.mogo.cloud:trafficlive:${MOGO_TRAFFICLIVE_VERSION}"
|
implementation "com.mogo.cloud:trafficlive:${MOGO_TRAFFICLIVE_VERSION}"
|
||||||
implementation "com.mogo.cloud:telematic:${MOGO_TELEMATIC_VERSION}"
|
implementation "com.mogo.cloud:telematic:${MOGO_TELEMATIC_VERSION}"
|
||||||
|
implementation "com.mogo.cloud:skin:${MOGO_SKIN_VERSION}"
|
||||||
implementation "com.mogo.v2x:v2x:${MOGO_V2X_VERSION}"
|
implementation "com.mogo.v2x:v2x:${MOGO_V2X_VERSION}"
|
||||||
} else {
|
} else {
|
||||||
implementation project(":foudations:mogo-location")
|
implementation project(":foudations:mogo-location")
|
||||||
@@ -72,6 +73,7 @@ dependencies {
|
|||||||
implementation project(":modules:mogo-realtime")
|
implementation project(":modules:mogo-realtime")
|
||||||
implementation project(":modules:mogo-trafficlive")
|
implementation project(":modules:mogo-trafficlive")
|
||||||
implementation project(":libraries:mogo-telematic")
|
implementation project(":libraries:mogo-telematic")
|
||||||
|
implementation project(":libraries:mogo-skin")
|
||||||
implementation project(":foudations:mogo-v2x")
|
implementation project(":foudations:mogo-v2x")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import android.view.SurfaceView;
|
|||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.widget.Button;
|
import android.widget.Button;
|
||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
|
import android.widget.ToggleButton;
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
import androidx.appcompat.app.AppCompatActivity;
|
import androidx.appcompat.app.AppCompatActivity;
|
||||||
@@ -18,6 +19,9 @@ import com.mogo.cloud.passport.MoGoAiCloudClientConfig;
|
|||||||
import com.mogo.cloud.trafficlive.api.ITrafficCarLiveCallBack;
|
import com.mogo.cloud.trafficlive.api.ITrafficCarLiveCallBack;
|
||||||
import com.mogo.cloud.trafficlive.api.ITrafficIntersectionLiveCallBack;
|
import com.mogo.cloud.trafficlive.api.ITrafficIntersectionLiveCallBack;
|
||||||
import com.mogo.cloud.trafficlive.api.MoGoAiCloudTrafficLive;
|
import com.mogo.cloud.trafficlive.api.MoGoAiCloudTrafficLive;
|
||||||
|
import com.mogo.skin.Skin;
|
||||||
|
import com.mogo.skin.SkinManager;
|
||||||
|
import com.mogo.skin.utils.SkinPreference;
|
||||||
import com.mogo.v2x.V2XManager;
|
import com.mogo.v2x.V2XManager;
|
||||||
import com.mogo.v2x.callback.IV2XCallback;
|
import com.mogo.v2x.callback.IV2XCallback;
|
||||||
import com.mogo.v2x.config.V2XConfig;
|
import com.mogo.v2x.config.V2XConfig;
|
||||||
@@ -32,6 +36,7 @@ public class MainActivity extends AppCompatActivity {
|
|||||||
private Button btnJumpPassPort;
|
private Button btnJumpPassPort;
|
||||||
private Button btnJumpConfigInfo;
|
private Button btnJumpConfigInfo;
|
||||||
private Button btnJumpNetWorkPort;
|
private Button btnJumpNetWorkPort;
|
||||||
|
private ToggleButton btnChangeSkin;
|
||||||
private Button btnJumpRealTime;
|
private Button btnJumpRealTime;
|
||||||
private Button btnJumpLocation;
|
private Button btnJumpLocation;
|
||||||
private Button btnJumpRoadCondition;
|
private Button btnJumpRoadCondition;
|
||||||
@@ -74,6 +79,25 @@ public class MainActivity extends AppCompatActivity {
|
|||||||
startActivity(intent);
|
startActivity(intent);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
btnChangeSkin = findViewById(R.id.btnChangeSkin);
|
||||||
|
btnChangeSkin.setChecked(SkinPreference.getInstance().getSkin() != "");
|
||||||
|
btnChangeSkin.setOnCheckedChangeListener((view, isCheck) -> {
|
||||||
|
Skin skin;
|
||||||
|
if (isCheck) {
|
||||||
|
skin = new Skin(
|
||||||
|
"d5493244467d3970834e42dc1a6f07c9",
|
||||||
|
"app-skin-debug.skin",
|
||||||
|
"https://carlife-static-1255510688.cos.ap-beijing.myqcloud.com/MoGoEagleEye/app-skin-debug.skin");
|
||||||
|
|
||||||
|
//换肤
|
||||||
|
SkinManager.getInstance().selectSkin(this, skin);
|
||||||
|
|
||||||
|
} else {
|
||||||
|
SkinManager.getInstance().loadSkin("");
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
btnJumpLocation = findViewById(R.id.btnJumpLocation);
|
btnJumpLocation = findViewById(R.id.btnJumpLocation);
|
||||||
btnJumpLocation.setOnClickListener(v -> {
|
btnJumpLocation.setOnClickListener(v -> {
|
||||||
Intent intent = new Intent(MainActivity.this, LocationActivity.class);
|
Intent intent = new Intent(MainActivity.this, LocationActivity.class);
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ import com.mogo.cloud.passport.MoGoAiCloudClient;
|
|||||||
import com.mogo.cloud.passport.MoGoAiCloudClientConfig;
|
import com.mogo.cloud.passport.MoGoAiCloudClientConfig;
|
||||||
import com.mogo.cloud.passport.location.ICurrentLocation;
|
import com.mogo.cloud.passport.location.ICurrentLocation;
|
||||||
import com.mogo.cloud.passport.location.SimpleLocation;
|
import com.mogo.cloud.passport.location.SimpleLocation;
|
||||||
|
import com.mogo.skin.SkinManager;
|
||||||
|
|
||||||
import java.util.Random;
|
import java.util.Random;
|
||||||
|
|
||||||
@@ -24,6 +25,8 @@ public class MoGoApplication extends MultiDexApplication {
|
|||||||
public void onCreate() {
|
public void onCreate() {
|
||||||
super.onCreate();
|
super.onCreate();
|
||||||
|
|
||||||
|
SkinManager.init(this);
|
||||||
|
|
||||||
// Crash 日志收集
|
// Crash 日志收集
|
||||||
CrashSystem crashSystem = CrashSystem.getInstance(this);
|
CrashSystem crashSystem = CrashSystem.getInstance(this);
|
||||||
crashSystem.init();
|
crashSystem.init();
|
||||||
|
|||||||
BIN
app/src/main/res/drawable-hdpi/icon_notice_default.png
Normal file
BIN
app/src/main/res/drawable-hdpi/icon_notice_default.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 19 KiB |
BIN
app/src/main/res/drawable-hdpi/text_drawable_left.png
Normal file
BIN
app/src/main/res/drawable-hdpi/text_drawable_left.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 2.3 KiB |
@@ -10,6 +10,12 @@
|
|||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
android:orientation="vertical">
|
android:orientation="vertical">
|
||||||
|
|
||||||
|
<com.mogo.skin.widget.SkinImageView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:src="@drawable/icon_notice_default"
|
||||||
|
/>
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/tvAppKey"
|
android:id="@+id/tvAppKey"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
@@ -60,6 +66,13 @@
|
|||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
android:text="实时数据测试" />
|
android:text="实时数据测试" />
|
||||||
|
|
||||||
|
<ToggleButton
|
||||||
|
android:id="@+id/btnChangeSkin"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:textOff="切换皮肤"
|
||||||
|
android:textOn="还原皮肤" />
|
||||||
|
|
||||||
<Button
|
<Button
|
||||||
android:id="@+id/btnJumpLocation"
|
android:id="@+id/btnJumpLocation"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ ext {
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
kotlinstdlibjdk7 : "org.jetbrains.kotlin:kotlin-stdlib-jdk7:${kotlin_version}",
|
kotlinstdlibjdk7 : "org.jetbrains.kotlin:kotlin-stdlib-jdk7:${kotlin_version}",
|
||||||
|
|
||||||
|
androidxccorektx : "androidx.core:core-ktx:1.5.0",
|
||||||
// android
|
// android
|
||||||
androidxappcompat : "androidx.appcompat:appcompat:1.3.1",
|
androidxappcompat : "androidx.appcompat:appcompat:1.3.1",
|
||||||
androidxconstraintlayout : "androidx.constraintlayout:constraintlayout:2.1.0",
|
androidxconstraintlayout : "androidx.constraintlayout:constraintlayout:2.1.0",
|
||||||
|
|||||||
@@ -1,5 +1,3 @@
|
|||||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
package="com.mogo.cloud.commons">
|
package="com.mogo.cloud.commons">
|
||||||
|
|
||||||
/
|
|
||||||
</manifest>
|
</manifest>
|
||||||
@@ -57,3 +57,5 @@ MOGO_LOCATION_VERSION=1.4.7.42
|
|||||||
MOGO_TELEMATIC_VERSION=1.4.7.42
|
MOGO_TELEMATIC_VERSION=1.4.7.42
|
||||||
# v2x
|
# v2x
|
||||||
MOGO_V2X_VERSION=1.4.7.42
|
MOGO_V2X_VERSION=1.4.7.42
|
||||||
|
# SKIN
|
||||||
|
MOGO_SKIN_VERSION=1.4.7.42
|
||||||
|
|||||||
1
libraries/mogo-skin/.gitignore
vendored
Normal file
1
libraries/mogo-skin/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
/build
|
||||||
49
libraries/mogo-skin/README.md
Normal file
49
libraries/mogo-skin/README.md
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
### 换肤SDK,
|
||||||
|
#### 使用方式
|
||||||
|
```java
|
||||||
|
public class MoGoApplication extends MultiDexApplication {
|
||||||
|
private static final String TAG = "MoGoApplication";
|
||||||
|
@Override
|
||||||
|
public void onCreate() {
|
||||||
|
super.onCreate();
|
||||||
|
|
||||||
|
// 初始化动态换肤SDK
|
||||||
|
SkinManager.init(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
##### 基础控件0改动完成资源替换,只需要保持使用的「资源文件:drawable、string、color等」皮肤包与APP中保持一致即可
|
||||||
|
##### 如果代码中动态控制了 ImageView 的 src、background 一定要在 XML 中替换成 SkinImageView.java
|
||||||
|
|
||||||
|
##### 代码中设置:图片、文字、颜色
|
||||||
|
```java
|
||||||
|
// 获取颜色
|
||||||
|
SkinResources.getInstance().getColor(resId);
|
||||||
|
|
||||||
|
// 获取图片
|
||||||
|
SkinResources.getInstance().getDrawable(resId);
|
||||||
|
|
||||||
|
// 获取文字
|
||||||
|
SkinResources.getInstance().getString(resId);
|
||||||
|
|
||||||
|
// 获取指定的资源id
|
||||||
|
SkinResources.getInstance().getIdentifier(resId);
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 手动控制切换皮肤
|
||||||
|
```java
|
||||||
|
Skin skin = new Skin(
|
||||||
|
// 皮肤包 MD5 用于文件损坏校验的
|
||||||
|
"d5493244467d3970834e42dc1a6f07c9",
|
||||||
|
// 皮肤文件名称
|
||||||
|
"app-skin-debug.skin",
|
||||||
|
// 外网可访问的文件服务器地址
|
||||||
|
"https://carlife-static-1255510688.cos.ap-beijing.myqcloud.com/MoGoEagleEye/app-skin-debug.skin");
|
||||||
|
|
||||||
|
// 换肤
|
||||||
|
SkinManager.getInstance().selectSkin(this, skin);
|
||||||
|
|
||||||
|
// 还原
|
||||||
|
SkinManager.getInstance().loadSkin("");
|
||||||
|
```
|
||||||
37
libraries/mogo-skin/build.gradle
Normal file
37
libraries/mogo-skin/build.gradle
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
apply plugin: 'com.android.library'
|
||||||
|
apply plugin: 'kotlin-android'
|
||||||
|
apply plugin: 'kotlin-android-extensions'
|
||||||
|
|
||||||
|
android {
|
||||||
|
compileSdkVersion rootProject.ext.android.compileSdkVersion
|
||||||
|
|
||||||
|
defaultConfig {
|
||||||
|
minSdkVersion rootProject.ext.android.minSdkVersion
|
||||||
|
targetSdkVersion rootProject.ext.android.targetSdkVersion
|
||||||
|
|
||||||
|
versionCode 1
|
||||||
|
versionName "${MOGO_REALTIME_VERSION}"
|
||||||
|
|
||||||
|
consumerProguardFiles "consumer-rules.pro"
|
||||||
|
}
|
||||||
|
|
||||||
|
buildTypes {
|
||||||
|
release {
|
||||||
|
minifyEnabled false
|
||||||
|
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
compileOptions {
|
||||||
|
sourceCompatibility JavaVersion.VERSION_1_8
|
||||||
|
targetCompatibility JavaVersion.VERSION_1_8
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
implementation fileTree(dir: "libs", include: ["*.jar"])
|
||||||
|
implementation rootProject.ext.dependencies.androidxappcompat
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
apply from: new File(rootProject.rootDir, "gradle/upload.gradle").toString()
|
||||||
0
libraries/mogo-skin/consumer-rules.pro
Normal file
0
libraries/mogo-skin/consumer-rules.pro
Normal file
4
libraries/mogo-skin/gradle.properties
Normal file
4
libraries/mogo-skin/gradle.properties
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
GROUP=com.mogo.cloud
|
||||||
|
POM_ARTIFACT_ID=skin
|
||||||
|
VERSION_CODE=1
|
||||||
|
VERSION_NAME=1.0.1-SNAPSHOT
|
||||||
21
libraries/mogo-skin/proguard-rules.pro
vendored
Normal file
21
libraries/mogo-skin/proguard-rules.pro
vendored
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
# 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
|
||||||
9
libraries/mogo-skin/src/main/AndroidManifest.xml
Normal file
9
libraries/mogo-skin/src/main/AndroidManifest.xml
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
package="com.mogo.skin">
|
||||||
|
|
||||||
|
<uses-permission android:name="android.permission.INTERNET" />
|
||||||
|
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
|
||||||
|
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
|
||||||
|
|
||||||
|
</manifest>
|
||||||
45
libraries/mogo-skin/src/main/java/com/mogo/skin/Skin.java
Normal file
45
libraries/mogo-skin/src/main/java/com/mogo/skin/Skin.java
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
package com.mogo.skin;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* donghongyu
|
||||||
|
*/
|
||||||
|
public class Skin {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 文件校验md5值
|
||||||
|
*/
|
||||||
|
public String md5 = "";
|
||||||
|
/**
|
||||||
|
* 下载地址
|
||||||
|
*/
|
||||||
|
public String url = "xxxx";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 皮肤名
|
||||||
|
*/
|
||||||
|
public String name = "";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 下载完成后缓存地址
|
||||||
|
*/
|
||||||
|
public String path = "";
|
||||||
|
|
||||||
|
public File file;
|
||||||
|
|
||||||
|
public Skin(String md5, String name, String url) {
|
||||||
|
this.md5 = md5;
|
||||||
|
this.name = name;
|
||||||
|
this.url = url;
|
||||||
|
}
|
||||||
|
|
||||||
|
public File getSkinFile(File theme) {
|
||||||
|
if (null == file) {
|
||||||
|
file = new File(theme, name);
|
||||||
|
}
|
||||||
|
path = file.getAbsolutePath();
|
||||||
|
return file;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,94 @@
|
|||||||
|
package com.mogo.skin;
|
||||||
|
|
||||||
|
import android.app.Activity;
|
||||||
|
import android.app.Application;
|
||||||
|
import android.graphics.Typeface;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.util.Log;
|
||||||
|
import android.view.LayoutInflater;
|
||||||
|
|
||||||
|
|
||||||
|
import androidx.core.view.LayoutInflaterCompat;
|
||||||
|
|
||||||
|
import com.mogo.skin.utils.SkinThemeUtils;
|
||||||
|
|
||||||
|
import java.lang.reflect.Field;
|
||||||
|
import java.util.HashMap;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* donghongyu
|
||||||
|
*/
|
||||||
|
public class SkinActivityLifecycle implements Application.ActivityLifecycleCallbacks {
|
||||||
|
|
||||||
|
private static final String TAG = "SkinActivityLifecycle";
|
||||||
|
|
||||||
|
HashMap<Activity, SkinLayoutFactory> mLayoutFactoryMap = new HashMap<>();
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onActivityCreated(Activity activity, Bundle savedInstanceState) {
|
||||||
|
|
||||||
|
Log.d(TAG, "onActivityCreated() called with: activity = [" + activity + "], savedInstanceState = [" + savedInstanceState + "]");
|
||||||
|
|
||||||
|
/* 更新状态栏 */
|
||||||
|
SkinThemeUtils.updateStatusBar(activity);
|
||||||
|
|
||||||
|
/* 字体 */
|
||||||
|
Typeface typeface = SkinThemeUtils.getSkinTypeface(activity);
|
||||||
|
|
||||||
|
LayoutInflater layoutInflater = LayoutInflater.from(activity);
|
||||||
|
//获得Activity的布局加载器
|
||||||
|
try {
|
||||||
|
//Android 布局加载器 使用 mFactorySet 标记是否设置过Factory
|
||||||
|
//如设置过抛出一次
|
||||||
|
//设置 mFactorySet 标签为false
|
||||||
|
Field field = LayoutInflater.class.getDeclaredField("mFactorySet");
|
||||||
|
field.setAccessible(true);
|
||||||
|
field.setBoolean(layoutInflater, false);
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
SkinLayoutFactory skinLayoutFactory = new SkinLayoutFactory(activity, typeface);
|
||||||
|
LayoutInflaterCompat.setFactory2(layoutInflater, skinLayoutFactory);
|
||||||
|
//注册观察者
|
||||||
|
SkinManager.getInstance().addObserver(skinLayoutFactory);
|
||||||
|
mLayoutFactoryMap.put(activity, skinLayoutFactory);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onActivityStarted(Activity activity) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onActivityResumed(Activity activity) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onActivityPaused(Activity activity) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onActivityStopped(Activity activity) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onActivitySaveInstanceState(Activity activity, Bundle outState) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onActivityDestroyed(Activity activity) {
|
||||||
|
//删除观察者
|
||||||
|
SkinLayoutFactory skinLayoutFactory = mLayoutFactoryMap.remove(activity);
|
||||||
|
SkinManager.getInstance().deleteObserver(skinLayoutFactory);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void updateSkin(Activity activity) {
|
||||||
|
SkinLayoutFactory skinLayoutFactory = mLayoutFactoryMap.get(activity);
|
||||||
|
skinLayoutFactory.update(null, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,255 @@
|
|||||||
|
package com.mogo.skin;
|
||||||
|
|
||||||
|
import android.graphics.Typeface;
|
||||||
|
import android.graphics.drawable.ColorDrawable;
|
||||||
|
import android.graphics.drawable.Drawable;
|
||||||
|
import android.os.Handler;
|
||||||
|
import android.os.Looper;
|
||||||
|
import android.util.AttributeSet;
|
||||||
|
import android.util.Log;
|
||||||
|
import android.view.View;
|
||||||
|
import android.widget.EditText;
|
||||||
|
import android.widget.ImageView;
|
||||||
|
import android.widget.TextView;
|
||||||
|
|
||||||
|
import androidx.core.view.ViewCompat;
|
||||||
|
|
||||||
|
import com.mogo.skin.utils.SkinResources;
|
||||||
|
import com.mogo.skin.utils.SkinThemeUtils;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* donghongyu
|
||||||
|
*/
|
||||||
|
public class SkinAttribute {
|
||||||
|
|
||||||
|
private static final List<String> mAttributes = new ArrayList<>();
|
||||||
|
|
||||||
|
static {
|
||||||
|
mAttributes.add("background");
|
||||||
|
mAttributes.add("src");
|
||||||
|
|
||||||
|
mAttributes.add("textColor");
|
||||||
|
mAttributes.add("textColorHint");
|
||||||
|
mAttributes.add("drawableLeft");
|
||||||
|
mAttributes.add("drawableTop");
|
||||||
|
mAttributes.add("drawableRight");
|
||||||
|
mAttributes.add("drawableBottom");
|
||||||
|
|
||||||
|
mAttributes.add("skinTypeface");
|
||||||
|
|
||||||
|
mAttributes.add("text");
|
||||||
|
mAttributes.add("hint");
|
||||||
|
}
|
||||||
|
|
||||||
|
private Typeface typeface;
|
||||||
|
|
||||||
|
private static final String TAG = "SkinAttribute";
|
||||||
|
|
||||||
|
List<SkinView> mSkinViews = new ArrayList<>();
|
||||||
|
|
||||||
|
public SkinAttribute(Typeface typeface) {
|
||||||
|
this.typeface = typeface;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void load(View view, AttributeSet attrs) {
|
||||||
|
List<SkinPair> skinPairs = new ArrayList<>();
|
||||||
|
for (int i = 0; i < attrs.getAttributeCount(); i++) {
|
||||||
|
//获得属性名
|
||||||
|
String attributeName = attrs.getAttributeName(i);
|
||||||
|
//是否符合 需要筛选的属性名
|
||||||
|
if (mAttributes.contains(attributeName)) {
|
||||||
|
String attributeValue = attrs.getAttributeValue(i);
|
||||||
|
//写死了,不管了
|
||||||
|
if (attributeValue.startsWith("#")) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
//资源id
|
||||||
|
int resId = 0;
|
||||||
|
if (attributeValue.startsWith("?")) {
|
||||||
|
//attr Id
|
||||||
|
int attrId = Integer.parseInt(attributeValue.substring(1));
|
||||||
|
//获得 主题 style 中的 对应 attr 的资源id值
|
||||||
|
resId = SkinThemeUtils.getResId(view.getContext(), new int[]{attrId})[0];
|
||||||
|
} else {
|
||||||
|
try {
|
||||||
|
// @12343455332
|
||||||
|
if (isNumeric(attributeValue.substring(1))) {
|
||||||
|
resId = Integer.parseInt(attributeValue.substring(1));
|
||||||
|
}
|
||||||
|
} catch (NumberFormatException e) {
|
||||||
|
// 由于有时候TextView会直接配置文字,而不是用 @string/XXX 所以会导致这里转换异常,车里简单的处理异常跳过即可
|
||||||
|
//e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (resId != 0) {
|
||||||
|
//可以被替换的属性
|
||||||
|
SkinPair skinPair = new SkinPair(attributeName, resId);
|
||||||
|
skinPairs.add(skinPair);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//将View与之对应的可以动态替换的属性集合 放入 集合中
|
||||||
|
if (!skinPairs.isEmpty() || view instanceof TextView || view instanceof SkinViewSupport) {
|
||||||
|
SkinView skinView = new SkinView(view, skinPairs);
|
||||||
|
skinView.applySkin(typeface);
|
||||||
|
mSkinViews.add(skinView);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean isNumeric(String str) {
|
||||||
|
if (str == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return str.matches("-?\\d+(\\.\\d+)?"); // 匹配整数或小数
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 换皮肤
|
||||||
|
*/
|
||||||
|
public void applySkin(Typeface typeface) {
|
||||||
|
for (SkinView mSkinView : mSkinViews) {
|
||||||
|
mSkinView.applySkin(typeface);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static class SkinView {
|
||||||
|
|
||||||
|
View view;
|
||||||
|
List<SkinPair> skinPairs;
|
||||||
|
|
||||||
|
public SkinView(View view, List<SkinPair> skinPairs) {
|
||||||
|
this.view = view;
|
||||||
|
this.skinPairs = skinPairs;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param typeface 字体
|
||||||
|
*/
|
||||||
|
public void applySkin(Typeface typeface) {
|
||||||
|
applySkinTypeface(typeface);
|
||||||
|
applySkinViewSupport();
|
||||||
|
for (SkinPair skinPair : skinPairs) {
|
||||||
|
Drawable left = null, top = null, right = null, bottom = null;
|
||||||
|
String textStr;
|
||||||
|
switch (skinPair.attributeName) {
|
||||||
|
case "background":
|
||||||
|
Object background = SkinResources.getInstance().getBackground(skinPair.resId);
|
||||||
|
//Color
|
||||||
|
if (background instanceof Integer) {
|
||||||
|
view.setBackgroundColor((Integer) background);
|
||||||
|
} else {
|
||||||
|
ViewCompat.setBackground(view, (Drawable) background);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case "src":
|
||||||
|
background = SkinResources.getInstance().getBackground(skinPair.resId);
|
||||||
|
if (background instanceof Integer) {
|
||||||
|
((ImageView) view).setImageDrawable(new ColorDrawable((Integer) background));
|
||||||
|
} else {
|
||||||
|
((ImageView) view).setImageDrawable((Drawable) background);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case "textColor":
|
||||||
|
((TextView) view).setTextColor(SkinResources.getInstance().getColorStateList(skinPair.resId));
|
||||||
|
break;
|
||||||
|
case "textColorHint":
|
||||||
|
((EditText) view).setHintTextColor(SkinResources.getInstance().getColorStateList(skinPair.resId));
|
||||||
|
break;
|
||||||
|
case "drawableLeft":
|
||||||
|
left = SkinResources.getInstance().getDrawable(skinPair.resId);
|
||||||
|
break;
|
||||||
|
case "drawableTop":
|
||||||
|
top = SkinResources.getInstance().getDrawable(skinPair.resId);
|
||||||
|
break;
|
||||||
|
case "drawableRight":
|
||||||
|
right = SkinResources.getInstance().getDrawable(skinPair.resId);
|
||||||
|
break;
|
||||||
|
case "drawableBottom":
|
||||||
|
bottom = SkinResources.getInstance().getDrawable(skinPair.resId);
|
||||||
|
break;
|
||||||
|
case "skinTypeface":
|
||||||
|
Typeface typeface1 = SkinResources.getInstance().getTypeface(skinPair.resId);
|
||||||
|
applySkinTypeface(typeface1);
|
||||||
|
break;
|
||||||
|
case "text":
|
||||||
|
textStr = SkinResources.getInstance().getString(skinPair.resId);
|
||||||
|
applyText(textStr);
|
||||||
|
break;
|
||||||
|
case "hint":
|
||||||
|
textStr = SkinResources.getInstance().getString(skinPair.resId);
|
||||||
|
applyHintText(textStr);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (null != left || null != right || null != top || null != bottom) {
|
||||||
|
((TextView) view).setCompoundDrawablesWithIntrinsicBounds(left, top, right, bottom);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Handler mHandler = new Handler(Looper.getMainLooper());
|
||||||
|
|
||||||
|
private void applySkinViewSupport() {
|
||||||
|
if (view instanceof SkinViewSupport) {
|
||||||
|
((SkinViewSupport) view).applySkin();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void applySkinTypeface(final Typeface typeface) {
|
||||||
|
if (view instanceof TextView) {
|
||||||
|
Log.d(TAG, "applySkinTypeface() called with: view = [" + ((TextView) view).getText() + "] Parent = " + view.getParent());
|
||||||
|
//post 防止某些控件的属性设置是在构造函数调用完成之后进行的。
|
||||||
|
mHandler.post(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
((TextView) view).setTypeface(typeface);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void applyText(final String textStr) {
|
||||||
|
if (view instanceof TextView) {
|
||||||
|
Log.d(TAG, "applyText() called with: view = [" + ((TextView) view).getText() + "] Parent = " + view.getParent());
|
||||||
|
//post 防止某些控件的属性设置是在构造函数调用完成之后进行的。
|
||||||
|
mHandler.post(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
((TextView) view).setText(textStr);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void applyHintText(final String textStr) {
|
||||||
|
if (view instanceof EditText) {
|
||||||
|
Log.d(TAG, "applyHintText() called with: view = [" + ((TextView) view).getText() + "] Parent = " + view.getParent());
|
||||||
|
//post 防止某些控件的属性设置是在构造函数调用完成之后进行的。
|
||||||
|
mHandler.post(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
((EditText) view).setHint(textStr);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
static class SkinPair {
|
||||||
|
|
||||||
|
String attributeName;
|
||||||
|
int resId;
|
||||||
|
|
||||||
|
public SkinPair(String attributeName, int resId) {
|
||||||
|
this.attributeName = attributeName;
|
||||||
|
this.resId = resId;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,106 @@
|
|||||||
|
package com.mogo.skin;
|
||||||
|
|
||||||
|
import android.app.Activity;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.graphics.Typeface;
|
||||||
|
import android.util.AttributeSet;
|
||||||
|
import android.view.LayoutInflater;
|
||||||
|
import android.view.View;
|
||||||
|
|
||||||
|
import com.mogo.skin.utils.SkinThemeUtils;
|
||||||
|
|
||||||
|
import java.lang.reflect.Constructor;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Observable;
|
||||||
|
import java.util.Observer;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* donghongyu
|
||||||
|
*/
|
||||||
|
public class SkinLayoutFactory implements LayoutInflater.Factory2, Observer {
|
||||||
|
|
||||||
|
private static final String[] mClassPrefixList = {
|
||||||
|
"android.widget.",
|
||||||
|
"android.view.",
|
||||||
|
"android.webkit."
|
||||||
|
};
|
||||||
|
|
||||||
|
private static final Class<?>[] mConstructorSignature = new Class[]{Context.class, AttributeSet.class};
|
||||||
|
|
||||||
|
private static final HashMap<String, Constructor<? extends View>> sConstructorMap = new HashMap<>();
|
||||||
|
|
||||||
|
private Activity activity;
|
||||||
|
|
||||||
|
// 属性处理类
|
||||||
|
SkinAttribute skinAttribute;
|
||||||
|
|
||||||
|
public SkinLayoutFactory(Activity activity, Typeface typeface) {
|
||||||
|
this.activity = activity;
|
||||||
|
skinAttribute = new SkinAttribute(typeface);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final String TAG = "SkinLayoutFactory";
|
||||||
|
|
||||||
|
private int count = 0;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public View onCreateView(View parent, String name, Context context, AttributeSet attrs) {
|
||||||
|
//反射 classloader
|
||||||
|
View view = createViewFromTag(name, context, attrs);
|
||||||
|
// 自定义View
|
||||||
|
if (null == view) {
|
||||||
|
view = createView(name, context, attrs);
|
||||||
|
}
|
||||||
|
//筛选符合属性的View
|
||||||
|
skinAttribute.load(view, attrs);
|
||||||
|
return view;
|
||||||
|
}
|
||||||
|
|
||||||
|
private View createViewFromTag(String name, Context context, AttributeSet attrs) {
|
||||||
|
//包含了 . 自定义控件
|
||||||
|
if (-1 != name.indexOf(".")) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
View view = null;
|
||||||
|
for (int i = 0; i < mClassPrefixList.length; i++) {
|
||||||
|
view = createView(mClassPrefixList[i] + name, context, attrs);
|
||||||
|
if (null != view) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return view;
|
||||||
|
}
|
||||||
|
|
||||||
|
private View createView(String name, Context context, AttributeSet attrs) {
|
||||||
|
Constructor<? extends View> constructor = sConstructorMap.get(name);
|
||||||
|
if (null == constructor) {
|
||||||
|
try {
|
||||||
|
Class<? extends View> aClass = context.getClassLoader().loadClass(name).asSubclass(View.class);
|
||||||
|
constructor = aClass.getConstructor(mConstructorSignature);
|
||||||
|
sConstructorMap.put(name, constructor);
|
||||||
|
} catch (Exception e) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (null != constructor) {
|
||||||
|
try {
|
||||||
|
return constructor.newInstance(context, attrs);
|
||||||
|
} catch (Exception e) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public View onCreateView(String name, Context context, AttributeSet attrs) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void update(Observable o, Object arg) {
|
||||||
|
SkinThemeUtils.updateStatusBar(activity);
|
||||||
|
Typeface typeface = SkinThemeUtils.getSkinTypeface(activity);
|
||||||
|
// 更换皮肤
|
||||||
|
skinAttribute.applySkin(typeface);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
173
libraries/mogo-skin/src/main/java/com/mogo/skin/SkinManager.java
Normal file
173
libraries/mogo-skin/src/main/java/com/mogo/skin/SkinManager.java
Normal file
@@ -0,0 +1,173 @@
|
|||||||
|
package com.mogo.skin;
|
||||||
|
|
||||||
|
import android.app.Activity;
|
||||||
|
import android.app.Application;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.pm.PackageInfo;
|
||||||
|
import android.content.pm.PackageManager;
|
||||||
|
import android.content.res.AssetManager;
|
||||||
|
import android.content.res.Resources;
|
||||||
|
import android.text.TextUtils;
|
||||||
|
import android.util.Log;
|
||||||
|
|
||||||
|
import com.mogo.skin.net.DownloadManager;
|
||||||
|
import com.mogo.skin.utils.FileUtils;
|
||||||
|
import com.mogo.skin.utils.SkinPreference;
|
||||||
|
import com.mogo.skin.utils.SkinResources;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.lang.reflect.Method;
|
||||||
|
import java.util.Observable;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* donghongyu
|
||||||
|
*/
|
||||||
|
public class SkinManager extends Observable {
|
||||||
|
|
||||||
|
private static String TAG = "SkinManager";
|
||||||
|
|
||||||
|
private static Application mApplication;
|
||||||
|
private static SkinManager instance;
|
||||||
|
private SkinActivityLifecycle skinActivityLifecycle;
|
||||||
|
private Application application;
|
||||||
|
|
||||||
|
public static void init(Application application) {
|
||||||
|
synchronized (SkinManager.class) {
|
||||||
|
if (null == instance) {
|
||||||
|
mApplication = application;
|
||||||
|
instance = new SkinManager(application);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static SkinManager getInstance() {
|
||||||
|
return instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
private SkinManager(Application application) {
|
||||||
|
this.application = application;
|
||||||
|
SkinPreference.init(application);
|
||||||
|
SkinResources.init(application);
|
||||||
|
//注册Activity生命周期回调
|
||||||
|
skinActivityLifecycle = new SkinActivityLifecycle();
|
||||||
|
application.registerActivityLifecycleCallbacks(skinActivityLifecycle);
|
||||||
|
loadSkin(SkinPreference.getInstance().getSkin());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 加载皮肤包 并 更新
|
||||||
|
*
|
||||||
|
* @param path 皮肤包路径
|
||||||
|
*/
|
||||||
|
public void loadSkin(String path) {
|
||||||
|
Log.d(TAG, "加载皮肤路径:" + path);
|
||||||
|
//还原默认皮肤包
|
||||||
|
if (TextUtils.isEmpty(path)) {
|
||||||
|
SkinPreference.getInstance().setSkin("");
|
||||||
|
SkinResources.getInstance().reset();
|
||||||
|
}
|
||||||
|
// 加载指定目录下的皮肤
|
||||||
|
else {
|
||||||
|
try {
|
||||||
|
AssetManager assetManager = AssetManager.class.newInstance();
|
||||||
|
// 添加资源进入资源管理器
|
||||||
|
Method addAssetPath = assetManager.getClass().getMethod("addAssetPath", String.class);
|
||||||
|
addAssetPath.setAccessible(true);
|
||||||
|
addAssetPath.invoke(assetManager, path);
|
||||||
|
|
||||||
|
Resources resources = application.getResources();
|
||||||
|
// 横竖、语言
|
||||||
|
Resources skinResource = new Resources(assetManager, resources.getDisplayMetrics(), resources.getConfiguration());
|
||||||
|
//获取外部Apk(皮肤包) 包名
|
||||||
|
PackageManager mPm = application.getPackageManager();
|
||||||
|
PackageInfo info = mPm.getPackageArchiveInfo(path, PackageManager.GET_ACTIVITIES);
|
||||||
|
String packageName = info.packageName;
|
||||||
|
SkinResources.getInstance().applySkin(skinResource, packageName);
|
||||||
|
//保存当前使用的皮肤包
|
||||||
|
SkinPreference.getInstance().setSkin(path);
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//应用皮肤包
|
||||||
|
setChanged();
|
||||||
|
//通知观察者
|
||||||
|
notifyObservers();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void updateSkin(Activity activity) {
|
||||||
|
skinActivityLifecycle.updateSkin(activity);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 下载皮肤包
|
||||||
|
*/
|
||||||
|
public void selectSkin(Context context, Skin skin) {
|
||||||
|
File theme = new File(FileUtils.getExternalAppDirectory(context), "theme");
|
||||||
|
if (theme.exists() && theme.isFile()) {
|
||||||
|
theme.delete();
|
||||||
|
}
|
||||||
|
theme.mkdirs();
|
||||||
|
File skinFile = skin.getSkinFile(theme);
|
||||||
|
if (skinFile.exists()) {
|
||||||
|
Log.e("SkinActivity", "皮肤已存在,开始换肤……");
|
||||||
|
SkinManager.getInstance().loadSkin(skin.path);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Log.e("SkinActivity", "皮肤不存在,开始下载……");
|
||||||
|
DownloadManager manager = getDownloadManager(skin, theme);
|
||||||
|
manager.startDownload();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private static DownloadManager getDownloadManager(Skin skin, File theme) {
|
||||||
|
DownloadManager.DownloadListener downloadListener = new DownloadManager.DownloadListener() {
|
||||||
|
@Override
|
||||||
|
public void onProgressUpdate(int progress) {
|
||||||
|
// 更新UI或日志
|
||||||
|
Log.d("DownloadManager", "onProgressUpdate progress = " + progress);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onDownloadSuccess() {
|
||||||
|
// 下载成功后的处理
|
||||||
|
Log.d("DownloadManager", "onDownloadSuccess");
|
||||||
|
File skinFile = skin.getSkinFile(theme);
|
||||||
|
try {
|
||||||
|
//下载成功,将皮肤包信息insert已下载数据库
|
||||||
|
Log.e("SkinActivity", "皮肤包下载完成开始校验");
|
||||||
|
//皮肤包的md5校验 防止下载文件损坏(但是会减慢速度。从数据库查询已下载皮肤表数据库中保留md5字段)
|
||||||
|
if (TextUtils.equals(SkinUtils.getSkinMD5(skinFile), skin.md5)) {
|
||||||
|
Log.d("SkinActivity", "校验成功,修改文件名。");
|
||||||
|
|
||||||
|
} else {
|
||||||
|
Log.e("SkinActivity", "校验出错,本地文件MD5 与云端文件MD5 不一致。");
|
||||||
|
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
SkinManager.getInstance().loadSkin(skin.path);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onDownloadFailed(Exception e) {
|
||||||
|
// 下载失败后的处理
|
||||||
|
Log.e("DownloadManager", "onDownloadFailed");
|
||||||
|
e.printStackTrace();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onAlreadyDownloading(String url) {
|
||||||
|
// 处理重复下载的情况
|
||||||
|
Log.w("DownloadManager", "onAlreadyDownloading url=" + url);
|
||||||
|
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return new DownloadManager(skin.url, theme, downloadListener);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,48 @@
|
|||||||
|
package com.mogo.skin;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.FileInputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.math.BigInteger;
|
||||||
|
import java.security.MessageDigest;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* donghongyu
|
||||||
|
*/
|
||||||
|
public class SkinUtils {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取一个文件的md5值(可处理大文件)
|
||||||
|
*
|
||||||
|
* @return md5 value
|
||||||
|
*/
|
||||||
|
public static String getSkinMD5(File file) {
|
||||||
|
FileInputStream fis = null;
|
||||||
|
BigInteger bi = null;
|
||||||
|
try {
|
||||||
|
MessageDigest MD5 = MessageDigest.getInstance("MD5");
|
||||||
|
fis = new FileInputStream(file);
|
||||||
|
byte[] buffer = new byte[10240];
|
||||||
|
int length;
|
||||||
|
while ((length = fis.read(buffer)) != -1) {
|
||||||
|
MD5.update(buffer, 0, length);
|
||||||
|
}
|
||||||
|
byte[] digest = MD5.digest();
|
||||||
|
bi = new BigInteger(1, digest);
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
return null;
|
||||||
|
} finally {
|
||||||
|
try {
|
||||||
|
if (fis != null) {
|
||||||
|
fis.close();
|
||||||
|
}
|
||||||
|
} catch (IOException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return bi.toString(16);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
package com.mogo.skin;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* donghongyu
|
||||||
|
*/
|
||||||
|
public interface SkinViewSupport {
|
||||||
|
|
||||||
|
void applySkin();
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,131 @@
|
|||||||
|
package com.mogo.skin.net;
|
||||||
|
|
||||||
|
import android.os.AsyncTask;
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.io.RandomAccessFile;
|
||||||
|
import java.net.HttpURLConnection;
|
||||||
|
import java.net.URL;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
public class DownloadManager {
|
||||||
|
private static final int BUFFER_SIZE = 1024 * 1024; // 1MB
|
||||||
|
private static final Map<String, DownloadTask> activeDownloads = new HashMap<>();
|
||||||
|
private String downloadUrl;
|
||||||
|
private File outputDir;
|
||||||
|
private File outputFile;
|
||||||
|
private DownloadListener listener;
|
||||||
|
private long downloadedLength;
|
||||||
|
private boolean isCancelled = false;
|
||||||
|
|
||||||
|
public interface DownloadListener {
|
||||||
|
void onProgressUpdate(int progress);
|
||||||
|
void onDownloadSuccess();
|
||||||
|
void onDownloadFailed(Exception e);
|
||||||
|
void onAlreadyDownloading(String url);
|
||||||
|
}
|
||||||
|
|
||||||
|
public DownloadManager(String url, File directory, DownloadListener listener) {
|
||||||
|
this.downloadUrl = url;
|
||||||
|
this.outputDir = directory;
|
||||||
|
this.listener = listener;
|
||||||
|
this.outputFile = new File(outputDir, getFileNameFromUrl(url));
|
||||||
|
|
||||||
|
// 确保输出目录存在
|
||||||
|
if (!outputDir.exists()) {
|
||||||
|
outputDir.mkdirs();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 如果文件不存在,则创建空文件
|
||||||
|
if (!outputFile.exists()) {
|
||||||
|
try {
|
||||||
|
outputFile.createNewFile();
|
||||||
|
} catch (IOException e) {
|
||||||
|
if (listener != null) {
|
||||||
|
listener.onDownloadFailed(e);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this.downloadedLength = outputFile.length();
|
||||||
|
}
|
||||||
|
|
||||||
|
private String getFileNameFromUrl(String url) {
|
||||||
|
return url.substring(url.lastIndexOf('/') + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void startDownload() {
|
||||||
|
if (activeDownloads.containsKey(downloadUrl)) {
|
||||||
|
if (listener != null) {
|
||||||
|
listener.onAlreadyDownloading(downloadUrl);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
DownloadTask task = new DownloadTask();
|
||||||
|
activeDownloads.put(downloadUrl, task);
|
||||||
|
task.execute(downloadUrl);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void cancelDownload() {
|
||||||
|
isCancelled = true;
|
||||||
|
DownloadTask task = activeDownloads.get(downloadUrl);
|
||||||
|
if (task != null) {
|
||||||
|
task.cancel(true);
|
||||||
|
activeDownloads.remove(downloadUrl);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class DownloadTask extends AsyncTask<String, Integer, Boolean> {
|
||||||
|
@Override
|
||||||
|
protected Boolean doInBackground(String... urls) {
|
||||||
|
try (RandomAccessFile raf = new RandomAccessFile(outputFile, "rw")) {
|
||||||
|
URL url = new URL(urls[0]);
|
||||||
|
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
|
||||||
|
connection.setRequestMethod("GET");
|
||||||
|
connection.setConnectTimeout(15000); // 15 seconds
|
||||||
|
connection.setReadTimeout(15000); // 15 seconds
|
||||||
|
if (downloadedLength > 0) {
|
||||||
|
connection.setRequestProperty("Range", "bytes=" + downloadedLength + "-");
|
||||||
|
}
|
||||||
|
InputStream in = connection.getInputStream();
|
||||||
|
byte[] buffer = new byte[BUFFER_SIZE];
|
||||||
|
int read;
|
||||||
|
while ((read = in.read(buffer)) != -1 && !isCancelled) {
|
||||||
|
raf.write(buffer, 0, read);
|
||||||
|
downloadedLength += read;
|
||||||
|
publishProgress((int) (downloadedLength * 100 / connection.getContentLengthLong()));
|
||||||
|
}
|
||||||
|
return !isCancelled;
|
||||||
|
} catch (IOException e) {
|
||||||
|
if (!isCancelled) {
|
||||||
|
if (listener != null) {
|
||||||
|
listener.onDownloadFailed(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
} finally {
|
||||||
|
activeDownloads.remove(downloadUrl);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onProgressUpdate(Integer... values) {
|
||||||
|
super.onProgressUpdate(values);
|
||||||
|
if (listener != null) {
|
||||||
|
listener.onProgressUpdate(values[0]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onPostExecute(Boolean result) {
|
||||||
|
super.onPostExecute(result);
|
||||||
|
if (result && listener != null) {
|
||||||
|
listener.onDownloadSuccess();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
package com.mogo.skin.utils;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import java.io.File;
|
||||||
|
|
||||||
|
public class FileUtils {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取应用特定的外部存储目录。
|
||||||
|
*
|
||||||
|
* @param context 应用上下文
|
||||||
|
* @return 应用特定的外部存储目录
|
||||||
|
*/
|
||||||
|
public static File getExternalAppDirectory(Context context) {
|
||||||
|
// 获取应用特定的外部存储目录
|
||||||
|
File externalFilesDir = context.getExternalFilesDir(null);
|
||||||
|
if (externalFilesDir == null) {
|
||||||
|
// 如果外部存储不可用,可以考虑使用内部存储或其他逻辑
|
||||||
|
return context.getFilesDir();
|
||||||
|
}
|
||||||
|
return externalFilesDir;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取应用特定的外部存储目录下的子目录。
|
||||||
|
*
|
||||||
|
* @param context 应用上下文
|
||||||
|
* @param subDir 子目录名称
|
||||||
|
* @return 应用特定的外部存储目录下的子目录
|
||||||
|
*/
|
||||||
|
public static File getExternalAppSubDirectory(Context context, String subDir) {
|
||||||
|
// 获取应用特定的外部存储目录
|
||||||
|
File externalFilesDir = getExternalAppDirectory(context);
|
||||||
|
// 创建并返回子目录
|
||||||
|
return new File(externalFilesDir, subDir);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,43 @@
|
|||||||
|
package com.mogo.skin.utils;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.SharedPreferences;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* donghongyu
|
||||||
|
*/
|
||||||
|
public class SkinPreference {
|
||||||
|
|
||||||
|
private static final String SKIN_SHARED = "skins";
|
||||||
|
private static final String KEY_SKIN_PATH = "skin-path";
|
||||||
|
|
||||||
|
private static SkinPreference instance;
|
||||||
|
private final SharedPreferences mPref;
|
||||||
|
|
||||||
|
public static void init(Context context) {
|
||||||
|
if (instance == null) {
|
||||||
|
synchronized (SkinPreference.class) {
|
||||||
|
if (instance == null) {
|
||||||
|
instance = new SkinPreference(context.getApplicationContext());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static SkinPreference getInstance() {
|
||||||
|
return instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
private SkinPreference(Context context) {
|
||||||
|
mPref = context.getSharedPreferences(SKIN_SHARED, Context.MODE_PRIVATE);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setSkin(String skinPath) {
|
||||||
|
mPref.edit().putString(KEY_SKIN_PATH, skinPath).apply();
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getSkin() {
|
||||||
|
return mPref.getString(KEY_SKIN_PATH, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,170 @@
|
|||||||
|
package com.mogo.skin.utils;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.res.ColorStateList;
|
||||||
|
import android.content.res.Resources;
|
||||||
|
import android.graphics.Typeface;
|
||||||
|
import android.graphics.drawable.Drawable;
|
||||||
|
import android.text.TextUtils;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* donghongyu
|
||||||
|
*/
|
||||||
|
public class SkinResources {
|
||||||
|
|
||||||
|
private static SkinResources instance;
|
||||||
|
|
||||||
|
private Resources mSkinResources;
|
||||||
|
private String mSkinPkgName;
|
||||||
|
private boolean isDefaultSkin = true;
|
||||||
|
|
||||||
|
private Resources mAppResources;
|
||||||
|
|
||||||
|
private SkinResources(Context context) {
|
||||||
|
mAppResources = context.getResources();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void init(Context context) {
|
||||||
|
if (instance == null) {
|
||||||
|
synchronized (SkinResources.class) {
|
||||||
|
if (instance == null) {
|
||||||
|
instance = new SkinResources(context);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static SkinResources getInstance() {
|
||||||
|
return instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void reset() {
|
||||||
|
mSkinResources = null;
|
||||||
|
mSkinPkgName = "";
|
||||||
|
isDefaultSkin = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void applySkin(Resources resources, String pkgName) {
|
||||||
|
mSkinResources = resources;
|
||||||
|
mSkinPkgName = pkgName;
|
||||||
|
//是否使用默认皮肤
|
||||||
|
isDefaultSkin = TextUtils.isEmpty(pkgName) || resources == null;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public int getIdentifier(int resId) {
|
||||||
|
if (isDefaultSkin) {
|
||||||
|
return resId;
|
||||||
|
}
|
||||||
|
//在皮肤包中不一定就是 当前程序的 id
|
||||||
|
//获取对应id 在当前的名称 colorPrimary
|
||||||
|
//R.drawable.ic_launcher
|
||||||
|
String resName = mAppResources.getResourceEntryName(resId);//ic_launcher
|
||||||
|
String resType = mAppResources.getResourceTypeName(resId);//drawable
|
||||||
|
int skinId = mSkinResources.getIdentifier(resName, resType, mSkinPkgName);
|
||||||
|
return skinId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getColor(int resId) {
|
||||||
|
if (isDefaultSkin) {
|
||||||
|
return mAppResources.getColor(resId);
|
||||||
|
}
|
||||||
|
int skinId = getIdentifier(resId);
|
||||||
|
if (skinId == 0) {
|
||||||
|
return mAppResources.getColor(resId);
|
||||||
|
}
|
||||||
|
return mSkinResources.getColor(skinId);
|
||||||
|
}
|
||||||
|
|
||||||
|
public ColorStateList getColorStateList(int resId) {
|
||||||
|
if (isDefaultSkin) {
|
||||||
|
return mAppResources.getColorStateList(resId);
|
||||||
|
}
|
||||||
|
int skinId = getIdentifier(resId);
|
||||||
|
if (skinId == 0) {
|
||||||
|
return mAppResources.getColorStateList(resId);
|
||||||
|
}
|
||||||
|
return mSkinResources.getColorStateList(skinId);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Drawable getDrawable(int resId) {
|
||||||
|
//如果有皮肤 isDefaultSkin false 没有就是true
|
||||||
|
if (isDefaultSkin) {
|
||||||
|
return mAppResources.getDrawable(resId);
|
||||||
|
}
|
||||||
|
int skinId = getIdentifier(resId);
|
||||||
|
if (skinId == 0) {
|
||||||
|
return mAppResources.getDrawable(resId);
|
||||||
|
}
|
||||||
|
return mSkinResources.getDrawable(skinId);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 可能是Color 也可能是drawable
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public Object getBackground(int resId) {
|
||||||
|
String resourceTypeName = mAppResources.getResourceTypeName(resId);
|
||||||
|
|
||||||
|
if (resourceTypeName.equals("color")) {
|
||||||
|
return getColor(resId);
|
||||||
|
} else {
|
||||||
|
// drawable
|
||||||
|
return getDrawable(resId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取文字
|
||||||
|
*
|
||||||
|
* @param resId
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public String getString(int resId) {
|
||||||
|
try {
|
||||||
|
if (resId != 0) {
|
||||||
|
if (isDefaultSkin) {
|
||||||
|
return mAppResources.getString(resId);
|
||||||
|
}
|
||||||
|
int skinId = getIdentifier(resId);
|
||||||
|
if (skinId == 0) {
|
||||||
|
return mAppResources.getString(skinId);
|
||||||
|
}
|
||||||
|
return mSkinResources.getString(skinId);
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
} catch (Resources.NotFoundException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获得字体
|
||||||
|
*
|
||||||
|
* @param resId
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public Typeface getTypeface(int resId) {
|
||||||
|
String skinTypefacePath = getString(resId);
|
||||||
|
if (TextUtils.isEmpty(skinTypefacePath)) {
|
||||||
|
return Typeface.DEFAULT;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
Typeface typeface;
|
||||||
|
if (isDefaultSkin) {
|
||||||
|
typeface = Typeface.createFromAsset(mAppResources.getAssets(), skinTypefacePath);
|
||||||
|
return typeface;
|
||||||
|
|
||||||
|
}
|
||||||
|
typeface = Typeface.createFromAsset(mSkinResources.getAssets(), skinTypefacePath);
|
||||||
|
return typeface;
|
||||||
|
} catch (RuntimeException e) {
|
||||||
|
}
|
||||||
|
return Typeface.DEFAULT;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,69 @@
|
|||||||
|
package com.mogo.skin.utils;
|
||||||
|
|
||||||
|
import android.app.Activity;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.res.TypedArray;
|
||||||
|
import android.graphics.Typeface;
|
||||||
|
import android.os.Build;
|
||||||
|
|
||||||
|
import com.mogo.skin.R;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* donghongyu
|
||||||
|
*/
|
||||||
|
public class SkinThemeUtils {
|
||||||
|
|
||||||
|
private static int[] TYPEFACE_ATTR = {
|
||||||
|
R.attr.skinTypeface
|
||||||
|
};
|
||||||
|
|
||||||
|
private static int[] APPCOMPAT_COLOR_PRIMARY_DARK_ATTRS = {
|
||||||
|
R.attr.colorPrimaryDark
|
||||||
|
};
|
||||||
|
|
||||||
|
private static int[] STATUSBAR_COLOR_ATTRS = {android.R.attr.statusBarColor, android.R.attr.navigationBarColor};
|
||||||
|
|
||||||
|
public static int[] getResId(Context context, int[] attrs) {
|
||||||
|
int[] resIds = new int[attrs.length];
|
||||||
|
TypedArray typedArray = context.obtainStyledAttributes(attrs);
|
||||||
|
for (int i = 0; i < typedArray.length(); i++) {
|
||||||
|
resIds[i] = typedArray.getResourceId(i, 0);
|
||||||
|
}
|
||||||
|
typedArray.recycle();
|
||||||
|
return resIds;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void updateStatusBar(Activity activity) {
|
||||||
|
//5.0以上才能修改
|
||||||
|
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
int[] resIds = getResId(activity, STATUSBAR_COLOR_ATTRS);
|
||||||
|
/*
|
||||||
|
* 修改状态栏的颜色
|
||||||
|
*/
|
||||||
|
//如果没有配置 属性 则获得0
|
||||||
|
if (resIds[0] == 0) {
|
||||||
|
int statusBarColorId = getResId(activity, APPCOMPAT_COLOR_PRIMARY_DARK_ATTRS)[0];
|
||||||
|
if (statusBarColorId != 0) {
|
||||||
|
activity.getWindow().setStatusBarColor(SkinResources.getInstance().getColor(statusBarColorId));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
activity.getWindow().setStatusBarColor(SkinResources.getInstance().getColor(resIds[0]));
|
||||||
|
}
|
||||||
|
//修改底部虚拟按键的颜色
|
||||||
|
if (resIds[1] != 0) {
|
||||||
|
activity.getWindow().setNavigationBarColor(SkinResources.getInstance().getColor(resIds[1]));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获得字体
|
||||||
|
*/
|
||||||
|
public static Typeface getSkinTypeface(Activity activity) {
|
||||||
|
int skinTypeceId = getResId(activity, TYPEFACE_ATTR)[0];
|
||||||
|
return SkinResources.getInstance().getTypeface(skinTypeceId);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,58 @@
|
|||||||
|
package com.mogo.skin.widget;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.graphics.drawable.Drawable;
|
||||||
|
import android.util.AttributeSet;
|
||||||
|
|
||||||
|
import androidx.appcompat.widget.AppCompatImageView;
|
||||||
|
|
||||||
|
import com.mogo.skin.SkinViewSupport;
|
||||||
|
import com.mogo.skin.utils.SkinResources;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* donghongyu
|
||||||
|
*/
|
||||||
|
public class SkinImageView extends AppCompatImageView implements SkinViewSupport {
|
||||||
|
// 背景图
|
||||||
|
private int backgroundResId;
|
||||||
|
// 图标
|
||||||
|
private int imageResId;
|
||||||
|
|
||||||
|
public SkinImageView(Context context) {
|
||||||
|
this(context, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public SkinImageView(Context context, AttributeSet attrs) {
|
||||||
|
this(context, attrs, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
public SkinImageView(Context context, AttributeSet attrs, int defStyleAttr) {
|
||||||
|
super(context, attrs, defStyleAttr);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setBackgroundResource(int resId) {
|
||||||
|
backgroundResId = resId;
|
||||||
|
Drawable backgroundRes = SkinResources.getInstance().getDrawable(backgroundResId);
|
||||||
|
super.setBackgroundDrawable(backgroundRes);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setImageResource(int resId) {
|
||||||
|
imageResId = resId;
|
||||||
|
Drawable imageRes = SkinResources.getInstance().getDrawable(imageResId);
|
||||||
|
super.setImageDrawable(imageRes);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void applySkin() {
|
||||||
|
if (backgroundResId != 0) {
|
||||||
|
Drawable backgroundRes = SkinResources.getInstance().getDrawable(backgroundResId);
|
||||||
|
setBackgroundDrawable(backgroundRes);
|
||||||
|
}
|
||||||
|
if (imageResId != 0) {
|
||||||
|
Drawable imageRes = SkinResources.getInstance().getDrawable(imageResId);
|
||||||
|
setImageDrawable(imageRes);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
4
libraries/mogo-skin/src/main/res/values/attrs.xml
Normal file
4
libraries/mogo-skin/src/main/res/values/attrs.xml
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<resources>
|
||||||
|
<attr name="skinTypeface" format="string"/>
|
||||||
|
</resources>
|
||||||
3
libraries/mogo-skin/src/main/res/values/strings.xml
Normal file
3
libraries/mogo-skin/src/main/res/values/strings.xml
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
<resources>
|
||||||
|
<string name="app_name">skin-core2</string>
|
||||||
|
</resources>
|
||||||
@@ -1,3 +1,4 @@
|
|||||||
|
:libraries:mogo-skin
|
||||||
:libraries:mogo-telematic
|
:libraries:mogo-telematic
|
||||||
:foudations:mogo-passport
|
:foudations:mogo-passport
|
||||||
:foudations:mogo-network
|
:foudations:mogo-network
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ include ':modules:mogo-realtime'
|
|||||||
include ':modules:mogo-tanlu'
|
include ':modules:mogo-tanlu'
|
||||||
|
|
||||||
include ':libraries:mogo-telematic'
|
include ':libraries:mogo-telematic'
|
||||||
|
include ':libraries:mogo-skin'
|
||||||
|
|
||||||
include ':app'
|
include ':app'
|
||||||
rootProject.name = "MoGoAiCloudSdk"
|
rootProject.name = "MoGoAiCloudSdk"
|
||||||
|
|||||||
Reference in New Issue
Block a user