mtk 主题功能学习笔记
来源:互联网 发布:泰安广电网络怎么样 编辑:程序博客网 时间:2024/06/05 06:02
1. 如何添加一套自己的主题
这里以Jb3为例:
添加资源
在mediatek/frameworks/themes/ 下面添加一个新的主题文件夹.可以参看原有的主题theme-res-mint/theme-res-mocha/theme-res-raspberry/.
只要将对应图片资源放到对应的文件夹下面就可以了.
系统编译时, theme-res-xxx下面android.mk配置的
$(shell perl $(LOCAL_PATH)/copy_res.pl $(LOCAL_PATH))
会执行copy_res.pl 将theme-res-xxx 下面的资源统一拷贝到theme-res-xxx 下面的res 文件夹下面
如何将添加的主题资源编译到固件里面去
在alps/build/target/product/common.mk文件中,找到MTK_THEMENANAGER_APP这个关键字,在如下这个判断中,添加自己的资源包的名字
ifeq($(strip $(MTK_THEMEMANAGER_APP)),yes)
PRODUCT_PACKAGES += theme-res-mint \
theme-res-mocha \
theme-res-raspberry \
theme-res-xxx
endif
如果需要主题里面需要增加其他资源,比如: mipmap raw 等.
mipmap文件夹下的应用图标可以直接放在themes-res-xxxx相应应用程序的res/mipmap-xxxx下,修改copy-res.pl的 if (substr($mkdirinres, 0, 8) ne "drawable") {next;}添加mipmap的判断即可让目前的MTK ThemeManager识别到mipmap下的资源。
这是一种修改app icon的方法.这种方法不仅可以将launcher 里面显示的icon 改掉,还可以把从系统级别上起到效果.
如何添加额外的图片资源(默认的3个主题都没有的图片)
frameworks/base/data/etc/thememap.xml
frameworks/base/libs/androidfw/MTKThemeManager.cpp
查了一下源码,系统里面具体那些文件会根据主题的不同而不同的,是有什么决定的呢?图片资源和主题是如果一一映射的.
thememap.xml 里面配置了这些信息,下面举一个例子:
<Module name="Browser" path="/system/app/Browser.apk">
<item>progress.9.png</item>
<item>textfield_active_holo_dark.9.png</item>
<item>bookmarks_widget_thumb_selector_focused.9.png</item>
<item>bookmarks_widget_thumb_selector_pressed.9.png</item>
</Module>
上面就是Browser 浏览器和主题相关的图片资源了.
一个应用对应一个Module, name 的值就对应的编译过程中对应的LOCAL_PACKAGE_NAME, path 就是路径.
如何设定默认主题为mint,mocha或raspberry
以mint主题为例:
1,修改文件 frameworks/base/core/java/android/content/res/Configuration.java,将变量 SKIN_UNDEFINED 值赋为 "/system/framework/theme-res-mint.apk";
2,修改文件 frameworks/base/libs/androidfw/AssetManager.cpp,修改变量 static const char* kDefaultThemePath的值为:
"/system/framework/theme-res-mint.apk".
mtk 的主题功能涉及到了那些代码
packages/apps/Settings/src/com/mediatek/thememanager
frameworks/base/libs/androidfw/MTKThemeManager.cpp
frameworks/base/data/etc/thememap.xml
ThemeManager.java
@Override
public void onItemClick(AdapterView<?> parent, View view,int position,long id) {
mStatusBarManager.disable(StatusBarManager.DISABLE_EXPAND);
if (mCurrentPosition != position) {
newSetThemeTask(this.getActivity()).execute(position);
mCurrentPosition = position;
} else {
mStatusBarManager.disable(StatusBarManager.DISABLE_NONE);
}
}
private class SetThemeTask extends AsyncTask<Integer, Void, Void> {
public SetThemeTask(Context context) {
mContext = context;
}
@Override
protected Void doInBackground(Integer... types) {
int position = types[0];
IActivityManager am = ActivityManagerNative.getDefault();
try {
// The mThemeDatas list will be cleared when onDestroy is called,
// so null pointer exception would happens when calling mThemeDatas.get(position),
// synchronized set theme and destroy to fix this issue.
synchronized (mLock) {
if (mThemeDatas == null) {
Xlog.e(TAG, "doInBackground error occured, mThemeDatas becomes null.");
}
Configuration config = am.getConfiguration();
config.skin =mThemeDatas.get(position).getThemePath()
.toString();
Xlog.d(TAG, "doInBackground() am.updateConfiguration() config.skin = "
+ config.skin);
am.updateConfiguration(config);
}
} catch (RemoteException e) {
Xlog.e(TAG, "Update configuration for theme changed failed.");
e.printStackTrace();
}
return null;
具体可以继续查看ActivityManagerService.java 的相关方法
updateConfiguration(xxx),这个方法主要是修改persist.sys.skin 这个属性的值
具体原理的分析
谁来解析这个thememap .xml文件呢?
MTKThemeManager.cpp 类负责这个工作,没有想到是个cpp,本来以为是个java 类.应该是c++的解析速度更快.
主要是函数: void MTKThemeManager::parseThemeMapIfNeeded() 来完成这个工作.(为方便阅读,删除部分不重要的代码)
static const char* kThemeMapFile = "/system/etc/theme/thememap.xml";
static const char* kThemeModuleName = "Module";
static const char* kThemePathAttr = "path";
static const char* kThemeItemName = "item";
// kThemeMapFile 就是之前提到的参与到随主题功能切换的资源图片的文件
void MTKThemeManager::parseThemeMapIfNeeded()
{
/* Load theme map xml file. */
TiXmlDocument* pDoc = new TiXmlDocument(kThemeMapFile);
pDoc->LoadFile(); //加载文件
/* Get the root node(thememap) and the first module child node.*/
TiXmlElement *pRootElement = pDoc->RootElement();
TiXmlElement *pFirstModuleElement = pRootElement->FirstChildElement(kThemeModuleName);
/* Get module node count to create the path map array.*/
int moduleCnt = getChildNodeCount(kThemeModuleName,pRootElement, pFirstModuleElement);
//得到总的参与到主题功能里面的app 数量
mPathMap = new ThemePathMap[moduleCnt];
MTKThemeManager::mModuleCount = moduleCnt;
TiXmlNode *pModuleNode = pFirstModuleElement;
TiXmlNode *pItemNode = NULL;
TiXmlNode *pFirstItemElement = NULL;
int itemCnt = 0;
int moduleIndex = 0;
int tempIndex = 0;
/* Parse the whole xml by module. */
while (pModuleNode != NULL) {
mPathMap[moduleIndex].path = ((TiXmlElement*)pModuleNode)->Attribute(kThemePathAttr);
pFirstItemElement = pModuleNode->FirstChildElement(kThemeItemName);
itemCnt = getChildNodeCount(kThemeItemName,pModuleNode, pFirstItemElement);
mPathMap[moduleIndex].fileCount = itemCnt;
if (itemCnt == 0) {
mPathMap[moduleIndex].fileList = NULL;
continue;
}
// itemFileList :保存解析的信息,可以供查询
ThemeFileList *itemFileList = newThemeFileList[itemCnt];
pItemNode = pFirstItemElement;
tempIndex = 0;
/* Parse all items in the current module pModuleNode. */
while (pItemNode != NULL) {
itemFileList[tempIndex++].fileName = ((TiXmlElement*)pItemNode)->GetText();
pItemNode = (TiXmlElement *)pModuleNode->IterateChildren(kThemeItemName, pItemNode);
}
mPathMap[moduleIndex].fileList = itemFileList;
moduleIndex++;
pModuleNode = (TiXmlElement *)pRootElement->IterateChildren(kThemeModuleName, pModuleNode);
}
}
又是谁调用了MTKThemeManager 的 parseThemeMapIfNeeded 方法:
在frameworks/base/libs/androidfw/AssetManager.cpp 里面可以看到
构造函数
AssetManager::AssetManager(CacheMode cacheMode)
: mLocale(NULL), mVendor(NULL),
mResources(NULL), mConfig(new ResTable_config),
mCacheMode(cacheMode), mCacheValid(false)
{
//……….
#if MTK_THEMEMANAGER_APP
mThemePath = new asset_path()
#ifdef THEME_NO_BUILD
ALOGV("THEME_NO_BUILD is set for host.");
#else
mThemeManager = MTKThemeManager::getInstance();
mThemeManager->parseThemeMapIfNeeded();
#endif
#endif
}
AssetManager 在构造函数里面调用了 mThemeManager->parseThemeMapIfNeeded() 来解析
thememap.xml .
继续跟踪AssetManager 里面还有哪里使用mThemeManager .
函数getAssetFromTheme 里面调用了mThemeManager mThemeManager->isFileInMap
mThemeManager->isFileInMap 就是判断文件fileName 的资源是否是在thememap.xml 里面有着类似以下对应的信息
<Module name="Browser" path="/system/app/Browser.apk">
<item>progress.9.png</item>
isFileInMap 会调用方法
findFileInList(ThemeFileList *fileList, int listLen, const char* fileName) 来查询该文件是否在thememap.xml 的映射范围之内
Asset* AssetManager::getAssetFromTheme(int which, const char* fileName,
asset_path themePath, AccessMode mode) {
Asset* pAsset = NULL;
char* assetPath = (char*) mAssetPaths.itemAt(which).path.string();
if (0 == strncmp(assetPath, kISmsPath, 22)) {
assetPath = "/data/app/ISmsService.apk";
}
if (mThemeManager != NULL && mThemeManager->isFileInMap(assetPath,
fileName)) { //判断文件是否在主题map 范围之内
themePath.type = mAssetPaths.itemAt(which).type;
if (which == 0x00) { // frameworks resource use default filename to access
pAsset = openNonAssetInPathLocked(fileName, mode,themePath);
} else { // applications resource
char* apkFileName = (char*) malloc(strlen(fileName) + 60);
memset(apkFileName, 0x00, strlen(fileName) + 60);
appendApkPath(assetPath, fileName, apkFileName);
pAsset = openNonAssetInPathLocked(apkFileName, mode,themePath);
free(apkFileName);
apkFileName = NULL;
}
}
return pAsset;
}
同样逆推,会发现openNonAsset 调用了getAssetFromTheme
系统如何获取图片等资源
当我们调用Resources.java 来获取图片等资源的时候会调用getDrawable 方法, getDrawable最终把这还是使用loadDrawable 方法来处理.
Resources.java
public Drawable getDrawable(int id) throws NotFoundException {
TypedValue value;
synchronized (mAccessLock) {
value = mTmpValue;
if (value == null) {
value = new TypedValue();
} else {
mTmpValue = null;
}
getValue(id, value, true);
}
Drawable res = loadDrawable(value, id);
synchronized (mAccessLock) {
if (mTmpValue == null) {
mTmpValue = value;
}
}
return res;
}
loadDrawable 方法在获取资源的时候会先判断该资源是否在缓存中,如果在缓存中就直接获取返回,如果没有就继续查询.同时使用color配置的图片的资源也会先去缓存中读取,这样可以提高代码的效率.如果还是没有找到就会调用AssetManager.java 的openNonAsset 方法,再接着分析会发现调用的是Jni 的一个方法openNonAssetNative,Jni 最终会去调用AssetManager.cpp 的openNonAsset的方法来根据不同主题去获取对应的资源.
frameworks/base/core/jni/android_util_AssetManager.cpp àopenNonAssetNative
static jint android_content_AssetManager_openNonAssetNative(JNIEnv* env, jobject clazz,
jint cookie,
jstring fileName,
jint mode)
{
AssetManager* am = assetManagerForJavaObject(env, clazz);
if (am == NULL) {
return 0;
}
ALOGV("openNonAssetNative in %p (Java object %p)\n", am, clazz);
ScopedUtfChars fileName8(env, fileName);
if (fileName8.c_str() == NULL) {
return -1;
}
if (mode != Asset::ACCESS_UNKNOWN && mode != Asset::ACCESS_RANDOM
&& mode != Asset::ACCESS_STREAMING && mode != Asset::ACCESS_BUFFER) {
jniThrowException(env, "java/lang/IllegalArgumentException", "Bad access mode");
return -1;
}
Asset* a = cookie
? am->openNonAsset((void*)cookie, fileName8.c_str(), (Asset::AccessMode)mode)
: am->openNonAsset(fileName8.c_str(), (Asset::AccessMode)mode);
if (a == NULL) {
jniThrowException(env, "java/io/FileNotFoundException", fileName8.c_str());
return -1;
}
//printf("Created Asset Stream: %p\n", a);
return (jint)a;
}
Resources.java
Drawable loadDrawable(TypedValue value, int id)
throws NotFoundException {
if (TRACE_FOR_PRELOAD) {
// Log only framework resources
if ((id >>> 24) == 0x1) {//framework resources id 是以0x1 开头, mediatek Resource id 是一0x2 开头. 第3方资源一般以0x7 开头
final String name = getResourceName(id);
if (name != null) android.util.Log.d("PreloadDrawable", name);
}
}
boolean isColorDrawable = false;
if (value.type >= TypedValue.TYPE_FIRST_COLOR_INT &&
value.type <= TypedValue.TYPE_LAST_COLOR_INT) {
isColorDrawable = true;
}
final long key = isColorDrawable ? value.data :
(((long) value.assetCookie) << 32) | value.data;
Drawable dr = getCachedDrawable(isColorDrawable ? mColorDrawableCache : mDrawableCache, key);//从缓存中读取,如果缓存中已经存在,就直接获取并返回,缓存中没有的话就继续执行下面的代码去查询获取对应的资源文件.
if (dr != null) {
return dr;
}
Drawable.ConstantState cs = null;
/// M: Theme manager @{
Boolean checkPreload = true;
if (FeatureOption.MTK_THEMEMANAGER_APP) {
String skin= Configuration.getSkin();
if ((skin != null) &&
!(skin.equals(sDefaultSkin))){
checkPreload = false;
}
}
/// @}
//使用color配置的图片可以也直接从缓存中读取,提高效率.
if (checkPreload) {/// M: Theme manager
if (isColorDrawable) {
cs = sPreloadedColorDrawables.get(key);
} else {
cs = sPreloadedDrawables[mConfiguration.getLayoutDirection()].get(key);
}
}/// M: Theme manager
if (cs != null) {
dr = cs.newDrawable(this);
} else {
if (isColorDrawable) {
dr = new ColorDrawable(value.data);
}
if (dr == null) {
if (value.string == null) {
throw new NotFoundException(
"Resource is not a Drawable (color or path): " + value);
}
String file = value.string.toString();
if (TRACE_FOR_MISS_PRELOAD) {
// Log only framework resources
if ((id >>> 24) == 0x1) {
final String name = getResourceName(id);
if (name != null) android.util.Log.d(TAG, "Loading framework drawable #"
+ Integer.toHexString(id) + ": " + name
+ " at " + file);
}
}
if (DEBUG_LOAD) Log.v(TAG, "Loading drawable for cookie "
+ value.assetCookie + ": " + file);
if (file.endsWith(".xml")) {
Trace.traceBegin(Trace.TRACE_TAG_RESOURCES, file);
try {
XmlResourceParser rp = loadXmlResourceParser(
file, id, value.assetCookie, "drawable");
dr = Drawable.createFromXml(this, rp);
rp.close();
} catch (Exception e) {
Trace.traceEnd(Trace.TRACE_TAG_RESOURCES);
NotFoundException rnf = new NotFoundException(
"File " + file + " from drawable resource ID #0x"
+ Integer.toHexString(id));
rnf.initCause(e);
throw rnf;
}
Trace.traceEnd(Trace.TRACE_TAG_RESOURCES);
} else {
Trace.traceBegin(Trace.TRACE_TAG_RESOURCES, file);
try {
InputStream is = mAssets.openNonAsset(
value.assetCookie, file, AssetManager.ACCESS_STREAMING);
//System.out.println("Opened file " + file + ": " + is);
dr = Drawable.createFromResourceStream(this, value, is,
file, null);
is.close();
//System.out.println("Created stream: " + dr);
} catch (Exception e) {
Trace.traceEnd(Trace.TRACE_TAG_RESOURCES);
NotFoundException rnf = new NotFoundException(
"File " + file + " from drawable resource ID #0x"
+ Integer.toHexString(id));
rnf.initCause(e);
throw rnf;
}
Trace.traceEnd(Trace.TRACE_TAG_RESOURCES);
}
}
}
if (dr != null) {
dr.setChangingConfigurations(value.changingConfigurations);
cs = dr.getConstantState();
if (cs != null) {
if (mPreloading) {
final int changingConfigs = cs.getChangingConfigurations();
if (isColorDrawable) {
if (verifyPreloadConfig(changingConfigs, 0, value.resourceId,
"drawable")) {
sPreloadedColorDrawables.put(key, cs);
}
} else {
if (verifyPreloadConfig(changingConfigs,
LAYOUT_DIR_CONFIG, value.resourceId, "drawable")) {
if ((changingConfigs&LAYOUT_DIR_CONFIG) == 0) {
// If this resource does not vary based on layout direction,
// we can put it in all of the preload maps.
sPreloadedDrawables[0].put(key, cs);
sPreloadedDrawables[1].put(key, cs);
} else {
// Otherwise, only in the layout dir we loaded it for.
final LongSparseArray<Drawable.ConstantState> preloads
= sPreloadedDrawables[mConfiguration.getLayoutDirection()];
preloads.put(key, cs);
}
}
}
} else {
synchronized (mAccessLock) {
//Log.i(TAG, "Saving cached drawable @ #" +
// Integer.toHexString(key.intValue())
// + " in " + this + ": " + cs);
if (isColorDrawable) {
mColorDrawableCache.put(key, new WeakReference<Drawable.ConstantState>(cs));
} else {
mDrawableCache.put(key, new WeakReference<Drawable.ConstantState>(cs));
}
}
}
}
}
return dr;
}
系统如何根据不同主题来选择对应的资源
应用层获取资源的时候,不管当前系统使用的那个主题,同一个图片资源的id都是一样的,当前系统是如何根据这个id 来返回对应的图片资源呢?
系统在获取资源的时候,会根据是否选择了其他的主题来修改获取对应图片的Asset 对象
AssetManager.cpp
Asset* AssetManager::openNonAsset(void* cookie, const char* fileName,AccessMode mode)
{
const size_t which = ((size_t)cookie)-1;
#if MTK_THEMEMANAGER_APP
memset(kThemeApkPath, 0x00, 100);
//获取当前的主题.在java 里面可以使用Configuration 的 getSkin方法
property_get("persist.sys.skin", kThemeApkPath, kDefaultThemePath);
mThemePath->path.setTo(kThemeApkPath);
#endif
AutoMutex _l(mLock);
if (mCacheMode != CACHE_OFF && !mCacheValid)
loadFileNameCacheLocked();
#if MTK_THEMEMANAGER_APP
if (which < mAssetPaths.size()) {
Asset* pAsset = getAssetFromTheme(which, fileName, *mThemePath,mode);
//获取主题对应Asset 对象,如果对应的图片文件不在主题所修能修改文件(Map)里面就会返回null.
if (pAsset == NULL) {
pAsset = openNonAssetInPathLocked(
fileName, mode, mAssetPaths.itemAt(which));
}
if (pAsset != NULL) {
return pAsset != kExcludedAsset ? pAsset : NULL;
}
}
#else
//……….
#endif
return NULL;
}
AssetManager.cpp
Asset* AssetManager::getAssetFromTheme(int which, const char* fileName,
asset_path themePath, AccessMode mode) {
Asset* pAsset = NULL;
char* assetPath = (char*) mAssetPaths.itemAt(which).path.string();
if (0 == strncmp(assetPath, kISmsPath, 22)) {
assetPath = "/data/app/ISmsService.apk";
}
if (mThemeManager != NULL && mThemeManager->isFileInMap(assetPath,
fileName)) {
ALOGV("filename mapped success which = %d, fileName = %s.\n", which,
fileName);
themePath.type = mAssetPaths.itemAt(which).type;
if (which == 0x00) { // frameworks resource use default filename to access
pAsset = openNonAssetInPathLocked(fileName, mode, themePath);
} else { // applications resource
char* apkFileName = (char*) malloc(strlen(fileName) + 60);
memset(apkFileName, 0x00, strlen(fileName) + 60);
appendApkPath(assetPath, fileName, apkFileName);
pAsset = openNonAssetInPathLocked(apkFileName, mode, themePath);
free(apkFileName);
apkFileName = NULL;
}
}
return pAsset;
}
同时在frameworks/base/core/java/android/content/res/Resources.java 里面发现Resources.java也可以根据资源的类型和当前主题来调用对应的资源.
public Bitmap getThemeImage(String themePackagePath, String itemType) {
Bitmap bitmap = null;
int cookie = 0;
//………………………..
cookie = getAssets().addAssetPath(themePackagePath);
StringBuilder themeDrawablePath = new StringBuilder("res/drawable-");
themeDrawablePath.append(generateCurrentDensitySuffix());
themeDrawablePath.append(itemType + ".png");
//……….
try {
InputStream is = getAssets().openNonAsset(cookie, themeDrawablePath.toString());
bitmap = BitmapFactory.decodeStream(is);
} catch (IOException e) {
Log.e(TAG, "IOException happend when getThemeImage cookie = " + cookie);
}
return bitmap;
}
Framework java层和C++ 层是如何工作的。
AssetManager.java 和 AssetManager.cpp 之间的关联是通过C++文件android_util_AssetManager.cpp 来实现的。
frameworks/base/core/jni/android_util_AssetManager.cpp
设置应是如何把用户选择的主题通知到系统
用户单击主题item 的时候,会调用异步更新类SetThemeTask 来通知framework 层来更新AssetManager ,这样在系统调用资源的时候,如果该资源是在主题功能范围之内就会使用主题里面的资源,而不去默认的应用里面找。
ThemeManager.java
@Override
public void onItemClick(AdapterView<?> parent, View view, int position,long id) {
mStatusBarManager.disable(StatusBarManager.DISABLE_EXPAND);
if (mCurrentPosition != position) {
new SetThemeTask(this.getActivity()).execute(position);//根据实际选择的theme 来通知framework.
mCurrentPosition = position;
} else {
mStatusBarManager.disable(StatusBarManager.DISABLE_NONE);
}
}
private class SetThemeTask extends AsyncTask<Integer, Void, Void> {
public SetThemeTask(Context context) {
mContext = context;
}
@Override
protected Void doInBackground(Integer... types) {
int position = types[0];
IActivityManager am = ActivityManagerNative.getDefault();
try {
// The mThemeDatas list will be cleared when onDestroy is called,
// so null pointer exception would happens when calling mThemeDatas.get(position),
// synchronized set theme and destroy to fix this issue.
synchronized (mLock) {
if (mThemeDatas == null) {
Xlog.e(TAG, "doInBackground error occured, mThemeDatas becomes null.");
}
Configuration config = am.getConfiguration();
config.skin = mThemeDatas.get(position).getThemePath()
.toString();
Xlog.d(TAG, "doInBackground() am.updateConfiguration() config.skin = "
+ config.skin);
am.updateConfiguration(config); // 调用这个方法更新config.skin 之后,会导致重新调用oncreate() 方法。
}
} catch (RemoteException e) {
Xlog.e(TAG, "Update configuration for theme changed failed.");
e.printStackTrace();
}
return null;
}
@Override
protected void onPreExecute() {
showSetThemeDialog();
}
@Override
protected void onPostExecute(Void unused) {
mStatusBarManager.disable(StatusBarManager.DISABLE_NONE);
}
private Context mContext;
}
- mtk 主题功能学习笔记
- MTK主题风格追踪
- MTK主题风格
- MTK主题风格
- MTK主题修改
- MTK主题风格
- MTK主题风格
- mtk功能机平台触屏校准功能阅读笔记
- MTK源码学习笔记之-------taskInit学习
- Python学习笔记---高级主题
- LDA主题模型学习笔记
- mtk刷机/启动流程学习笔记
- MTK笔记
- [学习笔记]小型论坛功能——实现树状结构的主题贴显示的类[2]
- MTK学习
- 主题:Nosql 学习笔记(一)
- Android学习笔记_29_样式和主题
- Sencha Touch学习笔记(十一)主题
- 初学C++
- modafinil试吃
- TCP-IP协议族(二) HTTP报文头解析
- 字符串匹配的三种算法
- JAVA基础知识-常用但容易出错的代码
- mtk 主题功能学习笔记
- test title
- 浅析Android Material Design之TextInputLayout
- navicat mysql导入数据sq文件时 USING BTREE 错误解决办法
- Android开发——设置权限
- 用golang写的一个链表
- 【C#源码】爱流量活动免费领取300M移动流量 Q群验证
- 博客迁移
- BigDecimal总结