summaryrefslogtreecommitdiffstats
path: root/cmds/bootanimation
diff options
context:
space:
mode:
authorBill Yi <byi@google.com>2014-10-22 09:00:49 -0700
committerBill Yi <byi@google.com>2014-10-22 09:00:49 -0700
commit5a71f83227f0340a265f34b226d8fe01d4dd9773 (patch)
tree9e74435ad8e21fe6ace6a1c49ca2f596c60ced34 /cmds/bootanimation
parent52656916e35b08013bf776b3603d6cecba287a1e (diff)
parenta0d07d49b8b0aae71beb48ec31b43f6923530d00 (diff)
downloadframeworks_base-5a71f83227f0340a265f34b226d8fe01d4dd9773.zip
frameworks_base-5a71f83227f0340a265f34b226d8fe01d4dd9773.tar.gz
frameworks_base-5a71f83227f0340a265f34b226d8fe01d4dd9773.tar.bz2
Merge lmp-dev-plus-aosp-without-vendor into stage-aosp-master
Change-Id: I7063b7b52e9c09a57eb6bf3b4ffa3716d58ebf43
Diffstat (limited to 'cmds/bootanimation')
-rw-r--r--cmds/bootanimation/Android.mk9
-rw-r--r--cmds/bootanimation/AudioPlayer.cpp314
-rw-r--r--cmds/bootanimation/AudioPlayer.h47
-rw-r--r--cmds/bootanimation/BootAnimation.cpp145
-rw-r--r--cmds/bootanimation/BootAnimation.h7
5 files changed, 484 insertions, 38 deletions
diff --git a/cmds/bootanimation/Android.mk b/cmds/bootanimation/Android.mk
index dd987e0..d6ecbe3 100644
--- a/cmds/bootanimation/Android.mk
+++ b/cmds/bootanimation/Android.mk
@@ -3,10 +3,13 @@ include $(CLEAR_VARS)
LOCAL_SRC_FILES:= \
bootanimation_main.cpp \
+ AudioPlayer.cpp \
BootAnimation.cpp
LOCAL_CFLAGS += -DGL_GLEXT_PROTOTYPES -DEGL_EGLEXT_PROTOTYPES
+LOCAL_C_INCLUDES += external/tinyalsa/include
+
LOCAL_SHARED_LIBRARIES := \
libcutils \
liblog \
@@ -17,10 +20,8 @@ LOCAL_SHARED_LIBRARIES := \
libskia \
libEGL \
libGLESv1_CM \
- libgui
-
-LOCAL_C_INCLUDES := \
- $(call include-path-for, corecg graphics)
+ libgui \
+ libtinyalsa
LOCAL_MODULE:= bootanimation
diff --git a/cmds/bootanimation/AudioPlayer.cpp b/cmds/bootanimation/AudioPlayer.cpp
new file mode 100644
index 0000000..471b77f
--- /dev/null
+++ b/cmds/bootanimation/AudioPlayer.cpp
@@ -0,0 +1,314 @@
+/*
+ * Copyright (C) 2014 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.
+ */
+
+#define LOG_NDEBUG 0
+#define LOG_TAG "BootAnim_AudioPlayer"
+
+#include "AudioPlayer.h"
+
+#include <androidfw/ZipFileRO.h>
+#include <tinyalsa/asoundlib.h>
+#include <utils/Log.h>
+#include <utils/String8.h>
+
+#define ID_RIFF 0x46464952
+#define ID_WAVE 0x45564157
+#define ID_FMT 0x20746d66
+#define ID_DATA 0x61746164
+
+// Maximum line length for audio_conf.txt
+// We only accept lines less than this length to avoid overflows using sscanf()
+#define MAX_LINE_LENGTH 1024
+
+struct riff_wave_header {
+ uint32_t riff_id;
+ uint32_t riff_sz;
+ uint32_t wave_id;
+};
+
+struct chunk_header {
+ uint32_t id;
+ uint32_t sz;
+};
+
+struct chunk_fmt {
+ uint16_t audio_format;
+ uint16_t num_channels;
+ uint32_t sample_rate;
+ uint32_t byte_rate;
+ uint16_t block_align;
+ uint16_t bits_per_sample;
+};
+
+
+namespace android {
+
+AudioPlayer::AudioPlayer()
+ : mCard(-1),
+ mDevice(-1),
+ mPeriodSize(0),
+ mPeriodCount(0),
+ mCurrentFile(NULL)
+{
+}
+
+AudioPlayer::~AudioPlayer() {
+}
+
+static bool setMixerValue(struct mixer* mixer, const char* name, const char* values)
+{
+ if (!mixer) {
+ ALOGE("no mixer in setMixerValue");
+ return false;
+ }
+ struct mixer_ctl *ctl = mixer_get_ctl_by_name(mixer, name);
+ if (!ctl) {
+ ALOGE("mixer_get_ctl_by_name failed for %s", name);
+ return false;
+ }
+
+ enum mixer_ctl_type type = mixer_ctl_get_type(ctl);
+ int numValues = mixer_ctl_get_num_values(ctl);
+ int intValue;
+ char stringValue[MAX_LINE_LENGTH];
+
+ for (int i = 0; i < numValues && values; i++) {
+ // strip leading space
+ while (*values == ' ') values++;
+ if (*values == 0) break;
+
+ switch (type) {
+ case MIXER_CTL_TYPE_BOOL:
+ case MIXER_CTL_TYPE_INT:
+ if (sscanf(values, "%d", &intValue) == 1) {
+ if (mixer_ctl_set_value(ctl, i, intValue) != 0) {
+ ALOGE("mixer_ctl_set_value failed for %s %d", name, intValue);
+ }
+ } else {
+ ALOGE("Could not parse %s as int for %d", intValue, name);
+ }
+ break;
+ case MIXER_CTL_TYPE_ENUM:
+ if (sscanf(values, "%s", stringValue) == 1) {
+ if (mixer_ctl_set_enum_by_string(ctl, stringValue) != 0) {
+ ALOGE("mixer_ctl_set_enum_by_string failed for %s %%s", name, stringValue);
+ }
+ } else {
+ ALOGE("Could not parse %s as enum for %d", stringValue, name);
+ }
+ break;
+ default:
+ ALOGE("unsupported mixer type %d for %s", type, name);
+ break;
+ }
+
+ values = strchr(values, ' ');
+ }
+
+ return true;
+}
+
+
+/*
+ * Parse the audio configuration file.
+ * The file is named audio_conf.txt and must begin with the following header:
+ *
+ * card=<ALSA card number>
+ * device=<ALSA device number>
+ * period_size=<period size>
+ * period_count=<period count>
+ *
+ * This header is followed by zero or more mixer settings, each with the format:
+ * mixer "<name>" = <value list>
+ * Since mixer names can contain spaces, the name must be enclosed in double quotes.
+ * The values in the value list can be integers, booleans (represented by 0 or 1)
+ * or strings for enum values.
+ */
+bool AudioPlayer::init(const char* config)
+{
+ int tempInt;
+ struct mixer* mixer = NULL;
+ char name[MAX_LINE_LENGTH];
+
+ for (;;) {
+ const char* endl = strstr(config, "\n");
+ if (!endl) break;
+ String8 line(config, endl - config);
+ if (line.length() >= MAX_LINE_LENGTH) {
+ ALOGE("Line too long in audio_conf.txt");
+ return false;
+ }
+ const char* l = line.string();
+
+ if (sscanf(l, "card=%d", &tempInt) == 1) {
+ ALOGD("card=%d", tempInt);
+ mCard = tempInt;
+
+ mixer = mixer_open(mCard);
+ if (!mixer) {
+ ALOGE("could not open mixer for card %d", mCard);
+ return false;
+ }
+ } else if (sscanf(l, "device=%d", &tempInt) == 1) {
+ ALOGD("device=%d", tempInt);
+ mDevice = tempInt;
+ } else if (sscanf(l, "period_size=%d", &tempInt) == 1) {
+ ALOGD("period_size=%d", tempInt);
+ mPeriodSize = tempInt;
+ } else if (sscanf(l, "period_count=%d", &tempInt) == 1) {
+ ALOGD("period_count=%d", tempInt);
+ mPeriodCount = tempInt;
+ } else if (sscanf(l, "mixer \"%[0-9a-zA-Z _]s\"", name) == 1) {
+ const char* values = strchr(l, '=');
+ if (values) {
+ values++; // skip '='
+ ALOGD("name: \"%s\" = %s", name, values);
+ setMixerValue(mixer, name, values);
+ } else {
+ ALOGE("values missing for name: \"%s\"", name);
+ }
+ }
+ config = ++endl;
+ }
+
+ mixer_close(mixer);
+
+ if (mCard >= 0 && mDevice >= 0) {
+ return true;
+ }
+
+ return false;
+}
+
+void AudioPlayer::playFile(struct FileMap* fileMap) {
+ // stop any currently playing sound
+ requestExitAndWait();
+
+ mCurrentFile = fileMap;
+ run("bootanim audio", PRIORITY_URGENT_AUDIO);
+}
+
+bool AudioPlayer::threadLoop()
+{
+ struct pcm_config config;
+ struct pcm *pcm = NULL;
+ bool moreChunks = true;
+ const struct chunk_fmt* chunkFmt = NULL;
+ void* buffer = NULL;
+ int bufferSize;
+ const uint8_t* wavData;
+ size_t wavLength;
+ const struct riff_wave_header* wavHeader;
+
+ if (mCurrentFile == NULL) {
+ ALOGE("mCurrentFile is NULL");
+ return false;
+ }
+
+ wavData = (const uint8_t *)mCurrentFile->getDataPtr();
+ if (!wavData) {
+ ALOGE("Could not access WAV file data");
+ goto exit;
+ }
+ wavLength = mCurrentFile->getDataLength();
+
+ wavHeader = (const struct riff_wave_header *)wavData;
+ if (wavLength < sizeof(*wavHeader) || (wavHeader->riff_id != ID_RIFF) ||
+ (wavHeader->wave_id != ID_WAVE)) {
+ ALOGE("Error: audio file is not a riff/wave file\n");
+ goto exit;
+ }
+ wavData += sizeof(*wavHeader);
+ wavLength -= sizeof(*wavHeader);
+
+ do {
+ const struct chunk_header* chunkHeader = (const struct chunk_header*)wavData;
+ if (wavLength < sizeof(*chunkHeader)) {
+ ALOGE("EOF reading chunk headers");
+ goto exit;
+ }
+
+ wavData += sizeof(*chunkHeader);
+ wavLength -= sizeof(*chunkHeader);
+
+ switch (chunkHeader->id) {
+ case ID_FMT:
+ chunkFmt = (const struct chunk_fmt *)wavData;
+ wavData += chunkHeader->sz;
+ wavLength -= chunkHeader->sz;
+ break;
+ case ID_DATA:
+ /* Stop looking for chunks */
+ moreChunks = 0;
+ break;
+ default:
+ /* Unknown chunk, skip bytes */
+ wavData += chunkHeader->sz;
+ wavLength -= chunkHeader->sz;
+ }
+ } while (moreChunks);
+
+ if (!chunkFmt) {
+ ALOGE("format not found in WAV file");
+ goto exit;
+ }
+
+
+ memset(&config, 0, sizeof(config));
+ config.channels = chunkFmt->num_channels;
+ config.rate = chunkFmt->sample_rate;
+ config.period_size = mPeriodSize;
+ config.period_count = mPeriodCount;
+ config.start_threshold = mPeriodSize / 4;
+ config.stop_threshold = INT_MAX;
+ config.avail_min = config.start_threshold;
+ if (chunkFmt->bits_per_sample != 16) {
+ ALOGE("only 16 bit WAV files are supported");
+ goto exit;
+ }
+ config.format = PCM_FORMAT_S16_LE;
+
+ pcm = pcm_open(mCard, mDevice, PCM_OUT, &config);
+ if (!pcm || !pcm_is_ready(pcm)) {
+ ALOGE("Unable to open PCM device (%s)\n", pcm_get_error(pcm));
+ goto exit;
+ }
+
+ bufferSize = pcm_frames_to_bytes(pcm, pcm_get_buffer_size(pcm));
+
+ while (wavLength > 0) {
+ if (exitPending()) goto exit;
+ size_t count = bufferSize;
+ if (count > wavLength)
+ count = wavLength;
+
+ if (pcm_write(pcm, wavData, count)) {
+ ALOGE("pcm_write failed (%s)", pcm_get_error(pcm));
+ goto exit;
+ }
+ wavData += count;
+ wavLength -= count;
+ }
+
+exit:
+ if (pcm)
+ pcm_close(pcm);
+ mCurrentFile->release();
+ mCurrentFile = NULL;
+ return false;
+}
+
+} // namespace android
diff --git a/cmds/bootanimation/AudioPlayer.h b/cmds/bootanimation/AudioPlayer.h
new file mode 100644
index 0000000..7e82a07
--- /dev/null
+++ b/cmds/bootanimation/AudioPlayer.h
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2014 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.
+ */
+
+#ifndef _BOOTANIMATION_AUDIOPLAYER_H
+#define _BOOTANIMATION_AUDIOPLAYER_H
+
+#include <utils/Thread.h>
+
+namespace android {
+
+class AudioPlayer : public Thread
+{
+public:
+ AudioPlayer();
+ virtual ~AudioPlayer();
+ bool init(const char* config);
+
+ void playFile(struct FileMap* fileMap);
+
+private:
+ virtual bool threadLoop();
+
+private:
+ int mCard; // ALSA card to use
+ int mDevice; // ALSA device to use
+ int mPeriodSize;
+ int mPeriodCount;
+
+ struct FileMap* mCurrentFile;
+};
+
+} // namespace android
+
+#endif // _BOOTANIMATION_AUDIOPLAYER_H
diff --git a/cmds/bootanimation/BootAnimation.cpp b/cmds/bootanimation/BootAnimation.cpp
index f3e3aff..167014e 100644
--- a/cmds/bootanimation/BootAnimation.cpp
+++ b/cmds/bootanimation/BootAnimation.cpp
@@ -14,6 +14,7 @@
* limitations under the License.
*/
+#define LOG_NDEBUG 0
#define LOG_TAG "BootAnimation"
#include <stdint.h>
@@ -31,28 +32,28 @@
#include <utils/Atomic.h>
#include <utils/Errors.h>
#include <utils/Log.h>
-#include <utils/threads.h>
#include <ui/PixelFormat.h>
#include <ui/Rect.h>
#include <ui/Region.h>
#include <ui/DisplayInfo.h>
-#include <ui/FramebufferNativeWindow.h>
#include <gui/ISurfaceComposer.h>
#include <gui/Surface.h>
#include <gui/SurfaceComposerClient.h>
-#include <core/SkBitmap.h>
-#include <core/SkStream.h>
-#include <core/SkImageDecoder.h>
+#include <SkBitmap.h>
+#include <SkStream.h>
+#include <SkImageDecoder.h>
#include <GLES/gl.h>
#include <GLES/glext.h>
#include <EGL/eglext.h>
#include "BootAnimation.h"
+#include "AudioPlayer.h"
+#define OEM_BOOTANIMATION_FILE "/oem/media/bootanimation.zip"
#define SYSTEM_BOOTANIMATION_FILE "/system/media/bootanimation.zip"
#define SYSTEM_ENCRYPTED_BOOTANIMATION_FILE "/system/media/bootanimation-encrypted.zip"
#define EXIT_PROP_NAME "service.bootanim.exit"
@@ -96,6 +97,9 @@ void BootAnimation::binderDied(const wp<IBinder>&)
// might be blocked on a condition variable that will never be updated.
kill( getpid(), SIGKILL );
requestExit();
+ if (mAudioPlayer != NULL) {
+ mAudioPlayer->requestExit();
+ }
}
status_t BootAnimation::initTexture(Texture* texture, AssetManager& assets,
@@ -105,7 +109,7 @@ status_t BootAnimation::initTexture(Texture* texture, AssetManager& assets,
return NO_INIT;
SkBitmap bitmap;
SkImageDecoder::DecodeMemory(asset->getBuffer(false), asset->getLength(),
- &bitmap, SkBitmap::kNo_Config, SkImageDecoder::kDecodePixels_Mode);
+ &bitmap, kUnknown_SkColorType, SkImageDecoder::kDecodePixels_Mode);
asset->close();
delete asset;
@@ -124,20 +128,20 @@ status_t BootAnimation::initTexture(Texture* texture, AssetManager& assets,
glGenTextures(1, &texture->name);
glBindTexture(GL_TEXTURE_2D, texture->name);
- switch (bitmap.getConfig()) {
- case SkBitmap::kA8_Config:
+ switch (bitmap.colorType()) {
+ case kAlpha_8_SkColorType:
glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, w, h, 0, GL_ALPHA,
GL_UNSIGNED_BYTE, p);
break;
- case SkBitmap::kARGB_4444_Config:
+ case kARGB_4444_SkColorType:
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, w, h, 0, GL_RGBA,
GL_UNSIGNED_SHORT_4_4_4_4, p);
break;
- case SkBitmap::kARGB_8888_Config:
+ case kN32_SkColorType:
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, w, h, 0, GL_RGBA,
GL_UNSIGNED_BYTE, p);
break;
- case SkBitmap::kRGB_565_Config:
+ case kRGB_565_SkColorType:
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, w, h, 0, GL_RGB,
GL_UNSIGNED_SHORT_5_6_5, p);
break;
@@ -163,7 +167,7 @@ status_t BootAnimation::initTexture(const Animation::Frame& frame)
if (codec) {
codec->setDitherImage(false);
codec->decode(&stream, &bitmap,
- SkBitmap::kARGB_8888_Config,
+ kN32_SkColorType,
SkImageDecoder::kDecodePixels_Mode);
delete codec;
}
@@ -187,8 +191,8 @@ status_t BootAnimation::initTexture(const Animation::Frame& frame)
if (tw < w) tw <<= 1;
if (th < h) th <<= 1;
- switch (bitmap.getConfig()) {
- case SkBitmap::kARGB_8888_Config:
+ switch (bitmap.colorType()) {
+ case kN32_SkColorType:
if (tw != w || th != h) {
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, tw, th, 0, GL_RGBA,
GL_UNSIGNED_BYTE, 0);
@@ -200,7 +204,7 @@ status_t BootAnimation::initTexture(const Animation::Frame& frame)
}
break;
- case SkBitmap::kRGB_565_Config:
+ case kRGB_565_SkColorType:
if (tw != w || th != h) {
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, tw, th, 0, GL_RGB,
GL_UNSIGNED_SHORT_5_6_5, 0);
@@ -286,6 +290,9 @@ status_t BootAnimation::readyToRun() {
(access(SYSTEM_ENCRYPTED_BOOTANIMATION_FILE, R_OK) == 0) &&
((zipFile = ZipFileRO::open(SYSTEM_ENCRYPTED_BOOTANIMATION_FILE)) != NULL)) ||
+ ((access(OEM_BOOTANIMATION_FILE, R_OK) == 0) &&
+ ((zipFile = ZipFileRO::open(OEM_BOOTANIMATION_FILE)) != NULL)) ||
+
((access(SYSTEM_BOOTANIMATION_FILE, R_OK) == 0) &&
((zipFile = ZipFileRO::open(SYSTEM_BOOTANIMATION_FILE)) != NULL))) {
mZip = zipFile;
@@ -388,29 +395,76 @@ void BootAnimation::checkExit() {
int exitnow = atoi(value);
if (exitnow) {
requestExit();
+ if (mAudioPlayer != NULL) {
+ mAudioPlayer->requestExit();
+ }
}
}
-bool BootAnimation::movie()
+// Parse a color represented as an HTML-style 'RRGGBB' string: each pair of
+// characters in str is a hex number in [0, 255], which are converted to
+// floating point values in the range [0.0, 1.0] and placed in the
+// corresponding elements of color.
+//
+// If the input string isn't valid, parseColor returns false and color is
+// left unchanged.
+static bool parseColor(const char str[7], float color[3]) {
+ float tmpColor[3];
+ for (int i = 0; i < 3; i++) {
+ int val = 0;
+ for (int j = 0; j < 2; j++) {
+ val *= 16;
+ char c = str[2*i + j];
+ if (c >= '0' && c <= '9') val += c - '0';
+ else if (c >= 'A' && c <= 'F') val += (c - 'A') + 10;
+ else if (c >= 'a' && c <= 'f') val += (c - 'a') + 10;
+ else return false;
+ }
+ tmpColor[i] = static_cast<float>(val) / 255.0f;
+ }
+ memcpy(color, tmpColor, sizeof(tmpColor));
+ return true;
+}
+
+bool BootAnimation::readFile(const char* name, String8& outString)
{
- ZipEntryRO desc = mZip->findEntryByName("desc.txt");
- ALOGE_IF(!desc, "couldn't find desc.txt");
- if (!desc) {
+ ZipEntryRO entry = mZip->findEntryByName(name);
+ ALOGE_IF(!entry, "couldn't find %s", name);
+ if (!entry) {
return false;
}
- FileMap* descMap = mZip->createEntryFileMap(desc);
- mZip->releaseEntry(desc);
- ALOGE_IF(!descMap, "descMap is null");
- if (!descMap) {
+ FileMap* entryMap = mZip->createEntryFileMap(entry);
+ mZip->releaseEntry(entry);
+ ALOGE_IF(!entryMap, "entryMap is null");
+ if (!entryMap) {
return false;
}
- String8 desString((char const*)descMap->getDataPtr(),
- descMap->getDataLength());
- descMap->release();
+ outString.setTo((char const*)entryMap->getDataPtr(), entryMap->getDataLength());
+ entryMap->release();
+ return true;
+}
+
+bool BootAnimation::movie()
+{
+ String8 desString;
+
+ if (!readFile("desc.txt", desString)) {
+ return false;
+ }
char const* s = desString.string();
+ // Create and initialize an AudioPlayer if we have an audio_conf.txt file
+ String8 audioConf;
+ if (readFile("audio_conf.txt", audioConf)) {
+ mAudioPlayer = new AudioPlayer;
+ if (!mAudioPlayer->init(audioConf.string())) {
+ ALOGE("mAudioPlayer.init failed");
+ mAudioPlayer = NULL;
+ }
+ }
+
Animation animation;
// Parse the description file
@@ -421,20 +475,29 @@ bool BootAnimation::movie()
const char* l = line.string();
int fps, width, height, count, pause;
char path[ANIM_ENTRY_NAME_MAX];
+ char color[7] = "000000"; // default to black if unspecified
+
char pathType;
if (sscanf(l, "%d %d %d", &width, &height, &fps) == 3) {
- //LOGD("> w=%d, h=%d, fps=%d", width, height, fps);
+ // ALOGD("> w=%d, h=%d, fps=%d", width, height, fps);
animation.width = width;
animation.height = height;
animation.fps = fps;
}
- else if (sscanf(l, " %c %d %d %s", &pathType, &count, &pause, path) == 4) {
- //LOGD("> type=%c, count=%d, pause=%d, path=%s", pathType, count, pause, path);
+ else if (sscanf(l, " %c %d %d %s #%6s", &pathType, &count, &pause, path, color) >= 4) {
+ // ALOGD("> type=%c, count=%d, pause=%d, path=%s, color=%s", pathType, count, pause, path, color);
Animation::Part part;
part.playUntilComplete = pathType == 'c';
part.count = count;
part.pause = pause;
part.path = path;
+ part.audioFile = NULL;
+ if (!parseColor(color, part.backgroundColor)) {
+ ALOGE("> invalid color '#%s'", color);
+ part.backgroundColor[0] = 0.0f;
+ part.backgroundColor[1] = 0.0f;
+ part.backgroundColor[2] = 0.0f;
+ }
animation.parts.add(part);
}
@@ -469,11 +532,16 @@ bool BootAnimation::movie()
if (method == ZipFileRO::kCompressStored) {
FileMap* map = mZip->createEntryFileMap(entry);
if (map) {
- Animation::Frame frame;
- frame.name = leaf;
- frame.map = map;
Animation::Part& part(animation.parts.editItemAt(j));
- part.frames.add(frame);
+ if (leaf == "audio.wav") {
+ // a part may have at most one audio file
+ part.audioFile = map;
+ } else {
+ Animation::Frame frame;
+ frame.name = leaf;
+ frame.map = map;
+ part.frames.add(frame);
+ }
}
}
}
@@ -520,6 +588,17 @@ bool BootAnimation::movie()
if(exitPending() && !part.playUntilComplete)
break;
+ // only play audio file the first time we animate the part
+ if (r == 0 && mAudioPlayer != NULL && part.audioFile) {
+ mAudioPlayer->playFile(part.audioFile);
+ }
+
+ glClearColor(
+ part.backgroundColor[0],
+ part.backgroundColor[1],
+ part.backgroundColor[2],
+ 1.0f);
+
for (size_t j=0 ; j<fcount && (!exitPending() || part.playUntilComplete) ; j++) {
const Animation::Frame& frame(part.frames[j]);
nsecs_t lastFrame = systemTime();
diff --git a/cmds/bootanimation/BootAnimation.h b/cmds/bootanimation/BootAnimation.h
index ba1c507..f968b25 100644
--- a/cmds/bootanimation/BootAnimation.h
+++ b/cmds/bootanimation/BootAnimation.h
@@ -21,7 +21,7 @@
#include <sys/types.h>
#include <androidfw/AssetManager.h>
-#include <utils/threads.h>
+#include <utils/Thread.h>
#include <EGL/egl.h>
#include <GLES/gl.h>
@@ -30,6 +30,7 @@ class SkBitmap;
namespace android {
+class AudioPlayer;
class Surface;
class SurfaceComposerClient;
class SurfaceControl;
@@ -71,6 +72,8 @@ private:
String8 path;
SortedVector<Frame> frames;
bool playUntilComplete;
+ float backgroundColor[3];
+ FileMap* audioFile;
};
int fps;
int width;
@@ -81,11 +84,13 @@ private:
status_t initTexture(Texture* texture, AssetManager& asset, const char* name);
status_t initTexture(const Animation::Frame& frame);
bool android();
+ bool readFile(const char* name, String8& outString);
bool movie();
void checkExit();
sp<SurfaceComposerClient> mSession;
+ sp<AudioPlayer> mAudioPlayer;
AssetManager mAssets;
Texture mAndroid[2];
int mWidth;