diff options
Diffstat (limited to 'tools')
52 files changed, 2261 insertions, 342 deletions
diff --git a/tools/aapt/Command.cpp b/tools/aapt/Command.cpp index 739763e..8ac7590 100644 --- a/tools/aapt/Command.cpp +++ b/tools/aapt/Command.cpp @@ -198,8 +198,10 @@ int doList(Bundle* bundle) if (&res == NULL) { printf("\nNo resource table found.\n"); } else { +#ifndef HAVE_ANDROID_OS printf("\nResource table:\n"); res.print(false); +#endif } Asset* manifestAsset = assets.openNonAsset("AndroidManifest.xml", @@ -428,8 +430,9 @@ int doDump(Bundle* bundle) } if (strcmp("resources", option) == 0) { +#ifndef HAVE_ANDROID_OS res.print(bundle->getValues()); - +#endif } else if (strcmp("xmltree", option) == 0) { if (bundle->getFileSpecCount() < 3) { fprintf(stderr, "ERROR: no dump xmltree resource file specified\n"); diff --git a/tools/aapt/Package.cpp b/tools/aapt/Package.cpp index 999a5cf..ab71f34 100644 --- a/tools/aapt/Package.cpp +++ b/tools/aapt/Package.cpp @@ -33,8 +33,8 @@ static const char* kNoCompressExt[] = { /* fwd decls, so I can write this downward */ ssize_t processAssets(Bundle* bundle, ZipFile* zip, const sp<AaptAssets>& assets); -ssize_t processAssets(Bundle* bundle, ZipFile* zip, - const sp<AaptDir>& dir, const AaptGroupEntry& ge); +ssize_t processAssets(Bundle* bundle, ZipFile* zip, const sp<AaptDir>& dir, + const AaptGroupEntry& ge, const ResourceFilter* filter); bool processFile(Bundle* bundle, ZipFile* zip, const sp<AaptGroup>& group, const sp<AaptFile>& file); bool okayToCompress(Bundle* bundle, const String8& pathName); @@ -204,34 +204,45 @@ ssize_t processAssets(Bundle* bundle, ZipFile* zip, const size_t N = assets->getGroupEntries().size(); for (size_t i=0; i<N; i++) { const AaptGroupEntry& ge = assets->getGroupEntries()[i]; - if (!filter.match(ge.toParams())) { - continue; - } - ssize_t res = processAssets(bundle, zip, assets, ge); + + ssize_t res = processAssets(bundle, zip, assets, ge, &filter); if (res < 0) { return res; } + count += res; } return count; } -ssize_t processAssets(Bundle* bundle, ZipFile* zip, - const sp<AaptDir>& dir, const AaptGroupEntry& ge) +ssize_t processAssets(Bundle* bundle, ZipFile* zip, const sp<AaptDir>& dir, + const AaptGroupEntry& ge, const ResourceFilter* filter) { ssize_t count = 0; const size_t ND = dir->getDirs().size(); size_t i; for (i=0; i<ND; i++) { - ssize_t res = processAssets(bundle, zip, dir->getDirs().valueAt(i), ge); + const sp<AaptDir>& subDir = dir->getDirs().valueAt(i); + + const bool filterable = filter != NULL && subDir->getLeaf().find("mipmap-") != 0; + + if (filterable && subDir->getLeaf() != subDir->getPath() && !filter->match(ge.toParams())) { + continue; + } + + ssize_t res = processAssets(bundle, zip, subDir, ge, filterable ? filter : NULL); if (res < 0) { return res; } count += res; } + if (filter != NULL && !filter->match(ge.toParams())) { + return count; + } + const size_t NF = dir->getFiles().size(); for (i=0; i<NF; i++) { sp<AaptGroup> gp = dir->getFiles().valueAt(i); @@ -441,7 +452,7 @@ ssize_t processJarFile(ZipFile* jar, ZipFile* out) ssize_t processJarFiles(Bundle* bundle, ZipFile* zip) { - ssize_t err; + status_t err; ssize_t count = 0; const android::Vector<const char*>& jars = bundle->getJarFiles(); @@ -450,7 +461,7 @@ ssize_t processJarFiles(Bundle* bundle, ZipFile* zip) ZipFile jar; err = jar.open(jars[i], ZipFile::kOpenReadOnly); if (err != 0) { - fprintf(stderr, "ERROR: unable to open '%s' as a zip file: %zd\n", + fprintf(stderr, "ERROR: unable to open '%s' as a zip file: %d\n", jars[i], err); return err; } diff --git a/tools/aapt/Resource.cpp b/tools/aapt/Resource.cpp index 0a4f24f..730bd71 100644 --- a/tools/aapt/Resource.cpp +++ b/tools/aapt/Resource.cpp @@ -148,9 +148,10 @@ private: bool isValidResourceType(const String8& type) { - return type == "anim" || type == "drawable" || type == "layout" + return type == "anim" || type == "animator" || type == "interpolator" + || type == "drawable" || type == "layout" || type == "values" || type == "xml" || type == "raw" - || type == "color" || type == "menu"; + || type == "color" || type == "menu" || type == "mipmap"; } static sp<AaptFile> getResourceFile(const sp<AaptAssets>& assets, bool makeIfNecessary=true) @@ -284,9 +285,9 @@ static status_t makeFileResources(Bundle* bundle, const sp<AaptAssets>& assets, } static status_t preProcessImages(Bundle* bundle, const sp<AaptAssets>& assets, - const sp<ResourceTypeSet>& set) + const sp<ResourceTypeSet>& set, const char* type) { - ResourceDirIterator it(set, String8("drawable")); + ResourceDirIterator it(set, String8(type)); Vector<sp<AaptFile> > newNameFiles; Vector<String8> newNamePaths; bool hasErrors = false; @@ -542,11 +543,11 @@ static bool applyFileOverlay(Bundle *bundle, DefaultKeyedVector<AaptGroupEntry, sp<AaptFile> > baseFiles = baseGroup->getFiles(); for (size_t i=0; i < baseFiles.size(); i++) { - printf("baseFile %ld has flavor %s\n", i, + printf("baseFile %zd has flavor %s\n", i, baseFiles.keyAt(i).toString().string()); } for (size_t i=0; i < overlayFiles.size(); i++) { - printf("overlayFile %ld has flavor %s\n", i, + printf("overlayFile %zd has flavor %s\n", i, overlayFiles.keyAt(i).toString().string()); } } @@ -560,7 +561,7 @@ static bool applyFileOverlay(Bundle *bundle, keyAt(overlayGroupIndex)); if(baseFileIndex < UNKNOWN_ERROR) { if (bundle->getVerbose()) { - printf("found a match (%ld) for overlay file %s, for flavor %s\n", + printf("found a match (%zd) for overlay file %s, for flavor %s\n", baseFileIndex, overlayGroup->getLeaf().string(), overlayFiles.keyAt(overlayGroupIndex).toString().string()); @@ -798,18 +799,24 @@ status_t buildResources(Bundle* bundle, const sp<AaptAssets>& assets) sp<ResourceTypeSet> drawables; sp<ResourceTypeSet> layouts; sp<ResourceTypeSet> anims; + sp<ResourceTypeSet> animators; + sp<ResourceTypeSet> interpolators; sp<ResourceTypeSet> xmls; sp<ResourceTypeSet> raws; sp<ResourceTypeSet> colors; sp<ResourceTypeSet> menus; + sp<ResourceTypeSet> mipmaps; ASSIGN_IT(drawable); ASSIGN_IT(layout); ASSIGN_IT(anim); + ASSIGN_IT(animator); + ASSIGN_IT(interpolator); ASSIGN_IT(xml); ASSIGN_IT(raw); ASSIGN_IT(color); ASSIGN_IT(menu); + ASSIGN_IT(mipmap); assets->setResources(resources); // now go through any resource overlays and collect their files @@ -825,10 +832,13 @@ status_t buildResources(Bundle* bundle, const sp<AaptAssets>& assets) if (!applyFileOverlay(bundle, assets, &drawables, "drawable") || !applyFileOverlay(bundle, assets, &layouts, "layout") || !applyFileOverlay(bundle, assets, &anims, "anim") || + !applyFileOverlay(bundle, assets, &animators, "animator") || + !applyFileOverlay(bundle, assets, &interpolators, "interpolator") || !applyFileOverlay(bundle, assets, &xmls, "xml") || !applyFileOverlay(bundle, assets, &raws, "raw") || !applyFileOverlay(bundle, assets, &colors, "color") || - !applyFileOverlay(bundle, assets, &menus, "menu")) { + !applyFileOverlay(bundle, assets, &menus, "menu") || + !applyFileOverlay(bundle, assets, &mipmaps, "mipmap")) { return UNKNOWN_ERROR; } @@ -836,7 +846,7 @@ status_t buildResources(Bundle* bundle, const sp<AaptAssets>& assets) if (drawables != NULL) { if (bundle->getOutputAPKFile() != NULL) { - err = preProcessImages(bundle, assets, drawables); + err = preProcessImages(bundle, assets, drawables, "drawable"); } if (err == NO_ERROR) { err = makeFileResources(bundle, assets, &table, drawables, "drawable"); @@ -848,6 +858,20 @@ status_t buildResources(Bundle* bundle, const sp<AaptAssets>& assets) } } + if (mipmaps != NULL) { + if (bundle->getOutputAPKFile() != NULL) { + err = preProcessImages(bundle, assets, mipmaps, "mipmap"); + } + if (err == NO_ERROR) { + err = makeFileResources(bundle, assets, &table, mipmaps, "mipmap"); + if (err != NO_ERROR) { + hasErrors = true; + } + } else { + hasErrors = true; + } + } + if (layouts != NULL) { err = makeFileResources(bundle, assets, &table, layouts, "layout"); if (err != NO_ERROR) { @@ -862,6 +886,20 @@ status_t buildResources(Bundle* bundle, const sp<AaptAssets>& assets) } } + if (animators != NULL) { + err = makeFileResources(bundle, assets, &table, animators, "animator"); + if (err != NO_ERROR) { + hasErrors = true; + } + } + + if (interpolators != NULL) { + err = makeFileResources(bundle, assets, &table, interpolators, "interpolator"); + if (err != NO_ERROR) { + hasErrors = true; + } + } + if (xmls != NULL) { err = makeFileResources(bundle, assets, &table, xmls, "xml"); if (err != NO_ERROR) { @@ -969,6 +1007,36 @@ status_t buildResources(Bundle* bundle, const sp<AaptAssets>& assets) err = NO_ERROR; } + if (animators != NULL) { + ResourceDirIterator it(animators, String8("animator")); + while ((err=it.next()) == NO_ERROR) { + err = compileXmlFile(assets, it.getFile(), &table, xmlFlags); + if (err != NO_ERROR) { + hasErrors = true; + } + } + + if (err < NO_ERROR) { + hasErrors = true; + } + err = NO_ERROR; + } + + if (interpolators != NULL) { + ResourceDirIterator it(interpolators, String8("interpolator")); + while ((err=it.next()) == NO_ERROR) { + err = compileXmlFile(assets, it.getFile(), &table, xmlFlags); + if (err != NO_ERROR) { + hasErrors = true; + } + } + + if (err < NO_ERROR) { + hasErrors = true; + } + err = NO_ERROR; + } + if (xmls != NULL) { ResourceDirIterator it(xmls, String8("xml")); while ((err=it.next()) == NO_ERROR) { @@ -2088,12 +2156,13 @@ writeProguardForLayouts(ProguardKeepSet* keep, const sp<AaptAssets>& assets) // tag:attribute pairs that should be checked in layout files. KeyedVector<String8, NamespaceAttributePair> kLayoutTagAttrPairs; addTagAttrPair(&kLayoutTagAttrPairs, "view", NULL, "class"); + addTagAttrPair(&kLayoutTagAttrPairs, "fragment", NULL, "class"); addTagAttrPair(&kLayoutTagAttrPairs, "fragment", RESOURCES_ANDROID_NAMESPACE, "name"); // tag:attribute pairs that should be checked in xml files. KeyedVector<String8, NamespaceAttributePair> kXmlTagAttrPairs; addTagAttrPair(&kXmlTagAttrPairs, "PreferenceScreen", RESOURCES_ANDROID_NAMESPACE, "fragment"); - addTagAttrPair(&kXmlTagAttrPairs, "Header", RESOURCES_ANDROID_NAMESPACE, "fragment"); + addTagAttrPair(&kXmlTagAttrPairs, "header", RESOURCES_ANDROID_NAMESPACE, "fragment"); const Vector<sp<AaptDir> >& dirs = assets->resDirs(); const size_t K = dirs.size(); diff --git a/tools/aapt/ResourceTable.cpp b/tools/aapt/ResourceTable.cpp index 818c3c6..5339566 100644 --- a/tools/aapt/ResourceTable.cpp +++ b/tools/aapt/ResourceTable.cpp @@ -2444,7 +2444,7 @@ ResourceTable::validateLocalizations(void) if (configSet.count(defaultLocale) == 0) { fprintf(stdout, "aapt: warning: string '%s' has no default translation in %s; found:", String8(nameIter->first).string(), mBundle->getResourceSourceDirs()[0]); - for (set<String8>::iterator locales = configSet.begin(); + for (set<String8>::const_iterator locales = configSet.begin(); locales != configSet.end(); locales++) { fprintf(stdout, " %s", (*locales).string()); @@ -2555,7 +2555,7 @@ ResourceFilter::parse(const char* arg) } bool -ResourceFilter::match(int axis, uint32_t value) +ResourceFilter::match(int axis, uint32_t value) const { if (value == 0) { // they didn't specify anything so take everything @@ -2571,7 +2571,7 @@ ResourceFilter::match(int axis, uint32_t value) } bool -ResourceFilter::match(const ResTable_config& config) +ResourceFilter::match(const ResTable_config& config) const { if (config.locale) { uint32_t locale = (config.country[1] << 24) | (config.country[0] << 16) @@ -2624,6 +2624,8 @@ status_t ResourceTable::flatten(Bundle* bundle, const sp<AaptFile>& dest) const size_t N = mOrderedPackages.size(); size_t pi; + const static String16 mipmap16("mipmap"); + bool useUTF8 = !bundle->getWantUTF16() && bundle->isMinSdkAtLeast(SDK_FROYO); // Iterate through all data, collecting all values (strings, @@ -2646,7 +2648,10 @@ status_t ResourceTable::flatten(Bundle* bundle, const sp<AaptFile>& dest) typeStrings.add(String16("<empty>"), false); continue; } - typeStrings.add(t->getName(), false); + const String16 typeName(t->getName()); + typeStrings.add(typeName, false); + + const bool filterable = (typeName != mipmap16); const size_t N = t->getOrderedConfigs().size(); for (size_t ci=0; ci<N; ci++) { @@ -2657,7 +2662,7 @@ status_t ResourceTable::flatten(Bundle* bundle, const sp<AaptFile>& dest) const size_t N = c->getEntries().size(); for (size_t ei=0; ei<N; ei++) { ConfigDescription config = c->getEntries().keyAt(ei); - if (!filter.match(config)) { + if (filterable && !filter.match(config)) { continue; } sp<Entry> e = c->getEntries().valueAt(ei); @@ -2737,6 +2742,8 @@ status_t ResourceTable::flatten(Bundle* bundle, const sp<AaptFile>& dest) "Type name %s not found", String8(typeName).string()); + const bool filterable = (typeName != mipmap16); + const size_t N = t != NULL ? t->getOrderedConfigs().size() : 0; // First write the typeSpec chunk, containing information about @@ -2761,7 +2768,7 @@ status_t ResourceTable::flatten(Bundle* bundle, const sp<AaptFile>& dest) (((uint8_t*)data->editData()) + typeSpecStart + sizeof(ResTable_typeSpec)); memset(typeSpecFlags, 0, sizeof(uint32_t)*N); - + for (size_t ei=0; ei<N; ei++) { sp<ConfigList> cl = t->getOrderedConfigs().itemAt(ei); if (cl->getPublic()) { @@ -2769,11 +2776,11 @@ status_t ResourceTable::flatten(Bundle* bundle, const sp<AaptFile>& dest) } const size_t CN = cl->getEntries().size(); for (size_t ci=0; ci<CN; ci++) { - if (!filter.match(cl->getEntries().keyAt(ci))) { + if (filterable && !filter.match(cl->getEntries().keyAt(ci))) { continue; } for (size_t cj=ci+1; cj<CN; cj++) { - if (!filter.match(cl->getEntries().keyAt(cj))) { + if (filterable && !filter.match(cl->getEntries().keyAt(cj))) { continue; } typeSpecFlags[ei] |= htodl( @@ -2810,7 +2817,7 @@ status_t ResourceTable::flatten(Bundle* bundle, const sp<AaptFile>& dest) config.screenWidth, config.screenHeight)); - if (!filter.match(config)) { + if (filterable && !filter.match(config)) { continue; } diff --git a/tools/aapt/ResourceTable.h b/tools/aapt/ResourceTable.h index 186c7ca..bbb8140 100644 --- a/tools/aapt/ResourceTable.h +++ b/tools/aapt/ResourceTable.h @@ -549,9 +549,9 @@ class ResourceFilter public: ResourceFilter() : mData(), mContainsPseudo(false) {} status_t parse(const char* arg); - bool match(int axis, uint32_t value); - bool match(const ResTable_config& config); - inline bool containsPseudo() { return mContainsPseudo; } + bool match(int axis, uint32_t value) const; + bool match(const ResTable_config& config) const; + inline bool containsPseudo() const { return mContainsPseudo; } private: KeyedVector<int,SortedVector<uint32_t> > mData; diff --git a/tools/aapt/StringPool.cpp b/tools/aapt/StringPool.cpp index a09cec0..d067d59 100644 --- a/tools/aapt/StringPool.cpp +++ b/tools/aapt/StringPool.cpp @@ -30,7 +30,7 @@ void printStringPool(const ResStringPool* pool) str = String8(pool->stringAt(s, &len)).string(); } - printf("String #%ld: %s\n", s, str); + printf("String #%zd: %s\n", s, str); } } diff --git a/tools/aapt/ZipFile.h b/tools/aapt/ZipFile.h index dbbd072..7877550 100644 --- a/tools/aapt/ZipFile.h +++ b/tools/aapt/ZipFile.h @@ -57,7 +57,7 @@ public: /* * Open a new or existing archive. */ - typedef enum { + enum { kOpenReadOnly = 0x01, kOpenReadWrite = 0x02, kOpenCreate = 0x04, // create if it doesn't exist diff --git a/tools/layoutlib/bridge/resources/bars/action_bar.xml b/tools/layoutlib/bridge/resources/bars/action_bar.xml new file mode 100644 index 0000000..51983f2 --- /dev/null +++ b/tools/layoutlib/bridge/resources/bars/action_bar.xml @@ -0,0 +1,9 @@ +<?xml version="1.0" encoding="utf-8"?> +<merge xmlns:android="http://schemas.android.com/apk/res/android"> + <ImageView + android:layout_height="wrap_content" + android:layout_width="wrap_content"/> + <TextView + android:layout_width="wrap_content" + android:layout_height="wrap_content"/> +</merge> diff --git a/tools/layoutlib/bridge/resources/bars/mdpi/ic_sysbar_back_default.png b/tools/layoutlib/bridge/resources/bars/mdpi/ic_sysbar_back_default.png Binary files differnew file mode 100644 index 0000000..4bcd2be --- /dev/null +++ b/tools/layoutlib/bridge/resources/bars/mdpi/ic_sysbar_back_default.png diff --git a/tools/layoutlib/bridge/resources/bars/mdpi/ic_sysbar_home_default.png b/tools/layoutlib/bridge/resources/bars/mdpi/ic_sysbar_home_default.png Binary files differnew file mode 100644 index 0000000..cfeba3e --- /dev/null +++ b/tools/layoutlib/bridge/resources/bars/mdpi/ic_sysbar_home_default.png diff --git a/tools/layoutlib/bridge/resources/bars/mdpi/ic_sysbar_recent_default.png b/tools/layoutlib/bridge/resources/bars/mdpi/ic_sysbar_recent_default.png Binary files differnew file mode 100644 index 0000000..1d97e05 --- /dev/null +++ b/tools/layoutlib/bridge/resources/bars/mdpi/ic_sysbar_recent_default.png diff --git a/tools/layoutlib/bridge/resources/bars/tablet_system_bar.xml b/tools/layoutlib/bridge/resources/bars/tablet_system_bar.xml new file mode 100644 index 0000000..c5acddb --- /dev/null +++ b/tools/layoutlib/bridge/resources/bars/tablet_system_bar.xml @@ -0,0 +1,24 @@ +<?xml version="1.0" encoding="utf-8"?> +<merge xmlns:android="http://schemas.android.com/apk/res/android"> + <ImageView + android:layout_height="wrap_content" + android:layout_width="wrap_content"/> + <ImageView + android:layout_height="wrap_content" + android:layout_width="wrap_content"/> + <ImageView + android:layout_height="wrap_content" + android:layout_width="wrap_content"/> + <TextView + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_weight="1"/> + <ImageView + android:layout_height="wrap_content" + android:layout_width="wrap_content"/> + <ImageView + android:layout_height="wrap_content" + android:layout_width="wrap_content" + android:layout_marginLeft="3dip" + android:layout_marginRight="15dip"/> +</merge> diff --git a/tools/layoutlib/bridge/src/android/animation/PropertyValuesHolder_Delegate.java b/tools/layoutlib/bridge/src/android/animation/PropertyValuesHolder_Delegate.java new file mode 100644 index 0000000..7b444aa --- /dev/null +++ b/tools/layoutlib/bridge/src/android/animation/PropertyValuesHolder_Delegate.java @@ -0,0 +1,59 @@ +/* + * Copyright (C) 2010 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. + */ + +package android.animation; + +import com.android.layoutlib.bridge.impl.DelegateManager; +import com.android.tools.layoutlib.annotations.LayoutlibDelegate; + +/** + * Delegate implementing the native methods of android.animation.PropertyValuesHolder + * + * Through the layoutlib_create tool, the original native methods of PropertyValuesHolder have been + * replaced by calls to methods of the same name in this delegate class. + * + * Because it's a stateless class to start with, there's no need to keep a {@link DelegateManager} + * around to map int to instance of the delegate. + * + * The main goal of this class' methods are to provide a native way to access setters and getters + * on some object. In this case we want to default to using Java reflection instead so the native + * methods do nothing. + * + */ +/*package*/ class PropertyValuesHolder_Delegate { + + @LayoutlibDelegate + /*package*/ static int nGetIntMethod(Class<?> targetClass, String methodName) { + // return 0 to force PropertyValuesHolder to use Java reflection. + return 0; + } + + @LayoutlibDelegate + /*package*/ static int nGetFloatMethod(Class<?> targetClass, String methodName) { + // return 0 to force PropertyValuesHolder to use Java reflection. + return 0; + } + + @LayoutlibDelegate + /*package*/ static void nCallIntMethod(Object target, int methodID, int arg) { + // do nothing + } + + @LayoutlibDelegate + /*package*/ static void nCallFloatMethod(Object target, int methodID, float arg) { + // do nothing + } +} diff --git a/tools/layoutlib/bridge/src/android/app/Fragment_Delegate.java b/tools/layoutlib/bridge/src/android/app/Fragment_Delegate.java new file mode 100644 index 0000000..aabd3f1 --- /dev/null +++ b/tools/layoutlib/bridge/src/android/app/Fragment_Delegate.java @@ -0,0 +1,104 @@ +/* + * Copyright (C) 2010 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. + */ + +package android.app; + +import com.android.ide.common.rendering.api.IProjectCallback; +import com.android.tools.layoutlib.annotations.LayoutlibDelegate; + +import android.content.Context; +import android.os.Bundle; + +/** + * Delegate used to provide new implementation of a select few methods of {@link Fragment} + * + * Through the layoutlib_create tool, the original methods of Fragment have been replaced + * by calls to methods of the same name in this delegate class. + * + * The methods being re-implemented are the ones responsible for instantiating Fragment objects. + * Because the classes of these objects are found in the project, these methods need access to + * {@link IProjectCallback} object. They are however static methods, so the callback is set + * before the inflation through {@link #setProjectCallback(IProjectCallback)}. + */ +public class Fragment_Delegate { + + private static IProjectCallback sProjectCallback; + + /** + * Sets the current {@link IProjectCallback} to be used to instantiate classes coming + * from the project being rendered. + */ + public static void setProjectCallback(IProjectCallback projectCallback) { + sProjectCallback = projectCallback; + } + + /** + * Like {@link #instantiate(Context, String, Bundle)} but with a null + * argument Bundle. + */ + @LayoutlibDelegate + /*package*/ static Fragment instantiate(Context context, String fname) { + return instantiate(context, fname, null); + } + + /** + * Create a new instance of a Fragment with the given class name. This is + * the same as calling its empty constructor. + * + * @param context The calling context being used to instantiate the fragment. + * This is currently just used to get its ClassLoader. + * @param fname The class name of the fragment to instantiate. + * @param args Bundle of arguments to supply to the fragment, which it + * can retrieve with {@link #getArguments()}. May be null. + * @return Returns a new fragment instance. + * @throws InstantiationException If there is a failure in instantiating + * the given fragment class. This is a runtime exception; it is not + * normally expected to happen. + */ + @LayoutlibDelegate + /*package*/ static Fragment instantiate(Context context, String fname, Bundle args) { + try { + if (sProjectCallback != null) { + Fragment f = (Fragment) sProjectCallback.loadView(fname, + new Class[0], new Object[0]); + + if (args != null) { + args.setClassLoader(f.getClass().getClassLoader()); + f.mArguments = args; + } + return f; + } + + return null; + } catch (ClassNotFoundException e) { + throw new Fragment.InstantiationException("Unable to instantiate fragment " + fname + + ": make sure class name exists, is public, and has an" + + " empty constructor that is public", e); + } catch (java.lang.InstantiationException e) { + throw new Fragment.InstantiationException("Unable to instantiate fragment " + fname + + ": make sure class name exists, is public, and has an" + + " empty constructor that is public", e); + } catch (IllegalAccessException e) { + throw new Fragment.InstantiationException("Unable to instantiate fragment " + fname + + ": make sure class name exists, is public, and has an" + + " empty constructor that is public", e); + } catch (Exception e) { + throw new Fragment.InstantiationException("Unable to instantiate fragment " + fname + + ": make sure class name exists, is public, and has an" + + " empty constructor that is public", e); + } + } +} diff --git a/tools/layoutlib/bridge/src/android/graphics/BitmapFactory_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/BitmapFactory_Delegate.java index 9c86e80..080b85f 100644 --- a/tools/layoutlib/bridge/src/android/graphics/BitmapFactory_Delegate.java +++ b/tools/layoutlib/bridge/src/android/graphics/BitmapFactory_Delegate.java @@ -136,17 +136,20 @@ import java.io.InputStream; @LayoutlibDelegate /*package*/ static Bitmap nativeDecodeFileDescriptor(FileDescriptor fd, Rect padding, Options opts) { + opts.inBitmap = null; return null; } @LayoutlibDelegate /*package*/ static Bitmap nativeDecodeAsset(int asset, Rect padding, Options opts) { + opts.inBitmap = null; return null; } @LayoutlibDelegate /*package*/ static Bitmap nativeDecodeByteArray(byte[] data, int offset, int length, Options opts) { + opts.inBitmap = null; return null; } @@ -156,4 +159,9 @@ import java.io.InputStream; // BitmapFactory.finishDecode(); return chunk; } + + @LayoutlibDelegate + /*package*/ static boolean nativeIsSeekable(FileDescriptor fd) { + return true; + } } diff --git a/tools/layoutlib/bridge/src/android/graphics/BitmapShader_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/BitmapShader_Delegate.java index 8eb0693..9a8cf04 100644 --- a/tools/layoutlib/bridge/src/android/graphics/BitmapShader_Delegate.java +++ b/tools/layoutlib/bridge/src/android/graphics/BitmapShader_Delegate.java @@ -79,6 +79,13 @@ public class BitmapShader_Delegate extends Shader_Delegate { return sManager.addNewDelegate(newDelegate); } + @LayoutlibDelegate + /*package*/ static int nativePostCreate(int native_shader, int native_bitmap, + int shaderTileModeX, int shaderTileModeY) { + // pass, not needed. + return 0; + } + // ---- Private delegate/helper methods ---- private BitmapShader_Delegate(java.awt.image.BufferedImage image, diff --git a/tools/layoutlib/bridge/src/android/graphics/Bitmap_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/Bitmap_Delegate.java index 87c3eb6..66e59d8 100644 --- a/tools/layoutlib/bridge/src/android/graphics/Bitmap_Delegate.java +++ b/tools/layoutlib/bridge/src/android/graphics/Bitmap_Delegate.java @@ -62,6 +62,8 @@ public final class Bitmap_Delegate { private final Config mConfig; private BufferedImage mImage; private boolean mHasAlpha = true; + private int mGenerationId = 0; + // ---- Public Helper methods ---- @@ -184,6 +186,15 @@ public final class Bitmap_Delegate { return mHasAlpha && mConfig != Config.RGB_565; } + /** + * Update the generationId. + * + * @see Bitmap#getGenerationId() + */ + public void change() { + mGenerationId++; + } + // ---- native methods ---- @LayoutlibDelegate @@ -384,6 +395,16 @@ public final class Bitmap_Delegate { } @LayoutlibDelegate + /*package*/ static int nativeGenerationId(int nativeBitmap) { + Bitmap_Delegate delegate = sManager.getDelegate(nativeBitmap); + if (delegate == null) { + return 0; + } + + return delegate.mGenerationId; + } + + @LayoutlibDelegate /*package*/ static Bitmap nativeCreateFromParcel(Parcel p) { // This is only called by Bitmap.CREATOR (Parcelable.Creator<Bitmap>), which is only // used during aidl call so really this should not be called. @@ -504,7 +525,7 @@ public final class Bitmap_Delegate { int nativeInt = sManager.addNewDelegate(delegate); // and create/return a new Bitmap with it - return new Bitmap(nativeInt, isMutable, null /*ninePatchChunk*/, density); + return new Bitmap(nativeInt, null /* buffer */, isMutable, null /*ninePatchChunk*/, density); } /** diff --git a/tools/layoutlib/bridge/src/android/graphics/Canvas_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/Canvas_Delegate.java index 02a2ddf..4decd1a 100644 --- a/tools/layoutlib/bridge/src/android/graphics/Canvas_Delegate.java +++ b/tools/layoutlib/bridge/src/android/graphics/Canvas_Delegate.java @@ -270,13 +270,6 @@ public final class Canvas_Delegate { } @LayoutlibDelegate - /*package*/ static void drawText(Canvas thisCanvas, - String text, float x, float y, Paint paint) { - native_drawText(thisCanvas.mNativeCanvas, text, 0, text.length(), x, y, - paint.mNativePaint); - } - - @LayoutlibDelegate /*package*/ static void drawPoints(Canvas thisCanvas, float[] pts, int offset, int count, Paint paint) { // FIXME @@ -330,12 +323,6 @@ public final class Canvas_Delegate { } @LayoutlibDelegate - /*package*/ static int initGL() { - // not supported. - return 0; - } - - @LayoutlibDelegate /*package*/ static void native_setBitmap(int nativeCanvas, int bitmap) { // get the delegate from the native int. Canvas_Delegate canvasDelegate = sManager.getDelegate(nativeCanvas); @@ -353,11 +340,6 @@ public final class Canvas_Delegate { } @LayoutlibDelegate - /*package*/ static void nativeSetViewport(int nCanvas, int w, int h) { - // only useful in GL which is not supported, so no need to do anything. - } - - @LayoutlibDelegate /*package*/ static int native_saveLayer(int nativeCanvas, RectF bounds, int paint, int layerFlags) { // get the delegate from the native int. @@ -962,7 +944,7 @@ public final class Canvas_Delegate { @LayoutlibDelegate /*package*/ static void native_drawText(int nativeCanvas, final char[] text, final int index, final int count, - final float startX, final float startY, int paint) { + final float startX, final float startY, int flags, int paint) { draw(nativeCanvas, paint, false /*compositeOnly*/, false /*forceSrcMode*/, new GcSnapshot.Drawable() { public void draw(Graphics2D graphics, Paint_Delegate paint) { @@ -1062,12 +1044,30 @@ public final class Canvas_Delegate { @LayoutlibDelegate /*package*/ static void native_drawText(int nativeCanvas, String text, int start, int end, float x, - float y, int paint) { + float y, int flags, int paint) { int count = end - start; char[] buffer = TemporaryBuffer.obtain(count); TextUtils.getChars(text, start, end, buffer, 0); - native_drawText(nativeCanvas, buffer, 0, count, x, y, paint); + native_drawText(nativeCanvas, buffer, 0, count, x, y, flags, paint); + } + + @LayoutlibDelegate + /*package*/ static void native_drawTextRun(int nativeCanvas, String text, + int start, int end, int contextStart, int contextEnd, + float x, float y, int flags, int paint) { + int count = end - start; + char[] buffer = TemporaryBuffer.obtain(count); + TextUtils.getChars(text, start, end, buffer, 0); + + native_drawText(nativeCanvas, buffer, start, end, x, y, flags, paint); + } + + @LayoutlibDelegate + /*package*/ static void native_drawTextRun(int nativeCanvas, char[] text, + int start, int count, int contextStart, int contextCount, + float x, float y, int flags, int paint) { + native_drawText(nativeCanvas, text, start, count, x, y, flags, paint); } @LayoutlibDelegate @@ -1094,7 +1094,7 @@ public final class Canvas_Delegate { char[] text, int index, int count, int path, float hOffset, - float vOffset, + float vOffset, int bidiFlags, int paint) { // FIXME Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED, @@ -1106,7 +1106,7 @@ public final class Canvas_Delegate { String text, int path, float hOffset, float vOffset, - int paint) { + int flags, int paint) { // FIXME Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED, "Canvas.drawTextOnPath is not supported.", null, null /*data*/); diff --git a/tools/layoutlib/bridge/src/android/graphics/ColorFilter_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/ColorFilter_Delegate.java index 4c692c2..e5a7ab6 100644 --- a/tools/layoutlib/bridge/src/android/graphics/ColorFilter_Delegate.java +++ b/tools/layoutlib/bridge/src/android/graphics/ColorFilter_Delegate.java @@ -56,7 +56,7 @@ public abstract class ColorFilter_Delegate { // ---- native methods ---- @LayoutlibDelegate - /*package*/ static void finalizer(int native_instance) { + /*package*/ static void finalizer(int native_instance, int nativeColorFilter) { sManager.removeJavaReferenceFor(native_instance); } diff --git a/tools/layoutlib/bridge/src/android/graphics/ColorMatrixColorFilter_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/ColorMatrixColorFilter_Delegate.java index d4c5b8d..2de344b 100644 --- a/tools/layoutlib/bridge/src/android/graphics/ColorMatrixColorFilter_Delegate.java +++ b/tools/layoutlib/bridge/src/android/graphics/ColorMatrixColorFilter_Delegate.java @@ -60,5 +60,11 @@ public class ColorMatrixColorFilter_Delegate extends ColorFilter_Delegate { return sManager.addNewDelegate(newDelegate); } + @LayoutlibDelegate + /*package*/ static int nColorMatrixFilter(int nativeFilter, float[] array) { + // pass + return 0; + } + // ---- Private delegate/helper methods ---- } diff --git a/tools/layoutlib/bridge/src/android/graphics/ComposeShader_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/ComposeShader_Delegate.java index fcc12d7..f6e1d00 100644 --- a/tools/layoutlib/bridge/src/android/graphics/ComposeShader_Delegate.java +++ b/tools/layoutlib/bridge/src/android/graphics/ComposeShader_Delegate.java @@ -78,6 +78,20 @@ public class ComposeShader_Delegate extends Shader_Delegate { return sManager.addNewDelegate(newDelegate); } + @LayoutlibDelegate + /*package*/ static int nativePostCreate1(int native_shader, int native_skiaShaderA, + int native_skiaShaderB, int native_mode) { + // pass, not needed. + return 0; + } + + @LayoutlibDelegate + /*package*/ static int nativePostCreate2(int native_shader, int native_skiaShaderA, + int native_skiaShaderB, int porterDuffMode) { + // pass, not needed. + return 0; + } + // ---- Private delegate/helper methods ---- } diff --git a/tools/layoutlib/bridge/src/android/graphics/LightingColorFilter_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/LightingColorFilter_Delegate.java index 3afe965..0ee883d 100644 --- a/tools/layoutlib/bridge/src/android/graphics/LightingColorFilter_Delegate.java +++ b/tools/layoutlib/bridge/src/android/graphics/LightingColorFilter_Delegate.java @@ -60,5 +60,11 @@ public class LightingColorFilter_Delegate extends ColorFilter_Delegate { return sManager.addNewDelegate(newDelegate); } + @LayoutlibDelegate + /*package*/ static int nCreateLightingFilter(int nativeFilter, int mul, int add) { + // pass + return 0; + } + // ---- Private delegate/helper methods ---- } diff --git a/tools/layoutlib/bridge/src/android/graphics/LinearGradient_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/LinearGradient_Delegate.java index a735ea1..a2ba758 100644 --- a/tools/layoutlib/bridge/src/android/graphics/LinearGradient_Delegate.java +++ b/tools/layoutlib/bridge/src/android/graphics/LinearGradient_Delegate.java @@ -54,7 +54,7 @@ public final class LinearGradient_Delegate extends Gradient_Delegate { // ---- native methods ---- @LayoutlibDelegate - /*package*/ static int nativeCreate1( + /*package*/ static int nativeCreate1(LinearGradient thisGradient, float x0, float y0, float x1, float y1, int colors[], float positions[], int tileMode) { LinearGradient_Delegate newDelegate = new LinearGradient_Delegate(x0, y0, x1, y1, @@ -63,13 +63,30 @@ public final class LinearGradient_Delegate extends Gradient_Delegate { } @LayoutlibDelegate - /*package*/ static int nativeCreate2( + /*package*/ static int nativeCreate2(LinearGradient thisGradient, float x0, float y0, float x1, float y1, int color0, int color1, int tileMode) { - return nativeCreate1(x0, y0, x1, y1, new int[] { color0, color1}, null /*positions*/, + return nativeCreate1(thisGradient, + x0, y0, x1, y1, new int[] { color0, color1}, null /*positions*/, tileMode); } + @LayoutlibDelegate + /*package*/ static int nativePostCreate1(LinearGradient thisGradient, + int native_shader, float x0, float y0, float x1, float y1, + int colors[], float positions[], int tileMode) { + // nothing to be done here. + return 0; + } + + @LayoutlibDelegate + /*package*/ static int nativePostCreate2(LinearGradient thisGradient, + int native_shader, float x0, float y0, float x1, float y1, + int color0, int color1, int tileMode) { + // nothing to be done here. + return 0; + } + // ---- Private delegate/helper methods ---- /** diff --git a/tools/layoutlib/bridge/src/android/graphics/Paint_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/Paint_Delegate.java index d4cf1f6..373f482 100644 --- a/tools/layoutlib/bridge/src/android/graphics/Paint_Delegate.java +++ b/tools/layoutlib/bridge/src/android/graphics/Paint_Delegate.java @@ -23,6 +23,7 @@ import com.android.tools.layoutlib.annotations.LayoutlibDelegate; import android.graphics.Paint.FontMetrics; import android.graphics.Paint.FontMetricsInt; +import android.text.TextUtils; import java.awt.BasicStroke; import java.awt.Font; @@ -89,6 +90,7 @@ public class Paint_Delegate { private MaskFilter_Delegate mMaskFilter; private Rasterizer_Delegate mRasterizer; + // ---- Public Helper methods ---- public static Paint_Delegate getDelegate(int native_paint) { @@ -391,7 +393,7 @@ public class Paint_Delegate { } @LayoutlibDelegate - /*package*/ static void setShadowLayer(Paint thisPaint, float radius, float dx, float dy, + /*package*/ static void nSetShadowLayer(Paint thisPaint, float radius, float dx, float dy, int color) { // FIXME Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED, @@ -938,7 +940,82 @@ public class Paint_Delegate { } @LayoutlibDelegate - /*package*/ static void native_getTextPath(int native_object, + /*package*/ static float native_getTextRunAdvances(int native_object, + char[] text, int index, int count, int contextIndex, int contextCount, + int flags, float[] advances, int advancesIndex) { + // get the delegate from the native int. + Paint_Delegate delegate = sManager.getDelegate(native_object); + if (delegate == null) { + return 0.f; + } + + if (delegate.mFonts.size() > 0) { + // FIXME: handle multi-char characters (see measureText) + float totalAdvance = 0; + for (int i = 0; i < count; i++) { + char c = text[i + index]; + boolean found = false; + for (FontInfo info : delegate.mFonts) { + if (info.mFont.canDisplay(c)) { + float adv = info.mMetrics.charWidth(c); + totalAdvance += adv; + if (advances != null) { + advances[i] = adv; + } + + found = true; + break; + } + } + + if (found == false) { + // no advance for this char. + if (advances != null) { + advances[i] = 0.f; + } + } + } + + return totalAdvance; + } + + return 0; + + } + + @LayoutlibDelegate + /*package*/ static float native_getTextRunAdvances(int native_object, + String text, int start, int end, int contextStart, int contextEnd, + int flags, float[] advances, int advancesIndex) { + // FIXME: support contextStart, contextEnd and direction flag + int count = end - start; + char[] buffer = TemporaryBuffer.obtain(count); + TextUtils.getChars(text, start, end, buffer, 0); + + return native_getTextRunAdvances(native_object, buffer, 0, count, contextStart, + contextEnd - contextStart, flags, advances, advancesIndex); + } + + @LayoutlibDelegate + /*package*/ static int native_getTextRunCursor(Paint thisPaint, int native_object, char[] text, + int contextStart, int contextLength, int flags, int offset, int cursorOpt) { + // FIXME + Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED, + "Paint.getTextRunCursor is not supported.", null, null /*data*/); + return 0; + } + + @LayoutlibDelegate + /*package*/ static int native_getTextRunCursor(Paint thisPaint, int native_object, String text, + int contextStart, int contextEnd, int flags, int offset, int cursorOpt) { + // FIXME + Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED, + "Paint.getTextRunCursor is not supported.", null, null /*data*/); + return 0; + } + + @LayoutlibDelegate + /*package*/ static void native_getTextPath(int native_object, int bidiFlags, char[] text, int index, int count, float x, float y, int path) { // FIXME Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED, @@ -946,7 +1023,7 @@ public class Paint_Delegate { } @LayoutlibDelegate - /*package*/ static void native_getTextPath(int native_object, + /*package*/ static void native_getTextPath(int native_object, int bidiFlags, String text, int start, int end, float x, float y, int path) { // FIXME Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED, diff --git a/tools/layoutlib/bridge/src/android/graphics/PorterDuffColorFilter_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/PorterDuffColorFilter_Delegate.java index 65c65a1..c45dbaa 100644 --- a/tools/layoutlib/bridge/src/android/graphics/PorterDuffColorFilter_Delegate.java +++ b/tools/layoutlib/bridge/src/android/graphics/PorterDuffColorFilter_Delegate.java @@ -60,5 +60,12 @@ public class PorterDuffColorFilter_Delegate extends ColorFilter_Delegate { return sManager.addNewDelegate(newDelegate); } + @LayoutlibDelegate + /*package*/ static int nCreatePorterDuffFilter(int nativeFilter, int srcColor, + int porterDuffMode) { + // pass + return 0; + } + // ---- Private delegate/helper methods ---- } diff --git a/tools/layoutlib/bridge/src/android/graphics/RadialGradient_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/RadialGradient_Delegate.java index bdc0ab1..9bf78b4 100644 --- a/tools/layoutlib/bridge/src/android/graphics/RadialGradient_Delegate.java +++ b/tools/layoutlib/bridge/src/android/graphics/RadialGradient_Delegate.java @@ -68,6 +68,20 @@ public class RadialGradient_Delegate extends Gradient_Delegate { tileMode); } + @LayoutlibDelegate + /*package*/ static int nativePostCreate1(int native_shader, float x, float y, float radius, + int colors[], float positions[], int tileMode) { + // nothing to be done here. + return 0; + } + + @LayoutlibDelegate + /*package*/ static int nativePostCreate2(int native_shader, float x, float y, float radius, + int color0, int color1, int tileMode) { + // nothing to be done here. + return 0; + } + // ---- Private delegate/helper methods ---- /** diff --git a/tools/layoutlib/bridge/src/android/graphics/Region_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/Region_Delegate.java index b0ee5c2..cb31b8f 100644 --- a/tools/layoutlib/bridge/src/android/graphics/Region_Delegate.java +++ b/tools/layoutlib/bridge/src/android/graphics/Region_Delegate.java @@ -469,6 +469,16 @@ public class Region_Delegate { return region1.mArea.equals(region2.mArea); } + @LayoutlibDelegate + /*package*/ static String nativeToString(int native_region) { + Region_Delegate region = sManager.getDelegate(native_region); + if (region == null) { + return "not found"; + } + + return region.mArea.toString(); + } + // ---- Private delegate/helper methods ---- } diff --git a/tools/layoutlib/bridge/src/android/graphics/Shader_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/Shader_Delegate.java index c6dd54b..368c0384 100644 --- a/tools/layoutlib/bridge/src/android/graphics/Shader_Delegate.java +++ b/tools/layoutlib/bridge/src/android/graphics/Shader_Delegate.java @@ -76,38 +76,13 @@ public abstract class Shader_Delegate { // ---- native methods ---- @LayoutlibDelegate - /*package*/ static void nativeDestructor(int native_shader) { + /*package*/ static void nativeDestructor(int native_shader, int native_skiaShader) { sManager.removeJavaReferenceFor(native_shader); } @LayoutlibDelegate - /*package*/ static boolean nativeGetLocalMatrix(int native_shader, int matrix_instance) { - // get the delegate from the native int. - Shader_Delegate shaderDelegate = sManager.getDelegate(native_shader); - if (shaderDelegate == null) { - return false; - } - - // get the (optional) out matrix. - Matrix_Delegate outMatrixDelegate = Matrix_Delegate.getDelegate(matrix_instance); - - if (shaderDelegate.mLocalMatrix == null || shaderDelegate.mLocalMatrix.isIdentity()) { - if (outMatrixDelegate != null) { - outMatrixDelegate.reset(); - } - return false; - } - - if (outMatrixDelegate != null) { - outMatrixDelegate.set(shaderDelegate.mLocalMatrix); - } - - return true; - } - - - @LayoutlibDelegate - /*package*/ static void nativeSetLocalMatrix(int native_shader, int matrix_instance) { + /*package*/ static void nativeSetLocalMatrix(int native_shader, int native_skiaShader, + int matrix_instance) { // get the delegate from the native int. Shader_Delegate shaderDelegate = sManager.getDelegate(native_shader); if (shaderDelegate == null) { diff --git a/tools/layoutlib/bridge/src/android/graphics/SweepGradient_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/SweepGradient_Delegate.java index f70f9cf..966e06e 100644 --- a/tools/layoutlib/bridge/src/android/graphics/SweepGradient_Delegate.java +++ b/tools/layoutlib/bridge/src/android/graphics/SweepGradient_Delegate.java @@ -62,6 +62,20 @@ public class SweepGradient_Delegate extends Gradient_Delegate { return nativeCreate1(x, y, new int[] { color0, color1 }, null /*positions*/); } + @LayoutlibDelegate + /*package*/ static int nativePostCreate1(int native_shader, float cx, float cy, + int[] colors, float[] positions) { + // nothing to be done here. + return 0; + } + + @LayoutlibDelegate + /*package*/ static int nativePostCreate2(int native_shader, float cx, float cy, + int color0, int color1) { + // nothing to be done here. + return 0; + } + // ---- Private delegate/helper methods ---- /** diff --git a/tools/layoutlib/bridge/src/android/view/LayoutInflater_Delegate.java b/tools/layoutlib/bridge/src/android/view/LayoutInflater_Delegate.java index d5266a5..0f3cf57 100644 --- a/tools/layoutlib/bridge/src/android/view/LayoutInflater_Delegate.java +++ b/tools/layoutlib/bridge/src/android/view/LayoutInflater_Delegate.java @@ -22,10 +22,7 @@ import com.android.tools.layoutlib.annotations.LayoutlibDelegate; import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; -import android.content.res.TypedArray; -import android.content.res.XmlResourceParser; import android.util.AttributeSet; -import android.util.Xml; import java.io.IOException; @@ -35,131 +32,66 @@ import java.io.IOException; * Through the layoutlib_create tool, the original methods of LayoutInflater have been replaced * by calls to methods of the same name in this delegate class. * - * Generally we don't want to copy-paste a huge method like that just for one small features, - * but because this is done after this platform is final and the next version (Honeycomb) has a - * better mechanism (or slightly less copy-paste), maintenance of this duplicated code is not - * a problem. - * */ public class LayoutInflater_Delegate { + /** + * Recursive method used to descend down the xml hierarchy and instantiate + * views, instantiate their children, and then call onFinishInflate(). + */ @LayoutlibDelegate - /*package*/ static void parseInclude(LayoutInflater thisInflater, - XmlPullParser parser, View parent, AttributeSet attrs) - throws XmlPullParserException, IOException { + /*package*/ static void rInflate(LayoutInflater thisInflater, + XmlPullParser parser, View parent, final AttributeSet attrs, + boolean finishInflate) throws XmlPullParserException, IOException { + + if (finishInflate == false) { + // this is a merge rInflate! + if (thisInflater instanceof BridgeInflater) { + ((BridgeInflater) thisInflater).setIsInMerge(true); + } + } + + // ---- START DEFAULT IMPLEMENTATION. + final int depth = parser.getDepth(); int type; - if (parent instanceof ViewGroup) { - final int layout = attrs.getAttributeResourceValue(null, "layout", 0); - if (layout == 0) { - final String value = attrs.getAttributeValue(null, "layout"); - if (value == null) { - throw new InflateException("You must specifiy a layout in the" - + " include tag: <include layout=\"@layout/layoutID\" />"); - } else { - throw new InflateException("You must specifiy a valid layout " - + "reference. The layout ID " + value + " is not valid."); + while (((type = parser.next()) != XmlPullParser.END_TAG || + parser.getDepth() > depth) && type != XmlPullParser.END_DOCUMENT) { + + if (type != XmlPullParser.START_TAG) { + continue; + } + + final String name = parser.getName(); + + if (LayoutInflater.TAG_REQUEST_FOCUS.equals(name)) { + thisInflater.parseRequestFocus(parser, parent); + } else if (LayoutInflater.TAG_INCLUDE.equals(name)) { + if (parser.getDepth() == 0) { + throw new InflateException("<include /> cannot be the root element"); } + thisInflater.parseInclude(parser, parent, attrs); + } else if (LayoutInflater.TAG_MERGE.equals(name)) { + throw new InflateException("<merge /> must be the root element"); } else { - final XmlResourceParser childParser = - thisInflater.getContext().getResources().getLayout(layout); - - try { - final AttributeSet childAttrs = Xml.asAttributeSet(childParser); - - while ((type = childParser.next()) != XmlPullParser.START_TAG && - type != XmlPullParser.END_DOCUMENT) { - // Empty. - } - - if (type != XmlPullParser.START_TAG) { - throw new InflateException(childParser.getPositionDescription() + - ": No start tag found!"); - } - - final String childName = childParser.getName(); - - if (LayoutInflater.TAG_MERGE.equals(childName)) { - // ---- START MODIFICATIONS ---- - if (thisInflater instanceof BridgeInflater) { - ((BridgeInflater) thisInflater).setIsInMerge(true); - } - // ---- END MODIFICATIONS ---- - - // Inflate all children. - thisInflater.rInflate(childParser, parent, childAttrs); - - // ---- START MODIFICATIONS ---- - if (thisInflater instanceof BridgeInflater) { - ((BridgeInflater) thisInflater).setIsInMerge(false); - } - // ---- END MODIFICATIONS ---- - } else { - final View view = thisInflater.createViewFromTag(childName, childAttrs); - final ViewGroup group = (ViewGroup) parent; - - // We try to load the layout params set in the <include /> tag. If - // they don't exist, we will rely on the layout params set in the - // included XML file. - // During a layoutparams generation, a runtime exception is thrown - // if either layout_width or layout_height is missing. We catch - // this exception and set localParams accordingly: true means we - // successfully loaded layout params from the <include /> tag, - // false means we need to rely on the included layout params. - ViewGroup.LayoutParams params = null; - try { - params = group.generateLayoutParams(attrs); - } catch (RuntimeException e) { - params = group.generateLayoutParams(childAttrs); - } finally { - if (params != null) { - view.setLayoutParams(params); - } - } - - // Inflate all children. - thisInflater.rInflate(childParser, view, childAttrs); - - // Attempt to override the included layout's android:id with the - // one set on the <include /> tag itself. - TypedArray a = thisInflater.mContext.obtainStyledAttributes(attrs, - com.android.internal.R.styleable.View, 0, 0); - int id = a.getResourceId(com.android.internal.R.styleable.View_id, View.NO_ID); - // While we're at it, let's try to override android:visibility. - int visibility = a.getInt(com.android.internal.R.styleable.View_visibility, -1); - a.recycle(); - - if (id != View.NO_ID) { - view.setId(id); - } - - switch (visibility) { - case 0: - view.setVisibility(View.VISIBLE); - break; - case 1: - view.setVisibility(View.INVISIBLE); - break; - case 2: - view.setVisibility(View.GONE); - break; - } - - group.addView(view); - } - } finally { - childParser.close(); - } + final View view = thisInflater.createViewFromTag(parent, name, attrs); + final ViewGroup viewGroup = (ViewGroup) parent; + final ViewGroup.LayoutParams params = viewGroup.generateLayoutParams(attrs); + thisInflater.rInflate(parser, view, attrs, true); + viewGroup.addView(view, params); } - } else { - throw new InflateException("<include /> can only be used inside of a ViewGroup"); } - final int currentDepth = parser.getDepth(); - while (((type = parser.next()) != XmlPullParser.END_TAG || - parser.getDepth() > currentDepth) && type != XmlPullParser.END_DOCUMENT) { - // Empty + if (finishInflate) parent.onFinishInflate(); + + // ---- END DEFAULT IMPLEMENTATION. + + if (finishInflate == false) { + // this is a merge rInflate! + if (thisInflater instanceof BridgeInflater) { + ((BridgeInflater) thisInflater).setIsInMerge(false); + } } } } diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/Bridge.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/Bridge.java index 2792100..acc7379 100644 --- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/Bridge.java +++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/Bridge.java @@ -194,7 +194,9 @@ public final class Bridge extends com.android.ide.common.rendering.api.Bridge { Capability.RENDER, Capability.LAYOUT_ONLY, Capability.EMBEDDED_LAYOUT, - Capability.VIEW_MANIPULATION); + Capability.VIEW_MANIPULATION, + Capability.PLAY_ANIMATION, + Capability.ANIMATED_VIEW_MANIPULATION); BridgeAssetManager.initSystem(); diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/BridgeRenderSession.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/BridgeRenderSession.java index bf22c4d..765fd99 100644 --- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/BridgeRenderSession.java +++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/BridgeRenderSession.java @@ -126,8 +126,19 @@ public class BridgeRenderSession extends RenderSession { @Override public Result animate(Object targetObject, String animationName, boolean isFrameworkAnimation, IAnimationListener listener) { - // Animation is only supported in API 11+ - return super.animate(targetObject, animationName, isFrameworkAnimation, listener); + try { + Bridge.prepareThread(); + mLastResult = mSession.acquire(RenderParams.DEFAULT_TIMEOUT); + if (mLastResult.isSuccess()) { + mLastResult = mSession.animate(targetObject, animationName, isFrameworkAnimation, + listener); + } + } finally { + mSession.release(); + Bridge.cleanupThread(); + } + + return mLastResult; } @Override diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java index c4eee17..7d794bd 100644 --- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java +++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java @@ -28,6 +28,7 @@ import com.android.resources.ResourceType; import com.android.util.Pair; import android.app.Activity; +import android.app.Fragment; import android.content.BroadcastReceiver; import android.content.ComponentName; import android.content.ContentResolver; @@ -44,6 +45,7 @@ import android.content.res.Configuration; import android.content.res.Resources; import android.content.res.TypedArray; import android.content.res.Resources.Theme; +import android.database.DatabaseErrorHandler; import android.database.sqlite.SQLiteDatabase; import android.database.sqlite.SQLiteDatabase.CursorFactory; import android.graphics.Bitmap; @@ -125,6 +127,9 @@ public final class BridgeContext extends Activity { mRenderResources = renderResources; + mFragments.mCurState = Fragment.CREATED; + mFragments.mActivity = this; + mApplicationInfo = new ApplicationInfo(); mApplicationInfo.targetSdkVersion = targetSdkVersion; } @@ -988,6 +993,13 @@ public final class BridgeContext extends Activity { } @Override + public SQLiteDatabase openOrCreateDatabase(String arg0, int arg1, + CursorFactory arg2, DatabaseErrorHandler arg3) { + // TODO Auto-generated method stub + return null; + } + + @Override public Drawable peekWallpaper() { // TODO Auto-generated method stub return null; @@ -1125,6 +1137,12 @@ public final class BridgeContext extends Activity { } @Override + public void startActivities(Intent[] arg0) { + // TODO Auto-generated method stub + + } + + @Override public boolean isRestricted() { return false; } diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeInflater.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeInflater.java index cb8d8dd..5740e8b 100644 --- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeInflater.java +++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeInflater.java @@ -122,10 +122,10 @@ public final class BridgeInflater extends LayoutInflater { } @Override - public View createViewFromTag(String name, AttributeSet attrs) { + public View createViewFromTag(View parent, String name, AttributeSet attrs) { View view = null; try { - view = super.createViewFromTag(name, attrs); + view = super.createViewFromTag(parent, name, attrs); } catch (InflateException e) { // try to load the class from using the custom view loader try { diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeWindow.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeWindow.java index 0efa102..a8da377 100644 --- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeWindow.java +++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeWindow.java @@ -22,6 +22,7 @@ import android.os.Bundle; import android.os.IBinder; import android.os.ParcelFileDescriptor; import android.os.RemoteException; +import android.view.DragEvent; import android.view.IWindow; import android.view.KeyEvent; import android.view.MotionEvent; @@ -81,6 +82,10 @@ public final class BridgeWindow implements IWindow { // pass for now. } + public void dispatchDragEvent(DragEvent event) { + // pass for now. + } + public void dispatchSystemUiVisibilityChanged(int visibility) { // pass for now. } diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeWindowSession.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeWindowSession.java index 7866bfa..8422d48 100644 --- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeWindowSession.java +++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeWindowSession.java @@ -16,6 +16,7 @@ package com.android.layoutlib.bridge.android; +import android.content.ClipData; import android.content.res.Configuration; import android.graphics.Rect; import android.graphics.Region; @@ -25,6 +26,7 @@ import android.os.RemoteException; import android.view.IWindow; import android.view.IWindowSession; import android.view.InputChannel; +import android.view.MotionEvent; import android.view.Surface; import android.view.SurfaceView; import android.view.WindowManager.LayoutParams; @@ -52,6 +54,10 @@ public final class BridgeWindowSession implements IWindowSession { // pass for now. } + public void finishKey(IWindow arg0) throws RemoteException { + // pass for now. + } + public boolean getInTouchMode() throws RemoteException { // pass for now. return false; @@ -62,6 +68,16 @@ public final class BridgeWindowSession implements IWindowSession { return false; } + public MotionEvent getPendingPointerMove(IWindow arg0) throws RemoteException { + // pass for now. + return null; + } + + public MotionEvent getPendingTrackballMove(IWindow arg0) throws RemoteException { + // pass for now. + return null; + } + public int relayout(IWindow arg0, LayoutParams arg1, int arg2, int arg3, int arg4, boolean arg4_5, Rect arg5, Rect arg6, Rect arg7, Configuration arg7b, Surface arg8) throws RemoteException { @@ -85,6 +101,38 @@ public final class BridgeWindowSession implements IWindowSession { // pass for now. } + public void setInsets(IWindow window, int touchable, Rect contentInsets, + Rect visibleInsets, Region touchableRegion) { + // pass for now. + } + + public IBinder prepareDrag(IWindow window, int flags, + int thumbnailWidth, int thumbnailHeight, Surface outSurface) + throws RemoteException { + // pass for now + return null; + } + + public boolean performDrag(IWindow window, IBinder dragToken, + float touchX, float touchY, float thumbCenterX, float thumbCenterY, + ClipData data) + throws RemoteException { + // pass for now + return false; + } + + public void reportDropResult(IWindow window, boolean consumed) throws RemoteException { + // pass for now + } + + public void dragRecipientEntered(IWindow window) throws RemoteException { + // pass for now + } + + public void dragRecipientExited(IWindow window) throws RemoteException { + // pass for now + } + public void setWallpaperPosition(IBinder window, float x, float y, float xStep, float yStep) { // pass for now. @@ -104,13 +152,18 @@ public final class BridgeWindowSession implements IWindowSession { // pass for now. } + public void closeSystemDialogs(String reason) { + // pass for now. + } + public IBinder asBinder() { // pass for now. return null; } - public void setInsets(IWindow arg0, int arg1, Rect arg2, Rect arg3) throws RemoteException { + public IBinder prepareDrag(IWindow arg0, boolean arg1, int arg2, int arg3, Surface arg4) + throws RemoteException { // TODO Auto-generated method stub - + return null; } } diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/FakeActionBar.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/FakeActionBar.java new file mode 100644 index 0000000..3af4e3a --- /dev/null +++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/FakeActionBar.java @@ -0,0 +1,47 @@ +/* + * Copyright (C) 2011 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. + */ + +package com.android.layoutlib.bridge.bars; + +import com.android.resources.Density; + +import org.xmlpull.v1.XmlPullParserException; + +import android.content.Context; +import android.widget.TextView; + +public class FakeActionBar extends CustomBar { + + private TextView mTextView; + + public FakeActionBar(Context context, Density density, String label, String icon) + throws XmlPullParserException { + super(context, density, "/bars/action_bar.xml"); + + // Cannot access the inside items through id because no R.id values have been + // created for them. + // We do know the order though. + loadIcon(0, icon); + mTextView = setText(1, label); + + setStyle("actionBarStyle"); + } + + @Override + protected TextView getStyleableTextView() { + return mTextView; + } +} diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/PhoneSystemBar.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/PhoneSystemBar.java index 5507ef9..9fab51a 100644 --- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/PhoneSystemBar.java +++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/PhoneSystemBar.java @@ -38,7 +38,7 @@ public class PhoneSystemBar extends CustomBar { // Cannot access the inside items through id because no R.id values have been // created for them. // We do know the order though. - // 0 is the spacer. + // 0 is the spacer loadIcon(1, "stat_sys_wifi_signal_4_fully.png", density); Drawable drawable = loadIcon(2, ResourceType.DRAWABLE, "stat_sys_battery_charge"); if (drawable instanceof LevelListDrawable) { diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/TabletSystemBar.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/TabletSystemBar.java new file mode 100644 index 0000000..5ca68fa --- /dev/null +++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/TabletSystemBar.java @@ -0,0 +1,54 @@ +/* + * Copyright (C) 2011 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. + */ + +package com.android.layoutlib.bridge.bars; + +import com.android.resources.Density; +import com.android.resources.ResourceType; + +import org.xmlpull.v1.XmlPullParserException; + +import android.content.Context; +import android.graphics.drawable.Drawable; +import android.graphics.drawable.LevelListDrawable; +import android.widget.TextView; + +public class TabletSystemBar extends CustomBar { + + public TabletSystemBar(Context context, Density density) throws XmlPullParserException { + super(context, density, "/bars/tablet_system_bar.xml"); + + setBackgroundColor(0xFF000000); + + // Cannot access the inside items through id because no R.id values have been + // created for them. + // We do know the order though. + loadIcon(0, "ic_sysbar_back_default.png", density); + loadIcon(1, "ic_sysbar_home_default.png", density); + loadIcon(2, "ic_sysbar_recent_default.png", density); + // 3 is the spacer + loadIcon(4, "stat_sys_wifi_signal_4_fully.png", density); + Drawable drawable = loadIcon(5, ResourceType.DRAWABLE, "stat_sys_battery_charge"); + if (drawable instanceof LevelListDrawable) { + ((LevelListDrawable) drawable).setLevel(100); + } + } + + @Override + protected TextView getStyleableTextView() { + return null; + } +} diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/AnimationThread.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/AnimationThread.java new file mode 100644 index 0000000..4c18656 --- /dev/null +++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/AnimationThread.java @@ -0,0 +1,170 @@ +/* + * Copyright (C) 2010 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. + */ + +package com.android.layoutlib.bridge.impl; + +import com.android.ide.common.rendering.api.IAnimationListener; +import com.android.ide.common.rendering.api.RenderSession; +import com.android.ide.common.rendering.api.Result; +import com.android.ide.common.rendering.api.Result.Status; +import com.android.layoutlib.bridge.Bridge; + +import android.animation.ValueAnimator; +import android.os.Handler; +import android.os.Handler_Delegate; +import android.os.Message; +import android.os.Handler_Delegate.IHandlerCallback; + +import java.util.PriorityQueue; +import java.util.Queue; + +/** + * Abstract animation thread. + * <p/> + * This does not actually start an animation, instead it fakes a looper that will play whatever + * animation is sending messages to its own {@link Handler}. + * <p/> + * Classes should implement {@link #preAnimation()} and {@link #postAnimation()}. + * <p/> + * If {@link #preAnimation()} does not start an animation somehow then the thread doesn't do + * anything. + * + */ +public abstract class AnimationThread extends Thread { + + private static class MessageBundle implements Comparable<MessageBundle> { + final Handler mTarget; + final Message mMessage; + final long mUptimeMillis; + + MessageBundle(Handler target, Message message, long uptimeMillis) { + mTarget = target; + mMessage = message; + mUptimeMillis = uptimeMillis; + } + + public int compareTo(MessageBundle bundle) { + if (mUptimeMillis < bundle.mUptimeMillis) { + return -1; + } + return 1; + } + } + + private final RenderSessionImpl mSession; + + private Queue<MessageBundle> mQueue = new PriorityQueue<MessageBundle>(); + private final IAnimationListener mListener; + + public AnimationThread(RenderSessionImpl scene, String threadName, + IAnimationListener listener) { + super(threadName); + mSession = scene; + mListener = listener; + } + + public abstract Result preAnimation(); + public abstract void postAnimation(); + + @Override + public void run() { + Bridge.prepareThread(); + try { + Handler_Delegate.setCallback(new IHandlerCallback() { + public void sendMessageAtTime(Handler handler, Message msg, long uptimeMillis) { + if (msg.what == ValueAnimator.ANIMATION_START || + msg.what == ValueAnimator.ANIMATION_FRAME) { + mQueue.add(new MessageBundle(handler, msg, uptimeMillis)); + } else { + // just ignore. + } + } + }); + + // call out to the pre-animation work, which should start an animation or more. + Result result = preAnimation(); + if (result.isSuccess() == false) { + mListener.done(result); + } + + // loop the animation + RenderSession session = mSession.getSession(); + do { + // check early. + if (mListener.isCanceled()) { + break; + } + + // get the next message. + MessageBundle bundle = mQueue.poll(); + if (bundle == null) { + break; + } + + // sleep enough for this bundle to be on time + long currentTime = System.currentTimeMillis(); + if (currentTime < bundle.mUptimeMillis) { + try { + sleep(bundle.mUptimeMillis - currentTime); + } catch (InterruptedException e) { + // FIXME log/do something/sleep again? + e.printStackTrace(); + } + } + + // check after sleeping. + if (mListener.isCanceled()) { + break; + } + + // ready to do the work, acquire the scene. + result = mSession.acquire(250); + if (result.isSuccess() == false) { + mListener.done(result); + return; + } + + // process the bundle. If the animation is not finished, this will enqueue + // the next message, so mQueue will have another one. + try { + // check after acquiring in case it took a while. + if (mListener.isCanceled()) { + break; + } + + bundle.mTarget.handleMessage(bundle.mMessage); + if (mSession.render(false /*freshRender*/).isSuccess()) { + mListener.onNewFrame(session); + } + } finally { + mSession.release(); + } + } while (mListener.isCanceled() == false && mQueue.size() > 0); + + mListener.done(Status.SUCCESS.createResult()); + + } catch (Throwable throwable) { + // can't use Bridge.getLog() as the exception might be thrown outside + // of an acquire/release block. + mListener.done(Status.ERROR_UNKNOWN.createResult("Error playing animation", throwable)); + + } finally { + postAnimation(); + Handler_Delegate.setCallback(null); + Bridge.cleanupThread(); + } + } +} diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/GcSnapshot.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/GcSnapshot.java index 1cf64a8..21d6b1a 100644 --- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/GcSnapshot.java +++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/GcSnapshot.java @@ -166,6 +166,12 @@ public class GcSnapshot { return mOriginalCopy; } + void change() { + if (mBitmap != null) { + mBitmap.change(); + } + } + /** * Sets the clip for the graphics2D object associated with the layer. * This should be used over the normal Graphics2D setClip method. @@ -605,6 +611,7 @@ public class GcSnapshot { try { drawable.draw(configuredGraphics2D, paint); + layer.change(); } finally { // dispose Graphics2D object configuredGraphics2D.dispose(); diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/PlayAnimationThread.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/PlayAnimationThread.java new file mode 100644 index 0000000..35e5987 --- /dev/null +++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/PlayAnimationThread.java @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2010 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. + */ + +package com.android.layoutlib.bridge.impl; + +import com.android.ide.common.rendering.api.IAnimationListener; +import com.android.ide.common.rendering.api.Result; +import com.android.ide.common.rendering.api.Result.Status; + +import android.animation.Animator; + +public class PlayAnimationThread extends AnimationThread { + + private final Animator mAnimator; + + public PlayAnimationThread(Animator animator, RenderSessionImpl scene, String animName, + IAnimationListener listener) { + super(scene, animName, listener); + mAnimator = animator; + } + + @Override + public Result preAnimation() { + // start the animation. This will send a message to the handler right away, so + // the queue is filled when this method returns. + mAnimator.start(); + + return Status.SUCCESS.createResult(); + } + + @Override + public void postAnimation() { + // nothing to be done. + } +} diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/RenderDrawable.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/RenderDrawable.java index 10a4368..953d8cf 100644 --- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/RenderDrawable.java +++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/RenderDrawable.java @@ -87,6 +87,7 @@ public class RenderDrawable extends RenderAction<DrawableParams> { info.mHasWindowFocus = true; info.mWindowVisibility = View.VISIBLE; info.mInTouchMode = false; // this is so that we can display selections. + info.mHardwareAccelerated = false; content.dispatchAttachedToWindow(info, 0); diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/RenderSessionImpl.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/RenderSessionImpl.java index cfc047f..2fd58e4 100644 --- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/RenderSessionImpl.java +++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/RenderSessionImpl.java @@ -16,6 +16,7 @@ package com.android.layoutlib.bridge.impl; +import static com.android.ide.common.rendering.api.Result.Status.ERROR_ANIM_NOT_FOUND; import static com.android.ide.common.rendering.api.Result.Status.ERROR_INFLATION; import static com.android.ide.common.rendering.api.Result.Status.ERROR_NOT_INFLATED; import static com.android.ide.common.rendering.api.Result.Status.ERROR_UNKNOWN; @@ -42,7 +43,9 @@ import com.android.layoutlib.bridge.android.BridgeLayoutParamsMapAttributes; import com.android.layoutlib.bridge.android.BridgeWindow; import com.android.layoutlib.bridge.android.BridgeWindowSession; import com.android.layoutlib.bridge.android.BridgeXmlBlockParser; +import com.android.layoutlib.bridge.bars.FakeActionBar; import com.android.layoutlib.bridge.bars.PhoneSystemBar; +import com.android.layoutlib.bridge.bars.TabletSystemBar; import com.android.layoutlib.bridge.bars.TitleBar; import com.android.resources.ResourceType; import com.android.resources.ScreenSize; @@ -50,6 +53,11 @@ import com.android.util.Pair; import org.xmlpull.v1.XmlPullParserException; +import android.animation.Animator; +import android.animation.AnimatorInflater; +import android.animation.LayoutTransition; +import android.animation.LayoutTransition.TransitionListener; +import android.app.Fragment_Delegate; import android.graphics.Bitmap; import android.graphics.Bitmap_Delegate; import android.graphics.Canvas; @@ -64,6 +72,7 @@ import android.view.View.MeasureSpec; import android.view.ViewGroup.LayoutParams; import android.widget.FrameLayout; import android.widget.LinearLayout; +import android.widget.QuickContactBadge; import android.widget.TabHost; import android.widget.TabWidget; import android.widget.TabHost.TabSpec; @@ -103,7 +112,9 @@ public class RenderSessionImpl extends RenderAction<SessionParams> { private boolean mWindowIsFloating; private int mStatusBarSize; + private int mSystemBarSize; private int mTitleBarSize; + private int mActionBarSize; // information being returned through the API @@ -163,11 +174,13 @@ public class RenderSessionImpl extends RenderAction<SessionParams> { findBackground(resources); findStatusBar(resources, metrics); - findTitleBar(resources, metrics); + findActionBar(resources, metrics); + findSystemBar(resources, metrics); // build the inflater and parser. mInflater = new BridgeInflater(context, params.getProjectCallback()); context.setBridgeInflater(mInflater); + mInflater.setFactory2(context); mBlockParser = new BridgeXmlBlockParser( params.getLayoutDescription(), context, false /* platformResourceFlag */); @@ -201,16 +214,18 @@ public class RenderSessionImpl extends RenderAction<SessionParams> { * we're creating the following layout * +-------------------------------------------------+ - | System bar | + | System bar (only in phone UI) | +-------------------------------------------------+ | (Layout with background drawable) | | +---------------------------------------------+ | - | | Title (optional) | | + | | Title/Action bar (optional) | | | +---------------------------------------------+ | | | Content, vertical extending | | | | | | | +---------------------------------------------+ | +-------------------------------------------------+ + | System bar (only in tablet UI) | + +-------------------------------------------------+ */ @@ -242,8 +257,20 @@ public class RenderSessionImpl extends RenderAction<SessionParams> { topLayout.addView(backgroundLayout); - // if the theme says no title, then the size will be 0 - if (mTitleBarSize > 0) { + // if the theme says no title/action bar, then the size will be 0 + if (mActionBarSize > 0) { + try { + FakeActionBar actionBar = new FakeActionBar(context, + params.getDensity(), + params.getAppLabel(), params.getAppIcon()); + actionBar.setLayoutParams( + new LinearLayout.LayoutParams( + LayoutParams.MATCH_PARENT, mActionBarSize)); + backgroundLayout.addView(actionBar); + } catch (XmlPullParserException e) { + + } + } else if (mTitleBarSize > 0) { try { TitleBar titleBar = new TitleBar(context, params.getDensity(), params.getAppLabel()); @@ -256,6 +283,7 @@ public class RenderSessionImpl extends RenderAction<SessionParams> { } } + // content frame mContentRoot = new FrameLayout(context); layoutParams = new LinearLayout.LayoutParams( @@ -263,17 +291,38 @@ public class RenderSessionImpl extends RenderAction<SessionParams> { layoutParams.weight = 1; mContentRoot.setLayoutParams(layoutParams); backgroundLayout.addView(mContentRoot); + + if (mSystemBarSize > 0) { + // system bar + try { + TabletSystemBar systemBar = new TabletSystemBar(context, + params.getDensity()); + systemBar.setLayoutParams( + new LinearLayout.LayoutParams( + LayoutParams.MATCH_PARENT, mSystemBarSize)); + topLayout.addView(systemBar); + } catch (XmlPullParserException e) { + + } + } } + // Sets the project callback (custom view loader) to the fragment delegate so that + // it can instantiate the custom Fragment. + Fragment_Delegate.setProjectCallback(params.getProjectCallback()); + View view = mInflater.inflate(mBlockParser, mContentRoot); + Fragment_Delegate.setProjectCallback(null); + // set the AttachInfo on the root view. AttachInfo info = new AttachInfo(new BridgeWindowSession(), new BridgeWindow(), new Handler(), null); info.mHasWindowFocus = true; info.mWindowVisibility = View.VISIBLE; info.mInTouchMode = false; // this is so that we can display selections. + info.mHardwareAccelerated = false; mViewRoot.dispatchAttachedToWindow(info, 0); // post-inflate process. For now this supports TabHost/TabWidget @@ -445,6 +494,64 @@ public class RenderSessionImpl extends RenderAction<SessionParams> { } /** + * Animate an object + * <p> + * {@link #acquire(long)} must have been called before this. + * + * @throws IllegalStateException if the current context is different than the one owned by + * the scene, or if {@link #acquire(long)} was not called. + * + * @see RenderSession#animate(Object, String, boolean, IAnimationListener) + */ + public Result animate(Object targetObject, String animationName, + boolean isFrameworkAnimation, IAnimationListener listener) { + checkLock(); + + BridgeContext context = getContext(); + + // find the animation file. + ResourceValue animationResource = null; + int animationId = 0; + if (isFrameworkAnimation) { + animationResource = context.getRenderResources().getFrameworkResource( + ResourceType.ANIMATOR, animationName); + if (animationResource != null) { + animationId = Bridge.getResourceId(ResourceType.ANIMATOR, animationName); + } + } else { + animationResource = context.getRenderResources().getProjectResource( + ResourceType.ANIMATOR, animationName); + if (animationResource != null) { + animationId = context.getProjectCallback().getResourceId( + ResourceType.ANIMATOR, animationName); + } + } + + if (animationResource != null) { + try { + Animator anim = AnimatorInflater.loadAnimator(context, animationId); + if (anim != null) { + anim.setTarget(targetObject); + + new PlayAnimationThread(anim, this, animationName, listener).start(); + + return SUCCESS.createResult(); + } + } catch (Exception e) { + // get the real cause of the exception. + Throwable t = e; + while (t.getCause() != null) { + t = t.getCause(); + } + + return ERROR_UNKNOWN.createResult(t.getMessage(), t); + } + } + + return ERROR_ANIM_NOT_FOUND.createResult(); + } + + /** * Insert a new child into an existing parent. * <p> * {@link #acquire(long)} must have been called before this. @@ -455,7 +562,7 @@ public class RenderSessionImpl extends RenderAction<SessionParams> { * @see RenderSession#insertChild(Object, ILayoutPullParser, int, IAnimationListener) */ public Result insertChild(final ViewGroup parentView, ILayoutPullParser childXml, - final int index, final IAnimationListener listener) { + final int index, IAnimationListener listener) { checkLock(); BridgeContext context = getContext(); @@ -473,33 +580,17 @@ public class RenderSessionImpl extends RenderAction<SessionParams> { invalidateRenderingSize(); if (listener != null) { - // there is no support for animating views in this API level, so we fake the animation - // through a no animation thread. - new Thread("not animated insertChild") { - @Override - public void run() { - Result result = addView(parentView, child, index); - if (result.isSuccess() == false) { - listener.done(result); - } + new AnimationThread(this, "insertChild", listener) { - // ready to do the work, acquire the scene. - result = acquire(250); - if (result.isSuccess() == false) { - listener.done(result); - return; - } - - try { - result = render(false /*freshRender*/); - if (result.isSuccess()) { - listener.onNewFrame(RenderSessionImpl.this.getSession()); - } - } finally { - release(); - } + @Override + public Result preAnimation() { + parentView.setLayoutTransition(new LayoutTransition()); + return addView(parentView, child, index); + } - listener.done(result); + @Override + public void postAnimation() { + parentView.setLayoutTransition(null); } }.start(); @@ -571,36 +662,72 @@ public class RenderSessionImpl extends RenderAction<SessionParams> { if (listener != null) { final LayoutParams params = layoutParams; - // there is no support for animating views in this API level, so we fake the animation - // through a no animation thread. - new Thread("not animated moveChild") { - @Override - public void run() { - Result result = moveView(previousParent, newParentView, childView, index, - params); - if (result.isSuccess() == false) { - listener.done(result); - } + // there is no support for animating views across layouts, so in case the new and old + // parent views are different we fake the animation through a no animation thread. + if (previousParent != newParentView) { + new Thread("not animated moveChild") { + @Override + public void run() { + Result result = moveView(previousParent, newParentView, childView, index, + params); + if (result.isSuccess() == false) { + listener.done(result); + } + + // ready to do the work, acquire the scene. + result = acquire(250); + if (result.isSuccess() == false) { + listener.done(result); + return; + } + + try { + result = render(false /*freshRender*/); + if (result.isSuccess()) { + listener.onNewFrame(RenderSessionImpl.this.getSession()); + } + } finally { + release(); + } - // ready to do the work, acquire the scene. - result = acquire(250); - if (result.isSuccess() == false) { listener.done(result); - return; } + }.start(); + } else { + new AnimationThread(this, "moveChild", listener) { - try { - result = render(false /*freshRender*/); - if (result.isSuccess()) { - listener.onNewFrame(RenderSessionImpl.this.getSession()); - } - } finally { - release(); + @Override + public Result preAnimation() { + // set up the transition for the parent. + LayoutTransition transition = new LayoutTransition(); + previousParent.setLayoutTransition(transition); + + // tweak the animation durations and start delays (to match the duration of + // animation playing just before). + // Note: Cannot user Animation.setDuration() directly. Have to set it + // on the LayoutTransition. + transition.setDuration(LayoutTransition.DISAPPEARING, 100); + // CHANGE_DISAPPEARING plays after DISAPPEARING + transition.setStartDelay(LayoutTransition.CHANGE_DISAPPEARING, 100); + + transition.setDuration(LayoutTransition.CHANGE_DISAPPEARING, 100); + + transition.setDuration(LayoutTransition.CHANGE_APPEARING, 100); + // CHANGE_APPEARING plays after CHANGE_APPEARING + transition.setStartDelay(LayoutTransition.APPEARING, 100); + + transition.setDuration(LayoutTransition.APPEARING, 100); + + return moveView(previousParent, newParentView, childView, index, params); } - listener.done(result); - } - }.start(); + @Override + public void postAnimation() { + previousParent.setLayoutTransition(null); + newParentView.setLayoutTransition(null); + } + }.start(); + } // always return success since the real status will come through the listener. return SUCCESS.createResult(layoutParams); @@ -636,17 +763,66 @@ public class RenderSessionImpl extends RenderAction<SessionParams> { private Result moveView(ViewGroup previousParent, final ViewGroup newParent, final View movedView, final int index, final LayoutParams params) { try { - // standard code with no animation. pretty simple. - previousParent.removeView(movedView); + // check if there is a transition on the previousParent. + LayoutTransition previousTransition = previousParent.getLayoutTransition(); + if (previousTransition != null) { + // in this case there is an animation. This means we have to wait for the child's + // parent reference to be null'ed out so that we can add it to the new parent. + // It is technically removed right before the DISAPPEARING animation is done (if + // the animation of this type is not null, otherwise it's after which is impossible + // to handle). + // Because there is no move animation, if the new parent is the same as the old + // parent, we need to wait until the CHANGE_DISAPPEARING animation is done before + // adding the child or the child will appear in its new location before the + // other children have made room for it. + + // add a listener to the transition to be notified of the actual removal. + previousTransition.addTransitionListener(new TransitionListener() { + private int mChangeDisappearingCount = 0; + + public void startTransition(LayoutTransition transition, ViewGroup container, + View view, int transitionType) { + if (transitionType == LayoutTransition.CHANGE_DISAPPEARING) { + mChangeDisappearingCount++; + } + } + + public void endTransition(LayoutTransition transition, ViewGroup container, + View view, int transitionType) { + if (transitionType == LayoutTransition.CHANGE_DISAPPEARING) { + mChangeDisappearingCount--; + } + + if (transitionType == LayoutTransition.CHANGE_DISAPPEARING && + mChangeDisappearingCount == 0) { + // add it to the parentView in the correct location + if (params != null) { + newParent.addView(movedView, index, params); + } else { + newParent.addView(movedView, index); + } + } + } + }); + + // remove the view from the current parent. + previousParent.removeView(movedView); - // add it to the parentView in the correct location - if (params != null) { - newParent.addView(movedView, index, params); + // and return since adding the view to the new parent is done in the listener. + return SUCCESS.createResult(); } else { - newParent.addView(movedView, index); - } + // standard code with no animation. pretty simple. + previousParent.removeView(movedView); - return SUCCESS.createResult(); + // add it to the parentView in the correct location + if (params != null) { + newParent.addView(movedView, index, params); + } else { + newParent.addView(movedView, index); + } + + return SUCCESS.createResult(); + } } catch (UnsupportedOperationException e) { // looks like this is a view class that doesn't support children manipulation! return ERROR_VIEWGROUP_NO_CHILDREN.createResult(); @@ -663,7 +839,7 @@ public class RenderSessionImpl extends RenderAction<SessionParams> { * * @see RenderSession#removeChild(Object, IAnimationListener) */ - public Result removeChild(final View childView, final IAnimationListener listener) { + public Result removeChild(final View childView, IAnimationListener listener) { checkLock(); invalidateRenderingSize(); @@ -671,33 +847,17 @@ public class RenderSessionImpl extends RenderAction<SessionParams> { final ViewGroup parent = (ViewGroup) childView.getParent(); if (listener != null) { - // there is no support for animating views in this API level, so we fake the animation - // through a no animation thread. - new Thread("not animated moveChild") { - @Override - public void run() { - Result result = removeView(parent, childView); - if (result.isSuccess() == false) { - listener.done(result); - } + new AnimationThread(this, "moveChild", listener) { - // ready to do the work, acquire the scene. - result = acquire(250); - if (result.isSuccess() == false) { - listener.done(result); - return; - } - - try { - result = render(false /*freshRender*/); - if (result.isSuccess()) { - listener.onNewFrame(RenderSessionImpl.this.getSession()); - } - } finally { - release(); - } + @Override + public Result preAnimation() { + parent.setLayoutTransition(new LayoutTransition()); + return removeView(parent, childView); + } - listener.done(result); + @Override + public void postAnimation() { + parent.setLayoutTransition(null); } }.start(); @@ -742,43 +902,50 @@ public class RenderSessionImpl extends RenderAction<SessionParams> { } } - private void findStatusBar(RenderResources resources, DisplayMetrics metrics) { - boolean windowFullscreen = getBooleanThemeValue(resources, - "windowFullscreen", false /*defaultValue*/); - - if (windowFullscreen == false && mWindowIsFloating == false) { - // default value - mStatusBarSize = DEFAULT_STATUS_BAR_HEIGHT; - - // get the real value - ResourceValue value = resources.getFrameworkResource(ResourceType.DIMEN, - "status_bar_height"); + private boolean isTabletUi() { + return getParams().getConfigScreenSize() == ScreenSize.XLARGE; + } - if (value != null) { - TypedValue typedValue = ResourceHelper.getValue(value.getValue()); - if (typedValue != null) { - // compute the pixel value based on the display metrics - mStatusBarSize = (int)typedValue.getDimension(metrics); + private void findStatusBar(RenderResources resources, DisplayMetrics metrics) { + if (isTabletUi() == false) { + boolean windowFullscreen = getBooleanThemeValue(resources, + "windowFullscreen", false /*defaultValue*/); + + if (windowFullscreen == false && mWindowIsFloating == false) { + // default value + mStatusBarSize = DEFAULT_STATUS_BAR_HEIGHT; + + // get the real value + ResourceValue value = resources.getFrameworkResource(ResourceType.DIMEN, + "status_bar_height"); + + if (value != null) { + TypedValue typedValue = ResourceHelper.getValue(value.getValue()); + if (typedValue != null) { + // compute the pixel value based on the display metrics + mStatusBarSize = (int)typedValue.getDimension(metrics); + } } } } } - private void findTitleBar(RenderResources resources, DisplayMetrics metrics) { + private void findActionBar(RenderResources resources, DisplayMetrics metrics) { if (mWindowIsFloating) { return; } - boolean windowNoTitle = getBooleanThemeValue(resources, - "windowNoTitle", false /*defaultValue*/); + boolean windowActionBar = getBooleanThemeValue(resources, + "windowActionBar", true /*defaultValue*/); - if (windowNoTitle == false) { + // if there's a value and it's false (default is true) + if (windowActionBar) { // default size of the window title bar - mTitleBarSize = DEFAULT_TITLE_BAR_HEIGHT; + mActionBarSize = DEFAULT_TITLE_BAR_HEIGHT; // get value from the theme. - ResourceValue value = resources.findItemInTheme("windowTitleSize"); + ResourceValue value = resources.findItemInTheme("actionBarSize"); // resolve it value = resources.resolveResValue(value); @@ -788,7 +955,53 @@ public class RenderSessionImpl extends RenderAction<SessionParams> { TypedValue typedValue = ResourceHelper.getValue(value.getValue()); if (typedValue != null) { // compute the pixel value based on the display metrics - mTitleBarSize = (int)typedValue.getDimension(metrics); + mActionBarSize = (int)typedValue.getDimension(metrics); + } + } + } else { + // action bar overrides title bar so only look for this one if action bar is hidden + boolean windowNoTitle = getBooleanThemeValue(resources, + "windowNoTitle", false /*defaultValue*/); + + if (windowNoTitle == false) { + + // default size of the window title bar + mTitleBarSize = DEFAULT_TITLE_BAR_HEIGHT; + + // get value from the theme. + ResourceValue value = resources.findItemInTheme("windowTitleSize"); + + // resolve it + value = resources.resolveResValue(value); + + if (value != null) { + // get the numerical value, if available + TypedValue typedValue = ResourceHelper.getValue(value.getValue()); + if (typedValue != null) { + // compute the pixel value based on the display metrics + mTitleBarSize = (int)typedValue.getDimension(metrics); + } + } + } + + } + } + + private void findSystemBar(RenderResources resources, DisplayMetrics metrics) { + if (isTabletUi() && mWindowIsFloating == false) { + + // default value + mSystemBarSize = 48; // ?? + + // get the real value + ResourceValue value = resources.getFrameworkResource(ResourceType.DIMEN, + "status_bar_height"); + + if (value != null) { + TypedValue typedValue = ResourceHelper.getValue(value.getValue()); + if (typedValue != null) { + // compute the pixel value based on the display metrics + mSystemBarSize = (int)typedValue.getDimension(metrics); } } } @@ -823,6 +1036,9 @@ public class RenderSessionImpl extends RenderAction<SessionParams> { throws PostInflateException { if (view instanceof TabHost) { setupTabHost((TabHost)view, projectCallback); + } else if (view instanceof QuickContactBadge) { + QuickContactBadge badge = (QuickContactBadge) view; + badge.setImageToDefault(); } else if (view instanceof ViewGroup) { ViewGroup group = (ViewGroup)view; final int count = group.getChildCount(); diff --git a/tools/layoutlib/bridge/src/libcore/icu/ICU_Delegate.java b/tools/layoutlib/bridge/src/libcore/icu/ICU_Delegate.java new file mode 100644 index 0000000..e6dc646 --- /dev/null +++ b/tools/layoutlib/bridge/src/libcore/icu/ICU_Delegate.java @@ -0,0 +1,186 @@ +/* + * Copyright (C) 2011 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. + */ + +package libcore.icu; + +import com.android.tools.layoutlib.annotations.LayoutlibDelegate; + +import java.util.Locale; + +/** + * Delegate implementing the native methods of libcore.icu.ICU + * + * Through the layoutlib_create tool, the original native methods of ICU have been replaced + * by calls to methods of the same name in this delegate class. + * + */ +public class ICU_Delegate { + + // --- Java delegates + + @LayoutlibDelegate + /*package*/ static String toLowerCase(String s, String localeName) { + return s.toLowerCase(); + } + + @LayoutlibDelegate + /*package*/ static String toUpperCase(String s, String localeName) { + return s.toUpperCase(); + } + + // --- Native methods accessing ICU's database. + + @LayoutlibDelegate + /*package*/ static String[] getAvailableBreakIteratorLocalesNative() { + return new String[0]; + } + + @LayoutlibDelegate + /*package*/ static String[] getAvailableCalendarLocalesNative() { + return new String[0]; + } + + @LayoutlibDelegate + /*package*/ static String[] getAvailableCollatorLocalesNative() { + return new String[0]; + } + + @LayoutlibDelegate + /*package*/ static String[] getAvailableDateFormatLocalesNative() { + return new String[0]; + } + + @LayoutlibDelegate + /*package*/ static String[] getAvailableLocalesNative() { + return new String[0]; + } + + @LayoutlibDelegate + /*package*/ static String[] getAvailableNumberFormatLocalesNative() { + return new String[0]; + } + + @LayoutlibDelegate + /*package*/ static String getCurrencyCodeNative(String locale) { + return ""; + } + + @LayoutlibDelegate + /*package*/ static int getCurrencyFractionDigitsNative(String currencyCode) { + return 0; + } + + @LayoutlibDelegate + /*package*/ static String getCurrencySymbolNative(String locale, String currencyCode) { + return ""; + } + + @LayoutlibDelegate + /*package*/ static String getDisplayCountryNative(String countryCode, String locale) { + return ""; + } + + @LayoutlibDelegate + /*package*/ static String getDisplayLanguageNative(String languageCode, String locale) { + return ""; + } + + @LayoutlibDelegate + /*package*/ static String getDisplayVariantNative(String variantCode, String locale) { + return ""; + } + + @LayoutlibDelegate + /*package*/ static String getISO3CountryNative(String locale) { + return ""; + } + + @LayoutlibDelegate + /*package*/ static String getISO3LanguageNative(String locale) { + return ""; + } + + @LayoutlibDelegate + /*package*/ static String[] getISOLanguagesNative() { + return Locale.getISOLanguages(); + } + + @LayoutlibDelegate + /*package*/ static String[] getISOCountriesNative() { + return Locale.getISOCountries(); + } + + @LayoutlibDelegate + /*package*/ static boolean initLocaleDataImpl(String locale, LocaleData result) { + + // Used by Calendar. + result.firstDayOfWeek = Integer.valueOf(1); + result.minimalDaysInFirstWeek = Integer.valueOf(1); + + // Used by DateFormatSymbols. + result.amPm = new String[] { "AM", "PM" }; + result.eras = new String[] { "BC", "AD" }; + + result.longMonthNames = new String[] { "January", "February", "March", "April", "May", + "June", "July", "August", "September", "October", "November", "December" }; + result.shortMonthNames = new String[] { "Jan", "Feb", "Mar", "Apr", "May", + "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" }; + result.longStandAloneMonthNames = result.longMonthNames; + result.shortStandAloneMonthNames = result.shortMonthNames; + + result.longWeekdayNames = new String[] { + "Monday" ,"Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday" }; + result.shortWeekdayNames = new String[] { + "Mon" ,"Tue", "Wed", "Thu", "Fri", "Sat", "Sun" }; + result.longStandAloneWeekdayNames = result.longWeekdayNames; + result.shortStandAloneWeekdayNames = result.shortWeekdayNames; + + result.fullTimeFormat = ""; + result.longTimeFormat = ""; + result.mediumTimeFormat = ""; + result.shortTimeFormat = ""; + + result.fullDateFormat = ""; + result.longDateFormat = ""; + result.mediumDateFormat = ""; + result.shortDateFormat = ""; + + // Used by DecimalFormatSymbols. + result.zeroDigit = '0'; + result.digit = '0'; + result.decimalSeparator = '.'; + result.groupingSeparator = ','; + result.patternSeparator = ' '; + result.percent = '%'; + result.perMill = '\u2030'; + result.monetarySeparator = ' '; + result.minusSign = '-'; + result.exponentSeparator = "e"; + result.infinity = "\u221E"; + result.NaN = "NaN"; + // Also used by Currency. + result.currencySymbol = "$"; + result.internationalCurrencySymbol = "USD"; + + // Used by DecimalFormat and NumberFormat. + result.numberPattern = "%f"; + result.integerPattern = "%d"; + result.currencyPattern = "%s"; + result.percentPattern = "%f"; + + return true; + } +} diff --git a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/CreateInfo.java b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/CreateInfo.java index 4b62e43..eff6bbc 100644 --- a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/CreateInfo.java +++ b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/CreateInfo.java @@ -94,12 +94,13 @@ public final class CreateInfo implements ICreateInfo { * The list of methods to rewrite as delegates. */ private final static String[] DELEGATE_METHODS = new String[] { + "android.app.Fragment#instantiate", //(Landroid/content/Context;Ljava/lang/String;Landroid/os/Bundle;)Landroid/app/Fragment;", "android.content.res.Resources$Theme#obtainStyledAttributes", "android.content.res.Resources$Theme#resolveAttribute", "android.graphics.BitmapFactory#finishDecode", "android.os.Handler#sendMessageAtTime", "android.os.Build#getString", - "android.view.LayoutInflater#parseInclude", + "android.view.LayoutInflater#rInflate", "android.view.View#isInEditMode", "com.android.internal.util.XmlUtils#convertValueToInt", // TODO: comment out once DelegateClass is working @@ -109,6 +110,7 @@ public final class CreateInfo implements ICreateInfo { * The list of classes on which to delegate all native methods. */ private final static String[] DELEGATE_CLASS_NATIVES = new String[] { + "android.animation.PropertyValuesHolder", "android.graphics.AvoidXfermode", "android.graphics.Bitmap", "android.graphics.BitmapFactory", @@ -148,6 +150,7 @@ public final class CreateInfo implements ICreateInfo { "android.graphics.Xfermode", "android.os.SystemClock", "android.util.FloatMath", + "libcore.icu.ICU", }; /** diff --git a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/Main.java b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/Main.java index 0fce7ef..ce48069 100644 --- a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/Main.java +++ b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/Main.java @@ -68,6 +68,7 @@ public class Main { AsmAnalyzer aa = new AsmAnalyzer(log, osJarPath, agen, new String[] { // derived from "android.view.View", + "android.app.Fragment" }, new String[] { // include classes "android.*", // for android.R diff --git a/tools/orientationplot/README.txt b/tools/orientationplot/README.txt new file mode 100644 index 0000000..0143510 --- /dev/null +++ b/tools/orientationplot/README.txt @@ -0,0 +1,87 @@ +This directory contains a simple python script for visualizing +the behavior of the WindowOrientationListener. + + +PREREQUISITES +------------- + +1. Python 2.6 +2. numpy +3. matplotlib + + +USAGE +----- + +The tool works by scaping the debug log output from WindowOrientationListener +for interesting data and then plotting it. + +1. Enable the Window Orientation Listener debugging data log using the + Development Settings in the Dev Tools application (Development.apk). + +2. Plug in the device. Ensure that it is the only device plugged in + since this script is of very little brain and will get confused otherwise. + +3. Run "orientationplot.py". + +4. When finished, remember to disable the debug log output since it is quite verbose! + + +WHAT IT ALL MEANS +----------------- + +The tool displays several time series graphs that plot the output of the +WindowOrientationListener. Here you can see the raw accelerometer data, +filtered accelerometer data, measured tilt and orientation angle, confidence +intervals for the proposed orientation and accelerometer latency. + +Things to look for: + +1. Ensure the filtering is not too aggressive. If the filter cut-off frequency is + less than about 1Hz, then the filtered accelorometer data becomes too smooth + and the latency for orientation detection goes up. One way to observe this + is by holding the device vertically in one orientation then sharply turning + it 90 degrees to a different orientation. Compared the rapid changes in the + raw accelerometer data with the smoothed out filtered data. If the filtering + is too aggressive, the filter response may lag by hundreds of milliseconds. + +2. Ensure that there is an appropriate gap between adjacent orientation angles + for hysteresis. Try holding the device in one orientation and slowly turning + it 90 degrees. Note that the confidence intervals will all drop to 0 at some + point in between the two orientations; that is the gap. The gap should be + observed between all adjacent pairs of orientations when turning the device + in either direction. + + Next try holding the device in one orientation and rapidly turning it end + over end to a midpoint about 45 degrees between two opposing orientations. + There should be no gap observed initially. The algorithm should pick one + of the orientations and settle into it (since it is obviously quite + different from the original orientation of the device). However, once it + settles, the confidence values should start trending to 0 again because + the measured orientation angle is now within the gap between the new + orientation and the adjacent orientation. + + In other words, the hysteresis gap applies only when the measured orientation + angle (say, 45 degrees) is between the current orientation's ideal angle + (say, 0 degrees) and an adjacent orientation's ideal angle (say, 90 degrees). + +3. Accelerometer jitter. The accelerometer latency graph displays the interval + between sensor events as reported by the SensorEvent.timestamp field. It + should be a fairly constant 60ms. If the latency jumps around wildly or + greatly exceeds 60ms then there is a problem with the accelerometer or the + sensor manager. + +4. The orientation angle is not measured when the tilt is too close to 90 or -90 + degrees (refer to MAX_TILT constant). Consequently, you should expect there + to be no data. Likewise, all dependent calculations are suppressed in this case + so there will be no orientation proposal either. + +5. Each orientation has its own bound on allowable tilt angles. It's a good idea to + verify that these limits are being enforced by gradually varying the tilt of + the device until it is inside/outside the limit for each orientation. + +6. Orientation changes should be significantly harder when the device is held + overhead. People reading on tablets in bed often have their head turned + a little to the side, or they hold the device loosely so its orientation + can be a bit unusual. The tilt is a good indicator of whether the device is + overhead. diff --git a/tools/orientationplot/orientationplot.py b/tools/orientationplot/orientationplot.py new file mode 100755 index 0000000..07449d4 --- /dev/null +++ b/tools/orientationplot/orientationplot.py @@ -0,0 +1,451 @@ +#!/usr/bin/env python2.6 +# +# Copyright (C) 2011 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. +# + +# +# Plots debug log output from WindowOrientationListener. +# See README.txt for details. +# + +import numpy as np +import matplotlib.pyplot as plot +import subprocess +import re +import fcntl +import os +import errno +import bisect +from datetime import datetime, timedelta + +# Parameters. +timespan = 15 # seconds total span shown +scrolljump = 5 # seconds jump when scrolling +timeticks = 1 # seconds between each time tick + +# Non-blocking stream wrapper. +class NonBlockingStream: + def __init__(self, stream): + fcntl.fcntl(stream, fcntl.F_SETFL, os.O_NONBLOCK) + self.stream = stream + self.buffer = '' + self.pos = 0 + + def readline(self): + while True: + index = self.buffer.find('\n', self.pos) + if index != -1: + result = self.buffer[self.pos:index] + self.pos = index + 1 + return result + + self.buffer = self.buffer[self.pos:] + self.pos = 0 + try: + chunk = os.read(self.stream.fileno(), 4096) + except OSError, e: + if e.errno == errno.EAGAIN: + return None + raise e + if len(chunk) == 0: + if len(self.buffer) == 0: + raise(EOFError) + else: + result = self.buffer + self.buffer = '' + self.pos = 0 + return result + self.buffer += chunk + +# Plotter +class Plotter: + def __init__(self, adbout): + self.adbout = adbout + + self.fig = plot.figure(1) + self.fig.suptitle('Window Orientation Listener', fontsize=12) + self.fig.set_dpi(96) + self.fig.set_size_inches(16, 12, forward=True) + + self.raw_acceleration_x = self._make_timeseries() + self.raw_acceleration_y = self._make_timeseries() + self.raw_acceleration_z = self._make_timeseries() + self.raw_acceleration_axes = self._add_timeseries_axes( + 1, 'Raw Acceleration', 'm/s^2', [-20, 20], + yticks=range(-15, 16, 5)) + self.raw_acceleration_line_x = self._add_timeseries_line( + self.raw_acceleration_axes, 'x', 'red') + self.raw_acceleration_line_y = self._add_timeseries_line( + self.raw_acceleration_axes, 'y', 'green') + self.raw_acceleration_line_z = self._add_timeseries_line( + self.raw_acceleration_axes, 'z', 'blue') + self._add_timeseries_legend(self.raw_acceleration_axes) + + shared_axis = self.raw_acceleration_axes + + self.filtered_acceleration_x = self._make_timeseries() + self.filtered_acceleration_y = self._make_timeseries() + self.filtered_acceleration_z = self._make_timeseries() + self.magnitude = self._make_timeseries() + self.filtered_acceleration_axes = self._add_timeseries_axes( + 2, 'Filtered Acceleration', 'm/s^2', [-20, 20], + sharex=shared_axis, + yticks=range(-15, 16, 5)) + self.filtered_acceleration_line_x = self._add_timeseries_line( + self.filtered_acceleration_axes, 'x', 'red') + self.filtered_acceleration_line_y = self._add_timeseries_line( + self.filtered_acceleration_axes, 'y', 'green') + self.filtered_acceleration_line_z = self._add_timeseries_line( + self.filtered_acceleration_axes, 'z', 'blue') + self.magnitude_line = self._add_timeseries_line( + self.filtered_acceleration_axes, 'magnitude', 'orange', linewidth=2) + self._add_timeseries_legend(self.filtered_acceleration_axes) + + self.tilt_angle = self._make_timeseries() + self.tilt_angle_axes = self._add_timeseries_axes( + 3, 'Tilt Angle', 'degrees', [-105, 105], + sharex=shared_axis, + yticks=range(-90, 91, 30)) + self.tilt_angle_line = self._add_timeseries_line( + self.tilt_angle_axes, 'tilt', 'black') + self._add_timeseries_legend(self.tilt_angle_axes) + + self.orientation_angle = self._make_timeseries() + self.orientation_angle_axes = self._add_timeseries_axes( + 4, 'Orientation Angle', 'degrees', [-25, 375], + sharex=shared_axis, + yticks=range(0, 361, 45)) + self.orientation_angle_line = self._add_timeseries_line( + self.orientation_angle_axes, 'orientation', 'black') + self._add_timeseries_legend(self.orientation_angle_axes) + + self.actual_orientation = self._make_timeseries() + self.proposed_orientation = self._make_timeseries() + self.orientation_axes = self._add_timeseries_axes( + 5, 'Actual / Proposed Orientation and Confidence', 'rotation', [-1, 4], + sharex=shared_axis, + yticks=range(0, 4)) + self.actual_orientation_line = self._add_timeseries_line( + self.orientation_axes, 'actual', 'black', linewidth=2) + self.proposed_orientation_line = self._add_timeseries_line( + self.orientation_axes, 'proposed', 'purple', linewidth=3) + self._add_timeseries_legend(self.orientation_axes) + + self.confidence = [[self._make_timeseries(), self._make_timeseries()] for i in range(0, 4)] + self.confidence_polys = [] + + self.combined_confidence = self._make_timeseries() + self.orientation_confidence = self._make_timeseries() + self.tilt_confidence = self._make_timeseries() + self.magnitude_confidence = self._make_timeseries() + self.confidence_axes = self._add_timeseries_axes( + 6, 'Proposed Orientation Confidence Factors', 'confidence', [-0.1, 1.1], + sharex=shared_axis, + yticks=[0.0, 0.2, 0.4, 0.6, 0.8, 1.0]) + self.combined_confidence_line = self._add_timeseries_line( + self.confidence_axes, 'combined', 'purple', linewidth=2) + self.orientation_confidence_line = self._add_timeseries_line( + self.confidence_axes, 'orientation', 'black') + self.tilt_confidence_line = self._add_timeseries_line( + self.confidence_axes, 'tilt', 'brown') + self.magnitude_confidence_line = self._add_timeseries_line( + self.confidence_axes, 'magnitude', 'orange') + self._add_timeseries_legend(self.confidence_axes) + + self.sample_latency = self._make_timeseries() + self.sample_latency_axes = self._add_timeseries_axes( + 7, 'Accelerometer Sampling Latency', 'ms', [-10, 500], + sharex=shared_axis, + yticks=range(0, 500, 100)) + self.sample_latency_line = self._add_timeseries_line( + self.sample_latency_axes, 'latency', 'black') + self._add_timeseries_legend(self.sample_latency_axes) + + self.timer = self.fig.canvas.new_timer(interval=100) + self.timer.add_callback(lambda: self.update()) + self.timer.start() + + self.timebase = None + self._reset_parse_state() + + # Initialize a time series. + def _make_timeseries(self): + return [[], []] + + # Add a subplot to the figure for a time series. + def _add_timeseries_axes(self, index, title, ylabel, ylim, yticks, sharex=None): + num_graphs = 7 + height = 0.9 / num_graphs + top = 0.95 - height * index + axes = self.fig.add_axes([0.1, top, 0.8, height], + xscale='linear', + xlim=[0, timespan], + ylabel=ylabel, + yscale='linear', + ylim=ylim, + sharex=sharex) + axes.text(0.02, 0.02, title, transform=axes.transAxes, fontsize=10, fontweight='bold') + axes.set_xlabel('time (s)', fontsize=10, fontweight='bold') + axes.set_ylabel(ylabel, fontsize=10, fontweight='bold') + axes.set_xticks(range(0, timespan + 1, timeticks)) + axes.set_yticks(yticks) + axes.grid(True) + + for label in axes.get_xticklabels(): + label.set_fontsize(9) + for label in axes.get_yticklabels(): + label.set_fontsize(9) + + return axes + + # Add a line to the axes for a time series. + def _add_timeseries_line(self, axes, label, color, linewidth=1): + return axes.plot([], label=label, color=color, linewidth=linewidth)[0] + + # Add a legend to a time series. + def _add_timeseries_legend(self, axes): + axes.legend( + loc='upper left', + bbox_to_anchor=(1.01, 1), + borderpad=0.1, + borderaxespad=0.1, + prop={'size': 10}) + + # Resets the parse state. + def _reset_parse_state(self): + self.parse_raw_acceleration_x = None + self.parse_raw_acceleration_y = None + self.parse_raw_acceleration_z = None + self.parse_filtered_acceleration_x = None + self.parse_filtered_acceleration_y = None + self.parse_filtered_acceleration_z = None + self.parse_magnitude = None + self.parse_tilt_angle = None + self.parse_orientation_angle = None + self.parse_proposed_orientation = None + self.parse_combined_confidence = None + self.parse_orientation_confidence = None + self.parse_tilt_confidence = None + self.parse_magnitude_confidence = None + self.parse_actual_orientation = None + self.parse_confidence = None + self.parse_sample_latency = None + + # Update samples. + def update(self): + timeindex = 0 + while True: + try: + line = self.adbout.readline() + except EOFError: + plot.close() + return + if line is None: + break + print line + + try: + timestamp = self._parse_timestamp(line) + except ValueError, e: + continue + if self.timebase is None: + self.timebase = timestamp + delta = timestamp - self.timebase + timeindex = delta.seconds + delta.microseconds * 0.000001 + + if line.find('Raw acceleration vector:') != -1: + self.parse_raw_acceleration_x = self._get_following_number(line, 'x=') + self.parse_raw_acceleration_y = self._get_following_number(line, 'y=') + self.parse_raw_acceleration_z = self._get_following_number(line, 'z=') + + if line.find('Filtered acceleration vector:') != -1: + self.parse_filtered_acceleration_x = self._get_following_number(line, 'x=') + self.parse_filtered_acceleration_y = self._get_following_number(line, 'y=') + self.parse_filtered_acceleration_z = self._get_following_number(line, 'z=') + + if line.find('magnitude=') != -1: + self.parse_magnitude = self._get_following_number(line, 'magnitude=') + + if line.find('tiltAngle=') != -1: + self.parse_tilt_angle = self._get_following_number(line, 'tiltAngle=') + + if line.find('orientationAngle=') != -1: + self.parse_orientation_angle = self._get_following_number(line, 'orientationAngle=') + + if line.find('Proposal:') != -1: + self.parse_proposed_orientation = self._get_following_number(line, 'proposedOrientation=') + self.parse_combined_confidence = self._get_following_number(line, 'combinedConfidence=') + self.parse_orientation_confidence = self._get_following_number(line, 'orientationConfidence=') + self.parse_tilt_confidence = self._get_following_number(line, 'tiltConfidence=') + self.parse_magnitude_confidence = self._get_following_number(line, 'magnitudeConfidence=') + + if line.find('Result:') != -1: + self.parse_actual_orientation = self._get_following_number(line, 'rotation=') + self.parse_confidence = self._get_following_array_of_numbers(line, 'confidence=') + self.parse_sample_latency = self._get_following_number(line, 'timeDeltaMS=') + + for i in range(0, 4): + if self.parse_confidence is not None: + self._append(self.confidence[i][0], timeindex, i) + self._append(self.confidence[i][1], timeindex, i + self.parse_confidence[i]) + else: + self._append(self.confidence[i][0], timeindex, None) + self._append(self.confidence[i][1], timeindex, None) + + self._append(self.raw_acceleration_x, timeindex, self.parse_raw_acceleration_x) + self._append(self.raw_acceleration_y, timeindex, self.parse_raw_acceleration_y) + self._append(self.raw_acceleration_z, timeindex, self.parse_raw_acceleration_z) + self._append(self.filtered_acceleration_x, timeindex, self.parse_filtered_acceleration_x) + self._append(self.filtered_acceleration_y, timeindex, self.parse_filtered_acceleration_y) + self._append(self.filtered_acceleration_z, timeindex, self.parse_filtered_acceleration_z) + self._append(self.magnitude, timeindex, self.parse_magnitude) + self._append(self.tilt_angle, timeindex, self.parse_tilt_angle) + self._append(self.orientation_angle, timeindex, self.parse_orientation_angle) + self._append(self.actual_orientation, timeindex, self.parse_actual_orientation) + self._append(self.proposed_orientation, timeindex, self.parse_proposed_orientation) + self._append(self.combined_confidence, timeindex, self.parse_combined_confidence) + self._append(self.orientation_confidence, timeindex, self.parse_orientation_confidence) + self._append(self.tilt_confidence, timeindex, self.parse_tilt_confidence) + self._append(self.magnitude_confidence, timeindex, self.parse_magnitude_confidence) + self._append(self.sample_latency, timeindex, self.parse_sample_latency) + self._reset_parse_state() + + # Scroll the plots. + if timeindex > timespan: + bottom = int(timeindex) - timespan + scrolljump + self.timebase += timedelta(seconds=bottom) + self._scroll(self.raw_acceleration_x, bottom) + self._scroll(self.raw_acceleration_y, bottom) + self._scroll(self.raw_acceleration_z, bottom) + self._scroll(self.filtered_acceleration_x, bottom) + self._scroll(self.filtered_acceleration_y, bottom) + self._scroll(self.filtered_acceleration_z, bottom) + self._scroll(self.magnitude, bottom) + self._scroll(self.tilt_angle, bottom) + self._scroll(self.orientation_angle, bottom) + self._scroll(self.actual_orientation, bottom) + self._scroll(self.proposed_orientation, bottom) + self._scroll(self.combined_confidence, bottom) + self._scroll(self.orientation_confidence, bottom) + self._scroll(self.tilt_confidence, bottom) + self._scroll(self.magnitude_confidence, bottom) + self._scroll(self.sample_latency, bottom) + for i in range(0, 4): + self._scroll(self.confidence[i][0], bottom) + self._scroll(self.confidence[i][1], bottom) + + # Redraw the plots. + self.raw_acceleration_line_x.set_data(self.raw_acceleration_x) + self.raw_acceleration_line_y.set_data(self.raw_acceleration_y) + self.raw_acceleration_line_z.set_data(self.raw_acceleration_z) + self.filtered_acceleration_line_x.set_data(self.filtered_acceleration_x) + self.filtered_acceleration_line_y.set_data(self.filtered_acceleration_y) + self.filtered_acceleration_line_z.set_data(self.filtered_acceleration_z) + self.magnitude_line.set_data(self.magnitude) + self.tilt_angle_line.set_data(self.tilt_angle) + self.orientation_angle_line.set_data(self.orientation_angle) + self.actual_orientation_line.set_data(self.actual_orientation) + self.proposed_orientation_line.set_data(self.proposed_orientation) + self.combined_confidence_line.set_data(self.combined_confidence) + self.orientation_confidence_line.set_data(self.orientation_confidence) + self.tilt_confidence_line.set_data(self.tilt_confidence) + self.magnitude_confidence_line.set_data(self.magnitude_confidence) + self.sample_latency_line.set_data(self.sample_latency) + + for poly in self.confidence_polys: + poly.remove() + self.confidence_polys = [] + for i in range(0, 4): + self.confidence_polys.append(self.orientation_axes.fill_between(self.confidence[i][0][0], + self.confidence[i][0][1], self.confidence[i][1][1], + facecolor='goldenrod', edgecolor='goldenrod')) + + self.fig.canvas.draw_idle() + + # Scroll a time series. + def _scroll(self, timeseries, bottom): + bottom_index = bisect.bisect_left(timeseries[0], bottom) + del timeseries[0][:bottom_index] + del timeseries[1][:bottom_index] + for i, timeindex in enumerate(timeseries[0]): + timeseries[0][i] = timeindex - bottom + + # Extract a word following the specified prefix. + def _get_following_word(self, line, prefix): + prefix_index = line.find(prefix) + if prefix_index == -1: + return None + start_index = prefix_index + len(prefix) + delim_index = line.find(',', start_index) + if delim_index == -1: + return line[start_index:] + else: + return line[start_index:delim_index] + + # Extract a number following the specified prefix. + def _get_following_number(self, line, prefix): + word = self._get_following_word(line, prefix) + if word is None: + return None + return float(word) + + # Extract an array of numbers following the specified prefix. + def _get_following_array_of_numbers(self, line, prefix): + prefix_index = line.find(prefix + '[') + if prefix_index == -1: + return None + start_index = prefix_index + len(prefix) + 1 + delim_index = line.find(']', start_index) + if delim_index == -1: + return None + + result = [] + while start_index < delim_index: + comma_index = line.find(', ', start_index, delim_index) + if comma_index == -1: + result.append(float(line[start_index:delim_index])) + break; + result.append(float(line[start_index:comma_index])) + start_index = comma_index + 2 + return result + + # Add a value to a time series. + def _append(self, timeseries, timeindex, number): + timeseries[0].append(timeindex) + timeseries[1].append(number) + + # Parse the logcat timestamp. + # Timestamp has the form '01-21 20:42:42.930' + def _parse_timestamp(self, line): + return datetime.strptime(line[0:18], '%m-%d %H:%M:%S.%f') + +# Notice +print "Window Orientation Listener plotting tool" +print "-----------------------------------------\n" +print "Please turn on the Window Orientation Listener logging in Development Settings." + +# Start adb. +print "Starting adb logcat.\n" + +adb = subprocess.Popen(['adb', 'logcat', '-s', '-v', 'time', 'WindowOrientationListener:V'], + stdout=subprocess.PIPE) +adbout = NonBlockingStream(adb.stdout) + +# Prepare plotter. +plotter = Plotter(adbout) +plotter.update() + +# Main loop. +plot.show() diff --git a/tools/preload/Record.java b/tools/preload/Record.java index 9d45a26..2f2ffaf 100644 --- a/tools/preload/Record.java +++ b/tools/preload/Record.java @@ -30,6 +30,8 @@ class Record { "com.google.android.apps.maps\\u003Adriveabout", "com.google.android.apps.maps:LocationFriendService", "com.google.android.apps.maps\\u003ALocationFriendService", + "com.google.android.apps.maps:NetworkLocationService", + "com.google.android.apps.maps\\u003ANetworkLocationService", }; enum Type { @@ -69,7 +71,7 @@ class Record { /** Record time (ns). */ final long time; - + /** Source file line# */ int sourceLineNumber; @@ -91,7 +93,7 @@ class Record { for (int i = 0; i < REPLACE_CLASSES.length; i+= 2) { line = line.replace(REPLACE_CLASSES[i], REPLACE_CLASSES[i+1]); } - + line = line.substring(1); String[] parts = line.split(":"); @@ -106,12 +108,12 @@ class Record { time = Long.parseLong(parts[6]); } - + /** * Decode any escaping that may have been written to the log line. - * + * * Supports unicode-style escaping: \\uXXXX = character in hex - * + * * @param rawField the field as it was written into the log * @result the same field with any escaped characters replaced */ @@ -122,11 +124,11 @@ class Record { String before = result.substring(0, offset); String escaped = result.substring(offset+2, offset+6); String after = result.substring(offset+6); - + result = String.format("%s%c%s", before, Integer.parseInt(escaped, 16), after); - // find another but don't recurse - offset = result.indexOf("\\u", offset + 1); + // find another but don't recurse + offset = result.indexOf("\\u", offset + 1); } return result; } @@ -135,13 +137,13 @@ class Record { * Converts a VM-style name to a language-style name. */ String vmTypeToLanguage(String typeName) { - // if the typename is (null), just return it as-is. This is probably in dexopt and + // if the typename is (null), just return it as-is. This is probably in dexopt and // will be discarded anyway. NOTE: This corresponds to the case in dalvik/vm/oo/Class.c // where dvmLinkClass() returns false and we clean up and exit. if ("(null)".equals(typeName)) { return typeName; } - + if (!typeName.startsWith("L") || !typeName.endsWith(";") ) { throw new AssertionError("Bad name: " + typeName + " in line " + sourceLineNumber); } diff --git a/tools/validatekeymaps/Android.mk b/tools/validatekeymaps/Android.mk new file mode 100644 index 0000000..90979e1 --- /dev/null +++ b/tools/validatekeymaps/Android.mk @@ -0,0 +1,34 @@ +# +# Copyright 2010 The Android Open Source Project +# +# Keymap validation tool. +# + +# This tool is prebuilt if we're doing an app-only build. +ifeq ($(TARGET_BUILD_APPS),) + +LOCAL_PATH:= $(call my-dir) +include $(CLEAR_VARS) + +LOCAL_SRC_FILES := \ + Main.cpp + +LOCAL_CFLAGS := -Wall -Werror + +#LOCAL_C_INCLUDES += + +LOCAL_STATIC_LIBRARIES := \ + libui \ + libutils \ + libcutils + +ifeq ($(HOST_OS),linux) +LOCAL_LDLIBS += -lpthread +endif + +LOCAL_MODULE := validatekeymaps +LOCAL_MODULE_TAGS := optional + +include $(BUILD_HOST_EXECUTABLE) + +endif # TARGET_BUILD_APPS diff --git a/tools/validatekeymaps/Main.cpp b/tools/validatekeymaps/Main.cpp new file mode 100644 index 0000000..097b109 --- /dev/null +++ b/tools/validatekeymaps/Main.cpp @@ -0,0 +1,129 @@ +/* + * Copyright (C) 2010 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. + */ + +#include <ui/KeyCharacterMap.h> +#include <ui/KeyLayoutMap.h> +#include <ui/VirtualKeyMap.h> +#include <utils/String8.h> + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +using namespace android; + +static const char* gProgName = "validatekeymaps"; + +enum FileType { + FILETYPE_UNKNOWN, + FILETYPE_KEYLAYOUT, + FILETYPE_KEYCHARACTERMAP, + FILETYPE_VIRTUALKEYDEFINITION, +}; + + +static void usage() { + fprintf(stderr, "Keymap Validation Tool\n\n"); + fprintf(stderr, "Usage:\n"); + fprintf(stderr, + " %s [*.kl] [*.kcm] [virtualkeys.*] [...]\n" + " Validates the specified key layouts, key character maps \n" + " or virtual key definitions.\n\n", + gProgName); +} + +static FileType getFileType(const char* filename) { + const char *extension = strrchr(filename, '.'); + if (extension) { + if (strcmp(extension, ".kl") == 0) { + return FILETYPE_KEYLAYOUT; + } + if (strcmp(extension, ".kcm") == 0) { + return FILETYPE_KEYCHARACTERMAP; + } + } + + if (strstr(filename, "virtualkeys.")) { + return FILETYPE_VIRTUALKEYDEFINITION; + } + + return FILETYPE_UNKNOWN; +} + +static bool validateFile(const char* filename) { + fprintf(stdout, "Validating file '%s'...\n", filename); + + FileType fileType = getFileType(filename); + switch (fileType) { + case FILETYPE_UNKNOWN: + fprintf(stderr, "Supported file types: *.kl, *.kcm, virtualkeys.*\n\n"); + return false; + + case FILETYPE_KEYLAYOUT: { + KeyLayoutMap* map; + status_t status = KeyLayoutMap::load(String8(filename), &map); + if (status) { + fprintf(stderr, "Error %d parsing key layout file.\n\n", status); + return false; + } + break; + } + + case FILETYPE_KEYCHARACTERMAP: { + KeyCharacterMap* map; + status_t status = KeyCharacterMap::load(String8(filename), &map); + if (status) { + fprintf(stderr, "Error %d parsing key character map file.\n\n", status); + return false; + } + break; + } + + case FILETYPE_VIRTUALKEYDEFINITION: { + VirtualKeyMap* map; + status_t status = VirtualKeyMap::load(String8(filename), &map); + if (status) { + fprintf(stderr, "Error %d parsing virtual key definition file.\n\n", status); + return false; + } + break; + } + } + + fputs("No errors.\n\n", stdout); + return true; +} + +int main(int argc, const char** argv) { + if (argc < 2) { + usage(); + return 1; + } + + int result = 0; + for (int i = 1; i < argc; i++) { + if (!validateFile(argv[i])) { + result = 1; + } + } + + if (result) { + fputs("Failed!\n", stderr); + } else { + fputs("Success.\n", stdout); + } + return result; +} |