修改Android的开关机铃声、Android开关机画面与动画(附代码流程)
/********************************************************************************************
* author:[email protected]大钟
* E-mail:[email protected]
*site:http://www.idealpwr.com/
*深圳市动力思维科技发展有限公司
* http://blog.csdn.net/conowen
* 注:本文为原创,仅作为学习交流使用,转载请标明作者及出处。
********************************************************************************************/
1、修改Android的开关机铃声
待续……
2、修改Android开关机画面
开机画面包括三个过程
2.1、bootloader的开机画面
待续……
2.2、Android系统init时的开机画面
2.2.1、Android系统init时,装载initlogo.rle的简单流程。
Androidinit时,会读取 /initlogo.rle文件,如果读取成功,就会在/dev/graphics/fb0显示initlogo.rle;如果读取失败,打开/dev/tty0,输出文本“A N D R I O D”字样。
相关代码:
/system/core/init/init.c
/system/core/init/init.h
/system/core/init/init.rc
/system/core/init/logo.c
@/system/core/init/init.c
static int console_init_action(int nargs, char **args){ int fd; char tmp[PROP_VALUE_MAX]; if (console[0]) { snprintf(tmp, sizeof(tmp), "/dev/%s", console); console_name = strdup(tmp); } fd = open(console_name, O_RDWR); if (fd >= 0) have_console = 1; close(fd); if( load_565rle_image(INIT_IMAGE_FILE) ) {//在init.h文件定义 //#define INIT_IMAGE_FILE "/initlogo.rle" fd = open("/dev/tty0", O_WRONLY); if (fd >= 0) {//如果找不到initlog.rle图片,则打开TEXT模式, const char *msg; msg = "\n" "\n" "\n" "\n" "\n" "\n" "\n" // console is 40 cols x 30 lines "\n" "\n" "\n" "\n" "\n" "\n" "\n" " A N D R O I D ";//打印ANDROID字样的问题。 write(fd, msg, strlen(msg)); close(fd); } } return 0;}
@/system/core/init/logo.c
static int fb_open(struct FB *fb){ fb->fd = open("/dev/graphics/fb0", O_RDWR);//会在/dev/graphics/fb0显示initlogo.rle if (fb->fd < 0) return -1; if (ioctl(fb->fd, FBIOGET_FSCREENINFO, &fb->fi) < 0) goto fail; if (ioctl(fb->fd, FBIOGET_VSCREENINFO, &fb->vi) < 0) goto fail; fb->bits = mmap(0, fb_size(fb), PROT_READ | PROT_WRITE, MAP_SHARED, fb->fd, 0); if (fb->bits == MAP_FAILED) goto fail; return 0;fail: close(fb->fd); return -1;}/************部分省略****************//* 565RLE image format: [count(2 bytes), rle(2 bytes)] */int load_565rle_image(char *fn){ struct FB fb; struct stat s; unsigned short *data, *bits, *ptr; unsigned count, max; int fd; if (vt_set_mode(1)) return -1; fd = open(fn, O_RDONLY); if (fd < 0) { ERROR("cannot open '%s'\n", fn); goto fail_restore_text; } if (fstat(fd, &s) < 0) { goto fail_close_file; } data = mmap(0, s.st_size, PROT_READ, MAP_SHARED, fd, 0); if (data == MAP_FAILED) goto fail_close_file; if (fb_open(&fb)) goto fail_unmap_data; max = fb_width(&fb) * fb_height(&fb); ptr = data; count = s.st_size; bits = fb.bits; while (count > 3) { unsigned n = ptr[0]; if (n > max) break; android_memset16(bits, ptr[1], n << 1); bits += n; max -= n; ptr += 2; count -= 4; } munmap(data, s.st_size); fb_update(&fb); fb_close(&fb); close(fd); unlink(fn); return 0;fail_unmap_data: munmap(data, s.st_size); fail_close_file: close(fd);fail_restore_text: vt_set_mode(0); return -1;}
2.2.2、修改方法:
2.2.2.1、找到一张符合机器分辨率的bmp图像,把bmp图片转换成为raw图像
要注意分辨率问题,若是不符合机器分辨率,可能显示拉伸或者花屏之类等等各种情况。
使用到的工具imagemagick,若没有,用apt安装即可。
convert -depth 8 initlogo.bmp rgb:initlogo.raw
2.2.2.2、把raw图像转换为rle图像即可。
使用的工具rgb2565,编译Android系统源代码成功时,最红再编译SDK(执行make PRODUCT-sdk-sdk),编译SDK成功之后,在\out\host\linux-x86\bin生成rgb2565。然后在rgb2565目录下执行如下命令
./rgb2565 -rle < initlogo.raw > initlogo.rle
2.2.2.3、替换文件
若是想在虚拟机上面测试,当系统编译完成之后,把initlogo.rle文件copy到\out\target\product\generic\root目录下,然后把\out\target\product\generic的ramdisk.img删除掉,再执行以下make命令,就可以生成新的ramdisk.img了,把ramdisk.img替代虚拟机镜像的ramdisk.img目录即可。因为root目录是ramdisk.img解包之后的文件夹,这一步只是把rle文件打包到ramdisk.img里面。
若是在一些机器的SDK修改开机画面,则可以到devices目录下的相应芯片厂商那里找找mk文件,然后编辑
如tcc892X是如下路径
\device\telechips\tcc8920st\device_base.mk
修改或者添加为
PRODUCT_COPY_FILES += \ device/telechips/common/initlogo_conowen.rle:root/initlogo.rle
事实上,这就是一个copy的过程,和之前的添加第三方apk没啥区别。
2.3、Android系统最后的开机动画(类似windows的进度条)
相关代码:
frameworks\base\cmds\bootanimation\BootAnimation.cpp
frameworks\base\cmds\bootanimation\BootAnimation.h
2.3.1、Android开机动画有两种方式,一种是系统的默认开机动画,由两张png图片构成,由代码控制背景图片的移动,造成动画效果;
相关资源
Android默认开机动画的前景图片,文字部分镂空,尺寸256×64
\frameworks\base\core\res\assets\images\android-logo-shine.png
Android默认开机动画的背景图片,尺寸512×64
2.3.2、另外一种是用户自定义的动画,动画打包为一个bootanimation.zip文件,存储在以下地方。
//来自Bootanimation.cpp的宏定义#define USER_BOOTANIMATION_FILE "/data/local/bootanimation.zip"#define SYSTEM_BOOTANIMATION_FILE "/system/media/bootanimation.zip"#define SYSTEM_ENCRYPTED_BOOTANIMATION_FILE "/system/media/bootanimation-encrypted.zip"
系统会先读取上述路径,查询是否存在bootanimation.zip文件,并且可以成功加载。若是都成立,系统的开机动画则是用户自定义的bootanimation.zip开机方式。否则采用系统默认的开机动画。
2.3.3、开机动画的函数实现流程。
首先,BootAnimation里面的BootAnimation类继承thread类,开始时,由系统调用BootAnimation类的成员函数onFirstRef(),在这个函数里面主要是调用了父类Thread的成员函数run,然后就会创建一个新的线程。
void BootAnimation::onFirstRef() { status_t err = mSession->linkToComposerDeath(this); LOGE_IF(err, "linkToComposerDeath failed (%s) ", strerror(-err)); if (err == NO_ERROR) { run("BootAnimation", PRIORITY_DISPLAY);//开始新的线程 }}
线程开始运行时,会调用BootAnimation类的成员函数readyToRun()来作一些初始化工作,如读取给定路径的bootanimation.zip。
status_t BootAnimation::readyToRun() { mAssets.addDefaultAssets(); DisplayInfo dinfo; status_t status = session()->getDisplayInfo(0, &dinfo); if (status) return -1; // create the native surface sp<SurfaceControl> control = session()->createSurface( 0, dinfo.w, dinfo.h, PIXEL_FORMAT_RGB_565); SurfaceComposerClient::openGlobalTransaction(); control->setLayer(0x40000000); SurfaceComposerClient::closeGlobalTransaction(); sp<Surface> s = control->getSurface(); // initialize opengl and egl const EGLint attribs[] = { EGL_RED_SIZE, 8, EGL_GREEN_SIZE, 8, EGL_BLUE_SIZE, 8, EGL_DEPTH_SIZE, 0, EGL_NONE }; EGLint w, h, dummy; EGLint numConfigs; EGLConfig config; EGLSurface surface; EGLContext context; EGLDisplay display = eglGetDisplay(EGL_DEFAULT_DISPLAY); eglInitialize(display, 0, 0); eglChooseConfig(display, attribs, &config, 1, &numConfigs); surface = eglCreateWindowSurface(display, config, s.get(), NULL); context = eglCreateContext(display, config, NULL, NULL); eglQuerySurface(display, surface, EGL_WIDTH, &w); eglQuerySurface(display, surface, EGL_HEIGHT, &h); if (eglMakeCurrent(display, surface, surface, context) == EGL_FALSE) return NO_INIT; mDisplay = display; mContext = context; mSurface = surface; mWidth = w; mHeight = h; mFlingerSurfaceControl = control; mFlingerSurface = s; mAndroidAnimation = true;//默认是采用系统的默认开机动画,所以mAndroidAnimation默认为true // If the device has encryption turned on or is in process // of being encrypted we show the encrypted boot animation. char decrypt[PROPERTY_VALUE_MAX]; property_get("vold.decrypt", decrypt, ""); bool encryptedAnimation = atoi(decrypt) != 0 || !strcmp("trigger_restart_min_framework", decrypt); if ((encryptedAnimation && (access(SYSTEM_ENCRYPTED_BOOTANIMATION_FILE, R_OK) == 0) && (mZip.open(SYSTEM_ENCRYPTED_BOOTANIMATION_FILE) == NO_ERROR)) || ((access(USER_BOOTANIMATION_FILE, R_OK) == 0) && (mZip.open(USER_BOOTANIMATION_FILE) == NO_ERROR)) || ((access(SYSTEM_BOOTANIMATION_FILE, R_OK) == 0) && (mZip.open(SYSTEM_BOOTANIMATION_FILE) == NO_ERROR))) { mAndroidAnimation = false;//若成功读取到bootanimation.zip文件,标志位为false,也就是采用用户自定义的动画方案 } return NO_ERROR;}
之后再调用BootAnimation类的成员函数threadLoop()来显示Android系统的开机动画。
bool BootAnimation::threadLoop(){ bool r; if (mAndroidAnimation) {//由mAndroidAnimation这个标志位来决定到底播放哪一种 r = android();//播放系统默认的动画 } else { r = movie();//播放用户自定义的动画 } eglMakeCurrent(mDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); eglDestroyContext(mDisplay, mContext); eglDestroySurface(mDisplay, mSurface); mFlingerSurface.clear(); mFlingerSurfaceControl.clear(); eglTerminate(mDisplay); IPCThreadState::self()->stopProcess(); return r;}bool BootAnimation::android(){ initTexture(&mAndroid[0], mAssets, "images/android-logo-mask.png"); initTexture(&mAndroid[1], mAssets, "images/android-logo-shine.png");//读取默认开机动画的两张png图片 // clear screen glShadeModel(GL_FLAT); glDisable(GL_DITHER); glDisable(GL_SCISSOR_TEST); glClearColor(0,0,0,1); glClear(GL_COLOR_BUFFER_BIT); eglSwapBuffers(mDisplay, mSurface); glEnable(GL_TEXTURE_2D); glTexEnvx(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); const GLint xc = (mWidth - mAndroid[0].w) / 2; const GLint yc = (mHeight - mAndroid[0].h) / 2; const Rect updateRect(xc, yc, xc + mAndroid[0].w, yc + mAndroid[0].h); glScissor(updateRect.left, mHeight - updateRect.bottom, updateRect.width(), updateRect.height()); // Blend state glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); glTexEnvx(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); const nsecs_t startTime = systemTime(); do { nsecs_t now = systemTime(); double time = now - startTime; float t = 4.0f * float(time / us2ns(16667)) / mAndroid[1].w; GLint offset = (1 - (t - floorf(t))) * mAndroid[1].w; GLint x = xc - offset; glDisable(GL_SCISSOR_TEST); glClear(GL_COLOR_BUFFER_BIT); glEnable(GL_SCISSOR_TEST); glDisable(GL_BLEND); glBindTexture(GL_TEXTURE_2D, mAndroid[1].name); glDrawTexiOES(x, yc, 0, mAndroid[1].w, mAndroid[1].h); glDrawTexiOES(x + mAndroid[1].w, yc, 0, mAndroid[1].w, mAndroid[1].h); glEnable(GL_BLEND); glBindTexture(GL_TEXTURE_2D, mAndroid[0].name); glDrawTexiOES(xc, yc, 0, mAndroid[0].w, mAndroid[0].h); EGLBoolean res = eglSwapBuffers(mDisplay, mSurface); if (res == EGL_FALSE) break; // 12fps: don't animate too fast to preserve CPU const nsecs_t sleepTime = 83333 - ns2us(systemTime() - now); if (sleepTime > 0) usleep(sleepTime); } while (!exitPending()); glDeleteTextures(1, &mAndroid[0].name); glDeleteTextures(1, &mAndroid[1].name); return false;}bool BootAnimation::movie(){ ZipFileRO& zip(mZip); size_t numEntries = zip.getNumEntries(); ZipEntryRO desc = zip.findEntryByName("desc.txt"); FileMap* descMap = zip.createEntryFileMap(desc); LOGE_IF(!descMap, "descMap is null"); if (!descMap) { return false; } String8 desString((char const*)descMap->getDataPtr(), descMap->getDataLength()); char const* s = desString.string(); Animation animation; // Parse the description file for (;;) { const char* endl = strstr(s, "\n"); if (!endl) break; String8 line(s, endl - s); const char* l = line.string(); int fps, width, height, count, pause; char path[256]; if (sscanf(l, "%d %d %d", &width, &height, &fps) == 3) { //LOGD("> w=%d, h=%d, fps=%d", fps, width, height); animation.width = width; animation.height = height; animation.fps = fps; } if (sscanf(l, "p %d %d %s", &count, &pause, path) == 3) { //LOGD("> count=%d, pause=%d, path=%s", count, pause, path); Animation::Part part; part.count = count; part.pause = pause; part.path = path; animation.parts.add(part); } s = ++endl; } // read all the data structures const size_t pcount = animation.parts.size(); for (size_t i=0 ; i<numEntries ; i++) { char name[256]; ZipEntryRO entry = zip.findEntryByIndex(i); if (zip.getEntryFileName(entry, name, 256) == 0) { const String8 entryName(name); const String8 path(entryName.getPathDir()); const String8 leaf(entryName.getPathLeaf()); if (leaf.size() > 0) { for (int j=0 ; j<pcount ; j++) { if (path == animation.parts[j].path) { int method; // supports only stored png files if (zip.getEntryInfo(entry, &method, 0, 0, 0, 0, 0)) { if (method == ZipFileRO::kCompressStored) { FileMap* map = zip.createEntryFileMap(entry); if (map) { Animation::Frame frame; frame.name = leaf; frame.map = map; Animation::Part& part(animation.parts.editItemAt(j)); part.frames.add(frame); } } } } } } } } // clear screen glShadeModel(GL_FLAT); glDisable(GL_DITHER); glDisable(GL_SCISSOR_TEST); glDisable(GL_BLEND); glClearColor(0,0,0,1); glClear(GL_COLOR_BUFFER_BIT); eglSwapBuffers(mDisplay, mSurface); glBindTexture(GL_TEXTURE_2D, 0); glEnable(GL_TEXTURE_2D); glTexEnvx(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); const int xc = (mWidth - animation.width) / 2; const int yc = ((mHeight - animation.height) / 2); nsecs_t lastFrame = systemTime(); nsecs_t frameDuration = s2ns(1) / animation.fps; Region clearReg(Rect(mWidth, mHeight)); clearReg.subtractSelf(Rect(xc, yc, xc+animation.width, yc+animation.height)); for (int i=0 ; i<pcount && !exitPending() ; i++) { const Animation::Part& part(animation.parts[i]); const size_t fcount = part.frames.size(); glBindTexture(GL_TEXTURE_2D, 0); for (int r=0 ; !part.count || r<part.count ; r++) { for (int j=0 ; j<fcount && !exitPending(); j++) { const Animation::Frame& frame(part.frames[j]); if (r > 0) { glBindTexture(GL_TEXTURE_2D, frame.tid); } else { if (part.count != 1) { glGenTextures(1, &frame.tid); glBindTexture(GL_TEXTURE_2D, frame.tid); glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); } initTexture( frame.map->getDataPtr(), frame.map->getDataLength()); } if (!clearReg.isEmpty()) { Region::const_iterator head(clearReg.begin()); Region::const_iterator tail(clearReg.end()); glEnable(GL_SCISSOR_TEST); while (head != tail) { const Rect& r(*head++); glScissor(r.left, mHeight - r.bottom, r.width(), r.height()); glClear(GL_COLOR_BUFFER_BIT); } glDisable(GL_SCISSOR_TEST); } glDrawTexiOES(xc, yc, 0, animation.width, animation.height); eglSwapBuffers(mDisplay, mSurface); nsecs_t now = systemTime(); nsecs_t delay = frameDuration - (now - lastFrame); lastFrame = now; long wait = ns2us(frameDuration); if (wait > 0) usleep(wait); } usleep(part.pause * ns2us(frameDuration)); } // free the textures for this part if (part.count != 1) { for (int j=0 ; j<fcount ; j++) { const Animation::Frame& frame(part.frames[j]); glDeleteTextures(1, &frame.tid); } } } return false;}
2.3.4、制作bootanimation.zip
拿CM7的bootanimation.zip的做个例子,首先解压做个zip文件。
2.3.4.1、
可以发现里面由两个文件夹和一个desc.txt(固定文件名)文档构成的
480 480 24p 1 0 androidp 0 0 part1
第一行的三个数字分别表示开机动画在屏幕中的显示宽度、高度以及帧速(fps)(每秒钟传输的图片的数量,若是24,表示一秒钟有24张图片刷新,每一帧图片的停留时间为1/24秒)。
接下来的行定义是一样的,后面的有多少个文件夹,就接来下就写多少行来定义文件夹里面的图片就行。
第1个参数:标志符:
必须是: p
第2个参数:文件夹的图片完全播放完的循环次数:
0 : 表示本文件夹里面的图片无限循环
第3个参数:每一个循环的间隔时间:
单位是一个帧的持续时间,比如帧数是24,那么帧的持续时间就是1秒/24 。
若第3个参数为10表示————以帧数24fps播放完了android文件夹的文件,然后等待10个帧的持续时间【也就是10*(1/24)】,然后在以24fps来重复播放这个文件夹的下面的图片。以此类推。
若第3个参数为0表示————每次循环的间隔时间为0
第4个参数:文件夹名称
文件夹中的图片文件的加载刷新按文件名的名称排序。
2.3.4.2、制作
建立文件夹bootanimation,然后在里面按照规范建立desc.txt文件,把图片文件夹拖进去bootanimation文件夹里面。注意图片的文件名的顺序。
在linux下面执行以下命令即可。
zip -0 bootanimation.zip android/*png part1/*png desc.txt
zip -0表示无压缩
也可以在windows下面用rar软件打包为zip,压缩标准选择为存储就行。(不压缩)
然后就生成了bootanimation.zip
更多相关文章
- Android根据不同语言切换图片
- Android 图片处理工具类汇总
- android里图片下载工具类AsyncImageLoader分析
- android 自由缩放图片
- android中实现图片的上下移动
- android图片压缩并转为base64字符串
- android > 图片旋转
- android 滚动的缓冲图片
- android:设计一个能在图片上涂鸦的程序。