summaryrefslogtreecommitdiffstats
path: root/tools/layoutlib
diff options
context:
space:
mode:
Diffstat (limited to 'tools/layoutlib')
-rw-r--r--tools/layoutlib/.idea/libraries/layoutlib_api_prebuilt.xml2
-rw-r--r--tools/layoutlib/.idea/misc.xml28
-rw-r--r--tools/layoutlib/bridge/src/android/text/format/DateFormat_Delegate.java5
-rw-r--r--tools/layoutlib/bridge/src/android/view/BridgeInflater.java30
-rw-r--r--tools/layoutlib/bridge/src/android/view/IWindowManagerImpl.java5
-rw-r--r--tools/layoutlib/bridge/src/android/widget/TimePickerClockDelegate_Delegate.java (renamed from tools/layoutlib/bridge/src/android/widget/TimePickerSpinnerDelegate_Delegate.java)8
-rw-r--r--tools/layoutlib/bridge/src/com/android/layoutlib/bridge/BridgeConstants.java3
-rw-r--r--tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContentProvider.java2
-rw-r--r--tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java68
-rw-r--r--tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgePowerManager.java5
-rw-r--r--tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeWindow.java4
-rw-r--r--tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeWindowSession.java8
-rw-r--r--tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/SessionParamsFlags.java2
-rw-r--r--tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/support/RecyclerViewUtil.java206
-rw-r--r--tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/Config.java7
-rw-r--r--tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/RenderDrawable.java3
-rw-r--r--tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/RenderSessionImpl.java23
-rw-r--r--tools/layoutlib/bridge/src/com/android/layoutlib/bridge/util/ReflectionUtils.java75
-rw-r--r--tools/layoutlib/bridge/src/libcore/icu/ICU_Delegate.java2
-rw-r--r--tools/layoutlib/bridge/tests/src/com/android/layoutlib/bridge/intensive/Main.java20
-rw-r--r--tools/layoutlib/create/src/com/android/tools/layoutlib/create/CreateInfo.java4
-rw-r--r--tools/layoutlib/create/src/com/android/tools/layoutlib/create/ReplaceMethodCallsAdapter.java32
-rw-r--r--tools/layoutlib/create/src/com/android/tools/layoutlib/java/LinkedHashMap_Delegate.java36
23 files changed, 521 insertions, 57 deletions
diff --git a/tools/layoutlib/.idea/libraries/layoutlib_api_prebuilt.xml b/tools/layoutlib/.idea/libraries/layoutlib_api_prebuilt.xml
index 5952002..a873600 100644
--- a/tools/layoutlib/.idea/libraries/layoutlib_api_prebuilt.xml
+++ b/tools/layoutlib/.idea/libraries/layoutlib_api_prebuilt.xml
@@ -5,7 +5,7 @@
</CLASSES>
<JAVADOC />
<SOURCES>
- <root url="file://$ANDROID_SRC$/tools/base/layoutlib-api/src/main/java" />
+ <root url="jar://$PROJECT_DIR$/../../../../prebuilts/misc/common/layoutlib_api/layoutlib_api-sources.jar!/" />
</SOURCES>
</library>
</component> \ No newline at end of file
diff --git a/tools/layoutlib/.idea/misc.xml b/tools/layoutlib/.idea/misc.xml
index fa48f70..94bcd36 100644
--- a/tools/layoutlib/.idea/misc.xml
+++ b/tools/layoutlib/.idea/misc.xml
@@ -9,7 +9,33 @@
<component name="FrameworkDetectionExcludesConfiguration">
<type id="android" />
</component>
- <component name="ProjectRootManager" version="2" languageLevel="JDK_1_6" assert-keyword="true" jdk-15="true" project-jdk-name="1.7" project-jdk-type="JavaSDK">
+ <component name="NullableNotNullManager">
+ <option name="myDefaultNullable" value="com.android.annotations.Nullable" />
+ <option name="myDefaultNotNull" value="com.android.annotations.NonNull" />
+ <option name="myNullables">
+ <value>
+ <list size="5">
+ <item index="0" class="java.lang.String" itemvalue="org.jetbrains.annotations.Nullable" />
+ <item index="1" class="java.lang.String" itemvalue="javax.annotation.Nullable" />
+ <item index="2" class="java.lang.String" itemvalue="edu.umd.cs.findbugs.annotations.Nullable" />
+ <item index="3" class="java.lang.String" itemvalue="android.support.annotation.Nullable" />
+ <item index="4" class="java.lang.String" itemvalue="com.android.annotations.Nullable" />
+ </list>
+ </value>
+ </option>
+ <option name="myNotNulls">
+ <value>
+ <list size="5">
+ <item index="0" class="java.lang.String" itemvalue="org.jetbrains.annotations.NotNull" />
+ <item index="1" class="java.lang.String" itemvalue="javax.annotation.Nonnull" />
+ <item index="2" class="java.lang.String" itemvalue="edu.umd.cs.findbugs.annotations.NonNull" />
+ <item index="3" class="java.lang.String" itemvalue="android.support.annotation.NonNull" />
+ <item index="4" class="java.lang.String" itemvalue="com.android.annotations.NonNull" />
+ </list>
+ </value>
+ </option>
+ </component>
+ <component name="ProjectRootManager" version="2" languageLevel="JDK_1_6" default="false" assert-keyword="true" jdk-15="true" project-jdk-name="1.6" project-jdk-type="JavaSDK">
<output url="file://$PROJECT_DIR$/out" />
</component>
</project> \ No newline at end of file
diff --git a/tools/layoutlib/bridge/src/android/text/format/DateFormat_Delegate.java b/tools/layoutlib/bridge/src/android/text/format/DateFormat_Delegate.java
index 8cd1a69..1e4f213 100644
--- a/tools/layoutlib/bridge/src/android/text/format/DateFormat_Delegate.java
+++ b/tools/layoutlib/bridge/src/android/text/format/DateFormat_Delegate.java
@@ -34,4 +34,9 @@ public class DateFormat_Delegate {
/*package*/ static boolean is24HourFormat(Context context) {
return false;
}
+
+ @LayoutlibDelegate
+ /*package*/ static boolean is24HourFormat(Context context, int userHandle) {
+ return false;
+ }
}
diff --git a/tools/layoutlib/bridge/src/android/view/BridgeInflater.java b/tools/layoutlib/bridge/src/android/view/BridgeInflater.java
index 7e4ff69..fbd5e2a 100644
--- a/tools/layoutlib/bridge/src/android/view/BridgeInflater.java
+++ b/tools/layoutlib/bridge/src/android/view/BridgeInflater.java
@@ -22,9 +22,13 @@ import com.android.ide.common.rendering.api.MergeCookie;
import com.android.ide.common.rendering.api.ResourceReference;
import com.android.ide.common.rendering.api.ResourceValue;
import com.android.layoutlib.bridge.Bridge;
+import com.android.layoutlib.bridge.BridgeConstants;
import com.android.layoutlib.bridge.android.BridgeContext;
import com.android.layoutlib.bridge.android.BridgeXmlBlockParser;
+import com.android.layoutlib.bridge.android.support.RecyclerViewUtil;
+import com.android.layoutlib.bridge.android.support.RecyclerViewUtil.LayoutManagerType;
import com.android.layoutlib.bridge.impl.ParserFactory;
+import com.android.layoutlib.bridge.impl.RenderSessionImpl;
import com.android.resources.ResourceType;
import com.android.util.Pair;
@@ -111,8 +115,7 @@ public final class BridgeInflater extends LayoutInflater {
} catch (Exception e) {
// Wrap the real exception in a ClassNotFoundException, so that the calling method
// can deal with it.
- ClassNotFoundException exception = new ClassNotFoundException("onCreateView", e);
- throw exception;
+ throw new ClassNotFoundException("onCreateView", e);
}
setupViewInContext(view, attrs);
@@ -123,7 +126,7 @@ public final class BridgeInflater extends LayoutInflater {
@Override
public View createViewFromTag(View parent, String name, AttributeSet attrs,
boolean inheritContext) {
- View view = null;
+ View view;
try {
view = super.createViewFromTag(parent, name, attrs, inheritContext);
} catch (InflateException e) {
@@ -134,7 +137,7 @@ public final class BridgeInflater extends LayoutInflater {
// Wrap the real exception in an InflateException so that the calling
// method can deal with it.
InflateException exception = new InflateException();
- if (e2.getClass().equals(ClassNotFoundException.class) == false) {
+ if (!e2.getClass().equals(ClassNotFoundException.class)) {
exception.initCause(e2);
} else {
exception.initCause(e);
@@ -184,7 +187,7 @@ public final class BridgeInflater extends LayoutInflater {
return inflate(bridgeParser, root);
} catch (Exception e) {
Bridge.getLog().error(LayoutLog.TAG_RESOURCES_READ,
- "Failed to parse file " + f.getAbsolutePath(), e, null /*data*/);
+ "Failed to parse file " + f.getAbsolutePath(), e, null);
return null;
}
@@ -194,8 +197,7 @@ public final class BridgeInflater extends LayoutInflater {
return null;
}
- private View loadCustomView(String name, AttributeSet attrs) throws ClassNotFoundException,
- Exception{
+ private View loadCustomView(String name, AttributeSet attrs) throws Exception {
if (mProjectCallback != null) {
// first get the classname in case it's not the node name
if (name.equals("view")) {
@@ -227,6 +229,20 @@ public final class BridgeInflater extends LayoutInflater {
if (viewKey != null) {
bc.addViewKey(view, viewKey);
}
+ if (RenderSessionImpl.isInstanceOf(view, RecyclerViewUtil.CN_RECYCLER_VIEW)) {
+ String type = attrs.getAttributeValue(BridgeConstants.NS_RESOURCES,
+ BridgeConstants.ATTR_LAYOUT_MANAGER_TYPE);
+ if (type != null) {
+ LayoutManagerType layoutManagerType = LayoutManagerType.getByDisplayName(type);
+ if (layoutManagerType == null) {
+ Bridge.getLog().warning(LayoutLog.TAG_UNSUPPORTED,
+ "LayoutManager (" + type + ") not found, falling back to " +
+ "LinearLayoutManager", null);
+ } else {
+ bc.addCookie(view, layoutManagerType);
+ }
+ }
+ }
}
}
diff --git a/tools/layoutlib/bridge/src/android/view/IWindowManagerImpl.java b/tools/layoutlib/bridge/src/android/view/IWindowManagerImpl.java
index c403ce6..5176419 100644
--- a/tools/layoutlib/bridge/src/android/view/IWindowManagerImpl.java
+++ b/tools/layoutlib/bridge/src/android/view/IWindowManagerImpl.java
@@ -228,6 +228,11 @@ public class IWindowManagerImpl implements IWindowManager {
}
@Override
+ public void overridePendingAppTransitionInPlace(String packageName, int anim) {
+ // TODO Auto-generated method stub
+ }
+
+ @Override
public void pauseKeyDispatching(IBinder arg0) throws RemoteException {
// TODO Auto-generated method stub
diff --git a/tools/layoutlib/bridge/src/android/widget/TimePickerSpinnerDelegate_Delegate.java b/tools/layoutlib/bridge/src/android/widget/TimePickerClockDelegate_Delegate.java
index c9d35b9..1bd9830 100644
--- a/tools/layoutlib/bridge/src/android/widget/TimePickerSpinnerDelegate_Delegate.java
+++ b/tools/layoutlib/bridge/src/android/widget/TimePickerClockDelegate_Delegate.java
@@ -21,16 +21,16 @@ import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
import android.view.KeyEvent;
/**
- * Delegate used to provide new implementation of few methods in {@link TimePickerSpinnerDelegate}.
+ * Delegate used to provide new implementation of few methods in {@link TimePickerClockDelegate}.
*/
-public class TimePickerSpinnerDelegate_Delegate {
+public class TimePickerClockDelegate_Delegate {
- // Copied from TimePickerSpinnerDelegate.
+ // Copied from TimePickerClockDelegate.
private static final int AM = 0;
private static final int PM = 1;
@LayoutlibDelegate
- static int getAmOrPmKeyCode(TimePickerSpinnerDelegate tpsd, int amOrPm) {
+ static int getAmOrPmKeyCode(TimePickerClockDelegate tpcd, int amOrPm) {
// We don't care about locales here.
if (amOrPm == AM) {
return KeyEvent.KEYCODE_A;
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/BridgeConstants.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/BridgeConstants.java
index eb9e7f1..fdb4567 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/BridgeConstants.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/BridgeConstants.java
@@ -48,4 +48,7 @@ public class BridgeConstants {
public final static String MATCH_PARENT = "match_parent";
public final static String FILL_PARENT = "fill_parent";
public final static String WRAP_CONTENT = "wrap_content";
+
+ /** Attribute in the tools namespace used to specify layout manager for RecyclerView. */
+ public static final String ATTR_LAYOUT_MANAGER_TYPE = "layoutManagerType";
}
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContentProvider.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContentProvider.java
index 89288bf..e4cbb2f 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContentProvider.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContentProvider.java
@@ -90,7 +90,7 @@ public final class BridgeContentProvider implements IContentProvider {
@Override
public ParcelFileDescriptor openFile(
- String callingPackage, Uri arg0, String arg1, ICancellationSignal signal)
+ String callingPackage, Uri arg0, String arg1, ICancellationSignal signal, IBinder token)
throws RemoteException, FileNotFoundException {
// TODO Auto-generated method stub
return null;
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 7c86f75..aa4b560 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
@@ -16,6 +16,7 @@
package com.android.layoutlib.bridge.android;
+import android.os.IBinder;
import com.android.annotations.Nullable;
import com.android.ide.common.rendering.api.AssetRepository;
import com.android.ide.common.rendering.api.ILayoutPullParser;
@@ -93,6 +94,7 @@ import java.util.Map;
/**
* Custom implementation of Context/Activity to handle non compiled resources.
*/
+@SuppressWarnings("deprecation") // For use of Pair.
public final class BridgeContext extends Context {
/** The map adds cookies to each view so that IDE can link xml tags to views. */
@@ -568,7 +570,7 @@ public final class BridgeContext extends Context {
// Hint: for XmlPullParser, attach source //DEVICE_SRC/dalvik/libcore/xml/src/java
if (set instanceof BridgeXmlBlockParser) {
- BridgeXmlBlockParser parser = null;
+ BridgeXmlBlockParser parser;
parser = (BridgeXmlBlockParser)set;
isPlatformFile = parser.isPlatformFile();
@@ -589,7 +591,7 @@ public final class BridgeContext extends Context {
} else if (set != null) { // null parser is ok
// really this should not be happening since its instantiated in Bridge
Bridge.getLog().error(LayoutLog.TAG_BROKEN,
- "Parser is not a BridgeXmlBlockParser!", null /*data*/);
+ "Parser is not a BridgeXmlBlockParser!", null);
return null;
}
@@ -601,7 +603,7 @@ public final class BridgeContext extends Context {
// look for a custom style.
String customStyle = null;
if (set != null) {
- customStyle = set.getAttributeValue(null /* namespace*/, "style");
+ customStyle = set.getAttributeValue(null, "style");
}
StyleResourceValue customStyleValues = null;
@@ -624,31 +626,39 @@ public final class BridgeContext extends Context {
// get the name from the int.
Pair<String, Boolean> defStyleAttribute = searchAttr(defStyleAttr);
- if (defaultPropMap != null) {
- String defStyleName = defStyleAttribute.getFirst();
- if (defStyleAttribute.getSecond()) {
- defStyleName = "android:" + defStyleName;
+ if (defStyleAttribute == null) {
+ // This should be rare. Happens trying to map R.style.foo to @style/foo fails.
+ // This will happen if the user explicitly used a non existing int value for
+ // defStyleAttr or there's something wrong with the project structure/build.
+ Bridge.getLog().error(LayoutLog.TAG_RESOURCES_RESOLVE,
+ "Failed to find the style corresponding to the id " + defStyleAttr, null);
+ } else {
+ if (defaultPropMap != null) {
+ String defStyleName = defStyleAttribute.getFirst();
+ if (defStyleAttribute.getSecond()) {
+ defStyleName = "android:" + defStyleName;
+ }
+ defaultPropMap.put("style", defStyleName);
}
- defaultPropMap.put("style", defStyleName);
- }
- // look for the style in the current theme, and its parent:
- ResourceValue item = mRenderResources.findItemInTheme(defStyleAttribute.getFirst(),
- defStyleAttribute.getSecond());
+ // look for the style in the current theme, and its parent:
+ ResourceValue item = mRenderResources.findItemInTheme(defStyleAttribute.getFirst(),
+ defStyleAttribute.getSecond());
- if (item != null) {
- // item is a reference to a style entry. Search for it.
- item = mRenderResources.findResValue(item.getValue(), item.isFramework());
+ if (item != null) {
+ // item is a reference to a style entry. Search for it.
+ item = mRenderResources.findResValue(item.getValue(), item.isFramework());
- if (item instanceof StyleResourceValue) {
- defStyleValues = (StyleResourceValue)item;
+ if (item instanceof StyleResourceValue) {
+ defStyleValues = (StyleResourceValue) item;
+ }
+ } else {
+ Bridge.getLog().error(LayoutLog.TAG_RESOURCES_RESOLVE_THEME_ATTR,
+ String.format(
+ "Failed to find style '%s' in current theme",
+ defStyleAttribute.getFirst()),
+ null);
}
- } else {
- Bridge.getLog().error(LayoutLog.TAG_RESOURCES_RESOLVE_THEME_ATTR,
- String.format(
- "Failed to find style '%s' in current theme",
- defStyleAttribute.getFirst()),
- null /*data*/);
}
} else if (defStyleRes != 0) {
boolean isFrameworkRes = true;
@@ -963,12 +973,24 @@ public final class BridgeContext extends Context {
}
@Override
+ public int checkPermission(String arg0, int arg1, int arg2, IBinder arg3) {
+ // pass
+ return 0;
+ }
+
+ @Override
public int checkUriPermission(Uri arg0, int arg1, int arg2, int arg3) {
// pass
return 0;
}
@Override
+ public int checkUriPermission(Uri arg0, int arg1, int arg2, int arg3, IBinder arg4) {
+ // pass
+ return 0;
+ }
+
+ @Override
public int checkUriPermission(Uri arg0, String arg1, String arg2, int arg3,
int arg4, int arg5) {
// pass
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgePowerManager.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgePowerManager.java
index 05a6fd6..39ebdfc 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgePowerManager.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgePowerManager.java
@@ -116,11 +116,6 @@ public class BridgePowerManager implements IPowerManager {
}
@Override
- public void setMaximumScreenOffTimeoutFromDeviceAdmin(int arg0) throws RemoteException {
- // pass for now.
- }
-
- @Override
public void setStayOnSetting(int arg0) throws RemoteException {
// pass for now.
}
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 997b199..4c4454d 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
@@ -95,6 +95,10 @@ public final class BridgeWindow implements IWindow {
}
@Override
+ public void dispatchWindowShown() {
+ }
+
+ @Override
public IBinder asBinder() {
// pass for now.
return null;
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 0ed6ab1..0f51d00 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
@@ -38,7 +38,7 @@ import android.view.WindowManager.LayoutParams;
public final class BridgeWindowSession implements IWindowSession {
@Override
- public int add(IWindow arg0, int seq, LayoutParams arg1, int arg2, Rect arg3,
+ public int add(IWindow arg0, int seq, LayoutParams arg1, int arg2, Rect arg3, Rect arg4,
InputChannel outInputchannel)
throws RemoteException {
// pass for now.
@@ -47,7 +47,7 @@ public final class BridgeWindowSession implements IWindowSession {
@Override
public int addToDisplay(IWindow arg0, int seq, LayoutParams arg1, int arg2, int displayId,
- Rect arg3, InputChannel outInputchannel)
+ Rect arg3, Rect arg4, InputChannel outInputchannel)
throws RemoteException {
// pass for now.
return 0;
@@ -55,7 +55,7 @@ public final class BridgeWindowSession implements IWindowSession {
@Override
public int addWithoutInputChannel(IWindow arg0, int seq, LayoutParams arg1, int arg2,
- Rect arg3)
+ Rect arg3, Rect arg4)
throws RemoteException {
// pass for now.
return 0;
@@ -63,7 +63,7 @@ public final class BridgeWindowSession implements IWindowSession {
@Override
public int addToDisplayWithoutInputChannel(IWindow arg0, int seq, LayoutParams arg1, int arg2,
- int displayId, Rect arg3)
+ int displayId, Rect arg3, Rect arg4)
throws RemoteException {
// pass for now.
return 0;
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/SessionParamsFlags.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/SessionParamsFlags.java
index e00ea6a..22b5192 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/SessionParamsFlags.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/SessionParamsFlags.java
@@ -29,6 +29,8 @@ public final class SessionParamsFlags {
public static final SessionParams.Key<String> FLAG_KEY_ROOT_TAG =
new SessionParams.Key<String>("rootTag", String.class);
+ public static final SessionParams.Key<Boolean> FLAG_KEY_RECYCLER_VIEW_SUPPORT =
+ new SessionParams.Key<Boolean>("recyclerViewSupport", Boolean.class);
// Disallow instances.
private SessionParamsFlags() {}
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/support/RecyclerViewUtil.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/support/RecyclerViewUtil.java
new file mode 100644
index 0000000..2feab7a
--- /dev/null
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/support/RecyclerViewUtil.java
@@ -0,0 +1,206 @@
+/*
+ * Copyright (C) 2015 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.android.support;
+
+import com.android.annotations.NonNull;
+import com.android.annotations.Nullable;
+import com.android.ide.common.rendering.api.IProjectCallback;
+import com.android.ide.common.rendering.api.LayoutLog;
+import com.android.ide.common.rendering.api.SessionParams;
+import com.android.layoutlib.bridge.Bridge;
+import com.android.layoutlib.bridge.android.BridgeContext;
+import com.android.layoutlib.bridge.android.SessionParamsFlags;
+
+import android.content.Context;
+import android.view.View;
+import android.widget.LinearLayout;
+
+import java.lang.reflect.Method;
+import java.util.HashMap;
+
+import static com.android.layoutlib.bridge.util.ReflectionUtils.*;
+
+/**
+ * Utility class for working with android.support.v7.widget.RecyclerView
+ */
+@SuppressWarnings("SpellCheckingInspection") // for "recycler".
+public class RecyclerViewUtil {
+
+ /**
+ * Used by {@link LayoutManagerType}.
+ * <p/>
+ * Not declared inside the enum, since it needs to be accessible in the constructor.
+ */
+ private static final Object CONTEXT = new Object();
+
+ public static final String CN_RECYCLER_VIEW = "android.support.v7.widget.RecyclerView";
+ private static final String CN_LAYOUT_MANAGER = CN_RECYCLER_VIEW + "$LayoutManager";
+ private static final String CN_ADAPTER = CN_RECYCLER_VIEW + "$Adapter";
+
+ /**
+ * Tries to create an Adapter ({@code android.support.v7.widget.RecyclerView.Adapter} and a
+ * LayoutManager {@code RecyclerView.LayoutManager} and assign these to the {@code RecyclerView}
+ * that is passed.
+ * <p/>
+ * Any exceptions thrown during the process are logged in {@link Bridge#getLog()}
+ */
+ public static void setAdapter(@NonNull View recyclerView, @NonNull BridgeContext context,
+ @NonNull SessionParams params) {
+ try {
+ setLayoutManager(recyclerView, context, params.getProjectCallback());
+ Object adapter = createAdapter(params);
+ setProperty(recyclerView, CN_ADAPTER, adapter, "setAdapter");
+ } catch (ReflectionException e) {
+ Bridge.getLog().error(LayoutLog.TAG_BROKEN,
+ "Error occured while trying to setup RecyclerView.", e, null);
+ }
+ }
+
+ private static void setLayoutManager(@NonNull View recyclerView, @NonNull BridgeContext context,
+ @NonNull IProjectCallback callback) throws ReflectionException {
+ Object cookie = context.getCookie(recyclerView);
+ assert cookie == null || cookie instanceof LayoutManagerType;
+ if (cookie == null) {
+ cookie = LayoutManagerType.getDefault();
+ }
+ Object layoutManager = createLayoutManager((LayoutManagerType) cookie, context, callback);
+ setProperty(recyclerView, CN_LAYOUT_MANAGER, layoutManager, "setLayoutManager");
+ }
+
+ @Nullable
+ private static Object createLayoutManager(@Nullable LayoutManagerType type,
+ @NonNull Context context, @NonNull IProjectCallback callback)
+ throws ReflectionException {
+ if (type == null) {
+ type = LayoutManagerType.getDefault();
+ }
+ try {
+ return callback.loadView(type.getClassName(), type.getSignature(), type.getArgs(context));
+ } catch (Exception e) {
+ throw new ReflectionException(e);
+ }
+ }
+
+ @Nullable
+ private static Object createAdapter(@NonNull SessionParams params) throws ReflectionException {
+ Boolean ideSupport = params.getFlag(SessionParamsFlags.FLAG_KEY_RECYCLER_VIEW_SUPPORT);
+ if (ideSupport != Boolean.TRUE) {
+ return null;
+ }
+ try {
+ return params.getProjectCallback().loadView(CN_ADAPTER, new Class[0], new Object[0]);
+ } catch (Exception e) {
+ throw new ReflectionException(e);
+ }
+ }
+
+ private static void setProperty(@NonNull View recyclerView, @NonNull String propertyClassName,
+ @Nullable Object propertyValue, @NonNull String propertySetter)
+ throws ReflectionException {
+ if (propertyValue != null) {
+ Class<?> layoutManagerClass = getClassInstance(propertyValue, propertyClassName);
+ Method setLayoutManager = getMethod(recyclerView.getClass(),
+ propertySetter, layoutManagerClass);
+ if (setLayoutManager != null) {
+ invoke(setLayoutManager, recyclerView, propertyValue);
+ }
+ }
+ }
+
+ /**
+ * Looks through the class hierarchy of {@code object} at runtime and returns the class matching
+ * the name {@code className}.
+ * <p/>
+ * This is used when we cannot use Class.forName() since the class we want was loaded from a
+ * different ClassLoader.
+ */
+ @NonNull
+ private static Class<?> getClassInstance(@NonNull Object object, @NonNull String className) {
+ Class<?> superClass = object.getClass();
+ while (superClass != null) {
+ if (className.equals(superClass.getName())) {
+ return superClass;
+ }
+ superClass = superClass.getSuperclass();
+ }
+ throw new RuntimeException("invalid object/classname combination.");
+ }
+
+ /** Supported LayoutManagers. */
+ public enum LayoutManagerType {
+ LINEAR_LAYOUT_MANGER("Linear",
+ "android.support.v7.widget.LinearLayoutManager",
+ new Class[]{Context.class}, new Object[]{CONTEXT}),
+ GRID_LAYOUT_MANAGER("Grid",
+ "android.support.v7.widget.GridLayoutManager",
+ new Class[]{Context.class, int.class}, new Object[]{CONTEXT, 2}),
+ STAGGERED_GRID_LAYOUT_MANAGER("StaggeredGrid",
+ "android.support.v7.widget.StaggeredGridLayoutManager",
+ new Class[]{int.class, int.class}, new Object[]{2, LinearLayout.VERTICAL});
+
+ private String mDisplayName;
+ private String mClassName;
+ private Class[] mSignature;
+ private Object[] mArgs;
+
+ private static final HashMap<String, LayoutManagerType> sDisplayNameLookup =
+ new HashMap<String, LayoutManagerType>();
+
+ static {
+ for (LayoutManagerType type : LayoutManagerType.values()) {
+ sDisplayNameLookup.put(type.mDisplayName, type);
+ }
+ }
+
+ LayoutManagerType(String displayName, String className, Class[] signature, Object[] args) {
+ mDisplayName = displayName;
+ mClassName = className;
+ mSignature = signature;
+ mArgs = args;
+ }
+
+ String getClassName() {
+ return mClassName;
+ }
+
+ Class[] getSignature() {
+ return mSignature;
+ }
+
+ @NonNull
+ Object[] getArgs(Context context) {
+ Object[] args = new Object[mArgs.length];
+ System.arraycopy(mArgs, 0, args, 0, mArgs.length);
+ for (int i = 0; i < args.length; i++) {
+ if (args[i] == CONTEXT) {
+ args[i] = context;
+ }
+ }
+ return args;
+ }
+
+ @NonNull
+ public static LayoutManagerType getDefault() {
+ return LINEAR_LAYOUT_MANGER;
+ }
+
+ @Nullable
+ public static LayoutManagerType getByDisplayName(@Nullable String className) {
+ return sDisplayNameLookup.get(className);
+ }
+ }
+}
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/Config.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/Config.java
index 82a5130..dde041b 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/Config.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/Config.java
@@ -73,7 +73,7 @@ public class Config {
public static String getTime(int platformVersion) {
if (platformVersion == 0) {
- return "5:00";
+ return "5:10";
}
if (platformVersion < GINGERBREAD) {
return "2:20";
@@ -87,9 +87,12 @@ public class Config {
if (platformVersion < KITKAT) {
return "4:30";
}
- if (platformVersion <= KITKAT_WATCH) {
+ if (platformVersion < LOLLIPOP) {
return "4:40";
}
+ if (platformVersion < LOLLIPOP_MR1) {
+ return "5:00";
+ }
// Should never happen.
return "4:04";
}
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 669e6b5..6513c5f 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
@@ -16,8 +16,6 @@
package com.android.layoutlib.bridge.impl;
-import static com.android.ide.common.rendering.api.Result.Status.ERROR_UNKNOWN;
-
import com.android.ide.common.rendering.api.DrawableParams;
import com.android.ide.common.rendering.api.HardwareConfig;
import com.android.ide.common.rendering.api.ResourceValue;
@@ -38,7 +36,6 @@ import java.awt.AlphaComposite;
import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.image.BufferedImage;
-import java.io.IOException;
/**
* Action to render a given Drawable provided through {@link DrawableParams#getDrawable()}.
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 58acab9..b6b63b1 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
@@ -23,6 +23,8 @@ import static com.android.ide.common.rendering.api.Result.Status.ERROR_UNKNOWN;
import static com.android.ide.common.rendering.api.Result.Status.ERROR_VIEWGROUP_NO_CHILDREN;
import static com.android.ide.common.rendering.api.Result.Status.SUCCESS;
+import com.android.annotations.NonNull;
+import com.android.annotations.Nullable;
import com.android.ide.common.rendering.api.AdapterBinding;
import com.android.ide.common.rendering.api.HardwareConfig;
import com.android.ide.common.rendering.api.IAnimationListener;
@@ -51,6 +53,7 @@ import com.android.layoutlib.bridge.android.BridgeContext;
import com.android.layoutlib.bridge.android.BridgeLayoutParamsMapAttributes;
import com.android.layoutlib.bridge.android.BridgeXmlBlockParser;
import com.android.layoutlib.bridge.android.SessionParamsFlags;
+import com.android.layoutlib.bridge.android.support.RecyclerViewUtil;
import com.android.layoutlib.bridge.bars.BridgeActionBar;
import com.android.layoutlib.bridge.bars.AppCompatActionBar;
import com.android.layoutlib.bridge.bars.Config;
@@ -1327,6 +1330,8 @@ public class RenderSessionImpl extends RenderAction<SessionParams> {
}
}
}
+ } else if (isInstanceOf(view, RecyclerViewUtil.CN_RECYCLER_VIEW)) {
+ RecyclerViewUtil.setAdapter(view, getContext(), getParams());
} else if (view instanceof ViewGroup) {
ViewGroup group = (ViewGroup) view;
final int count = group.getChildCount();
@@ -1338,6 +1343,22 @@ public class RenderSessionImpl extends RenderAction<SessionParams> {
}
/**
+ * Check if the object is an instance of a class named {@code className}. This doesn't work
+ * for interfaces.
+ */
+ public static boolean isInstanceOf(Object object, String className) {
+ Class superClass = object.getClass();
+ while (superClass != null) {
+ String name = superClass.getName();
+ if (name.equals(className)) {
+ return true;
+ }
+ superClass = superClass.getSuperclass();
+ }
+ return false;
+ }
+
+ /**
* Sets up a {@link TabHost} object.
* @param tabHost the TabHost to setup.
* @param projectCallback The project callback object to access the project R class.
@@ -1494,6 +1515,7 @@ public class RenderSessionImpl extends RenderAction<SessionParams> {
* @return an array of length two, with ViewInfo at index 0 is without offset and ViewInfo at
* index 1 is with the offset.
*/
+ @NonNull
private ViewInfo[] visitContentRoot(View view, int offset, boolean setExtendedInfo) {
ViewInfo[] result = new ViewInfo[2];
if (view == null) {
@@ -1589,6 +1611,7 @@ public class RenderSessionImpl extends RenderAction<SessionParams> {
* The cookie for menu items are stored in menu item and not in the map from View stored in
* BridgeContext.
*/
+ @Nullable
private Object getViewKey(View view) {
BridgeContext context = getContext();
if (!(view instanceof MenuView.ItemView)) {
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/util/ReflectionUtils.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/util/ReflectionUtils.java
new file mode 100644
index 0000000..8e61edf
--- /dev/null
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/util/ReflectionUtils.java
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 2015 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.util;
+
+import com.android.annotations.NonNull;
+import com.android.annotations.Nullable;
+
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+
+/**
+ * Utility to convert checked Reflection exceptions to unchecked exceptions.
+ */
+public class ReflectionUtils {
+
+ @Nullable
+ public static Method getMethod(@NonNull Class<?> clazz, @NonNull String name,
+ @Nullable Class<?>... params) throws ReflectionException {
+ try {
+ return clazz.getMethod(name, params);
+ } catch (NoSuchMethodException e) {
+ throw new ReflectionException(e);
+ }
+ }
+
+ @Nullable
+ public static Object invoke(@NonNull Method method, @Nullable Object object,
+ @Nullable Object... args) throws ReflectionException {
+ Exception ex;
+ try {
+ return method.invoke(object, args);
+ } catch (IllegalAccessException e) {
+ ex = e;
+ } catch (InvocationTargetException e) {
+ ex = e;
+ }
+ throw new ReflectionException(ex);
+ }
+
+ /**
+ * Wraps all reflection related exceptions. Created since ReflectiveOperationException was
+ * introduced in 1.7 and we are still on 1.6
+ */
+ public static class ReflectionException extends Exception {
+ public ReflectionException() {
+ super();
+ }
+
+ public ReflectionException(String message) {
+ super(message);
+ }
+
+ public ReflectionException(String message, Throwable cause) {
+ super(message, cause);
+ }
+
+ public ReflectionException(Throwable cause) {
+ super(cause);
+ }
+ }
+}
diff --git a/tools/layoutlib/bridge/src/libcore/icu/ICU_Delegate.java b/tools/layoutlib/bridge/src/libcore/icu/ICU_Delegate.java
index 8898856..b8b5fed 100644
--- a/tools/layoutlib/bridge/src/libcore/icu/ICU_Delegate.java
+++ b/tools/layoutlib/bridge/src/libcore/icu/ICU_Delegate.java
@@ -223,7 +223,7 @@ public class ICU_Delegate {
result.decimalSeparator = '.';
result.groupingSeparator = ',';
result.patternSeparator = ' ';
- result.percent = '%';
+ result.percent = "%";
result.perMill = '\u2030';
result.monetarySeparator = ' ';
result.minusSign = "-";
diff --git a/tools/layoutlib/bridge/tests/src/com/android/layoutlib/bridge/intensive/Main.java b/tools/layoutlib/bridge/tests/src/com/android/layoutlib/bridge/intensive/Main.java
index 96725af..a86fcdd 100644
--- a/tools/layoutlib/bridge/tests/src/com/android/layoutlib/bridge/intensive/Main.java
+++ b/tools/layoutlib/bridge/tests/src/com/android/layoutlib/bridge/intensive/Main.java
@@ -165,11 +165,27 @@ public class Main {
if (!out.isDirectory()) {
return null;
}
- File sdkDir = new File(out, "sdk" + File.separator + "sdk");
+ File sdkDir = new File(out, "sdk");
if (!sdkDir.isDirectory()) {
- // The directory we thought that should contain the sdk is not a directory.
return null;
}
+ File[] sdkDirs = sdkDir.listFiles(new FileFilter() {
+ @Override
+ public boolean accept(File path) {
+ // We need to search for $TARGET_PRODUCT (usually, sdk_phone_armv7)
+ return path.isDirectory() && path.getName().startsWith("sdk");
+ }
+ });
+ for (File dir : sdkDirs) {
+ String platformDir = getPlatformDirFromHostOutSdkSdk(dir);
+ if (platformDir != null) {
+ return platformDir;
+ }
+ }
+ return null;
+ }
+
+ private static String getPlatformDirFromHostOutSdkSdk(File sdkDir) {
File[] possibleSdks = sdkDir.listFiles(new FileFilter() {
@Override
public boolean accept(File path) {
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 f9e6151..98acd2f 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
@@ -20,6 +20,7 @@ import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
import com.android.tools.layoutlib.java.AutoCloseable;
import com.android.tools.layoutlib.java.Charsets;
import com.android.tools.layoutlib.java.IntegralToString;
+import com.android.tools.layoutlib.java.LinkedHashMap_Delegate;
import com.android.tools.layoutlib.java.Objects;
import com.android.tools.layoutlib.java.System_Delegate;
import com.android.tools.layoutlib.java.UnsafeByteSequence;
@@ -133,6 +134,7 @@ public final class CreateInfo implements ICreateInfo {
UnsafeByteSequence.class,
Charsets.class,
System_Delegate.class,
+ LinkedHashMap_Delegate.class,
};
/**
@@ -170,7 +172,7 @@ public final class CreateInfo implements ICreateInfo {
"android.view.RenderNode#nSetElevation",
"android.view.RenderNode#nGetElevation",
"android.view.ViewGroup#drawChild",
- "android.widget.TimePickerSpinnerDelegate#getAmOrPmKeyCode",
+ "android.widget.TimePickerClockDelegate#getAmOrPmKeyCode",
"com.android.internal.view.menu.MenuBuilder#createNewMenuItem",
"com.android.internal.util.XmlUtils#convertValueToInt",
"com.android.internal.textservice.ITextServicesManager$Stub#asInterface",
diff --git a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/ReplaceMethodCallsAdapter.java b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/ReplaceMethodCallsAdapter.java
index 1e2623f..384d8ca 100644
--- a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/ReplaceMethodCallsAdapter.java
+++ b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/ReplaceMethodCallsAdapter.java
@@ -16,6 +16,7 @@
package com.android.tools.layoutlib.create;
+import com.android.tools.layoutlib.java.LinkedHashMap_Delegate;
import com.android.tools.layoutlib.java.System_Delegate;
import org.objectweb.asm.ClassVisitor;
@@ -26,8 +27,10 @@ import org.objectweb.asm.Type;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
+import java.util.LinkedHashMap;
import java.util.List;
import java.util.Locale;
+import java.util.Map;
import java.util.Set;
/**
@@ -44,7 +47,7 @@ public class ReplaceMethodCallsAdapter extends ClassVisitor {
"([CI[CII)V", "([BI[BII)V", "([SI[SII)V", "([II[III)V",
"([JI[JII)V", "([FI[FII)V", "([DI[DII)V", "([ZI[ZII)V"));
- private static final List<MethodReplacer> METHOD_REPLACERS = new ArrayList<MethodReplacer>(2);
+ private static final List<MethodReplacer> METHOD_REPLACERS = new ArrayList<MethodReplacer>(5);
private static final String ANDROID_LOCALE_CLASS =
"com/android/layoutlib/bridge/android/AndroidLocale";
@@ -74,7 +77,8 @@ public class ReplaceMethodCallsAdapter extends ClassVisitor {
// Case 2: java.util.Locale.toLanguageTag() and java.util.Locale.getScript()
METHOD_REPLACERS.add(new MethodReplacer() {
- String LOCALE_TO_STRING = Type.getMethodDescriptor(STRING, Type.getType(Locale.class));
+ private final String LOCALE_TO_STRING =
+ Type.getMethodDescriptor(STRING, Type.getType(Locale.class));
@Override
public boolean isNeeded(String owner, String name, String desc) {
@@ -129,6 +133,30 @@ public class ReplaceMethodCallsAdapter extends ClassVisitor {
mi.owner = Type.getInternalName(System_Delegate.class);
}
});
+
+ // Case 5: java.util.LinkedHashMap.eldest()
+ METHOD_REPLACERS.add(new MethodReplacer() {
+
+ private final String VOID_TO_MAP_ENTRY =
+ Type.getMethodDescriptor(Type.getType(Map.Entry.class));
+ private final String LINKED_HASH_MAP = Type.getInternalName(LinkedHashMap.class);
+
+ @Override
+ public boolean isNeeded(String owner, String name, String desc) {
+ return LINKED_HASH_MAP.equals(owner) &&
+ "eldest".equals(name) &&
+ VOID_TO_MAP_ENTRY.equals(desc);
+ }
+
+ @Override
+ public void replace(MethodInformation mi) {
+ assert isNeeded(mi.owner, mi.name, mi.desc);
+ mi.opcode = Opcodes.INVOKESTATIC;
+ mi.owner = Type.getInternalName(LinkedHashMap_Delegate.class);
+ mi.desc = Type.getMethodDescriptor(
+ Type.getType(Map.Entry.class), Type.getType(LinkedHashMap.class));
+ }
+ });
}
public static boolean isReplacementNeeded(String owner, String name, String desc) {
diff --git a/tools/layoutlib/create/src/com/android/tools/layoutlib/java/LinkedHashMap_Delegate.java b/tools/layoutlib/create/src/com/android/tools/layoutlib/java/LinkedHashMap_Delegate.java
new file mode 100644
index 0000000..59cc75f
--- /dev/null
+++ b/tools/layoutlib/create/src/com/android/tools/layoutlib/java/LinkedHashMap_Delegate.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2015 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.tools.layoutlib.java;
+
+import com.android.tools.layoutlib.create.ReplaceMethodCallsAdapter;
+
+import java.util.Iterator;
+import java.util.LinkedHashMap;
+import java.util.Map;
+import java.util.Map.Entry;
+
+/**
+ * Provides alternate implementation to java.util.LinkedHashMap#eldest(), which is present as a
+ * non-public method in the Android VM, but not present on the host VM. This is injected in the
+ * layoutlib using {@link ReplaceMethodCallsAdapter}.
+ */
+public class LinkedHashMap_Delegate {
+ public static <K,V> Map.Entry<K,V> eldest(LinkedHashMap<K,V> map) {
+ Iterator<Entry<K, V>> iterator = map.entrySet().iterator();
+ return iterator.hasNext() ? iterator.next() : null;
+ }
+}