Merge "Remove redundant cancel events from FingerprintService" into mnc-dev
diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java
index 849253b..08e1696 100644
--- a/core/java/android/app/AppOpsManager.java
+++ b/core/java/android/app/AppOpsManager.java
@@ -773,7 +773,7 @@
AppOpsManager.MODE_ALLOWED,
AppOpsManager.MODE_ALLOWED,
AppOpsManager.MODE_ALLOWED,
- AppOpsManager.MODE_ALLOWED,
+ AppOpsManager.MODE_DEFAULT, // OP_SYSTEM_ALERT_WINDOW
AppOpsManager.MODE_ALLOWED,
AppOpsManager.MODE_ALLOWED,
AppOpsManager.MODE_ALLOWED,
diff --git a/core/java/com/android/internal/widget/FloatingToolbar.java b/core/java/com/android/internal/widget/FloatingToolbar.java
index b3f688b..ca6fe61 100644
--- a/core/java/com/android/internal/widget/FloatingToolbar.java
+++ b/core/java/com/android/internal/widget/FloatingToolbar.java
@@ -285,7 +285,6 @@
private final Context mContext;
private final View mParent;
- private final int[] mParentPositionOnScreen = new int[2];
private final PopupWindow mPopupWindow;
private final ViewGroup mContentContainer;
private final int mMarginHorizontal;
@@ -339,7 +338,8 @@
};
private final Rect mViewPortOnScreen = new Rect();
- private final Point mCoordsOnScreen = new Point();
+ private final Point mCoordsOnWindow = new Point();
+ private final int[] mTmpCoords = new int[2];
private final Rect mTmpRect = new Rect();
private final Region mTouchableRegion = new Region();
@@ -450,13 +450,11 @@
}
refreshCoordinatesAndOverflowDirection(contentRectOnScreen);
preparePopupContent();
- // We need to specify the offset relative to mParent.
+ // We need to specify the position in window coordinates.
// TODO: Consider to use PopupWindow.setLayoutInScreenEnabled(true) so that we can
// specify the popup poision in screen coordinates.
- mParent.getLocationOnScreen(mParentPositionOnScreen);
- final int relativeX = mCoordsOnScreen.x - mParentPositionOnScreen[0];
- final int relativeY = mCoordsOnScreen.y - mParentPositionOnScreen[1];
- mPopupWindow.showAtLocation(mParent, Gravity.NO_GRAVITY, relativeX, relativeY);
+ mPopupWindow.showAtLocation(mParent, Gravity.NO_GRAVITY, mCoordsOnWindow.x,
+ mCoordsOnWindow.y);
setTouchableSurfaceInsetsComputer();
runShowAnimation();
}
@@ -519,13 +517,10 @@
cancelOverflowAnimations();
refreshCoordinatesAndOverflowDirection(contentRectOnScreen);
preparePopupContent();
- // We need to specify the offset relative to mParent.
+ // We need to specify the position in window coordinates.
// TODO: Consider to use PopupWindow.setLayoutInScreenEnabled(true) so that we can
// specify the popup poision in screen coordinates.
- mParent.getLocationOnScreen(mParentPositionOnScreen);
- final int relativeX = mCoordsOnScreen.x - mParentPositionOnScreen[0];
- final int relativeY = mCoordsOnScreen.y - mParentPositionOnScreen[1];
- mPopupWindow.update(relativeX, relativeY, getWidth(), getHeight());
+ mPopupWindow.update(mCoordsOnWindow.x, mCoordsOnWindow.y, getWidth(), getHeight());
}
/**
@@ -624,7 +619,22 @@
mOverflowPanel.setOverflowDirection(mOverflowDirection);
}
- mCoordsOnScreen.set(x, y);
+ // We later specify the location of PopupWindow relative to the attached window.
+ // The idea here is that 1) we can get the location of a View in both window coordinates
+ // and screen coordiantes, where the offset between them should be equal to the window
+ // origin, and 2) we can use an arbitrary for this calculation while calculating the
+ // location of the rootview is supposed to be least expensive.
+ // TODO: Consider to use PopupWindow.setLayoutInScreenEnabled(true) so that we can avoid
+ // the following calculation.
+ mParent.getRootView().getLocationOnScreen(mTmpCoords);
+ int rootViewLeftOnScreen = mTmpCoords[0];
+ int rootViewTopOnScreen = mTmpCoords[1];
+ mParent.getRootView().getLocationInWindow(mTmpCoords);
+ int rootViewLeftOnWindow = mTmpCoords[0];
+ int rootViewTopOnWindow = mTmpCoords[1];
+ int windowLeftOnScreen = rootViewLeftOnScreen - rootViewLeftOnWindow;
+ int windowTopOnScreen = rootViewTopOnScreen - rootViewTopOnWindow;
+ mCoordsOnWindow.set(x - windowLeftOnScreen, y - windowTopOnScreen);
}
private int getToolbarHeightWithVerticalMargin() {
diff --git a/core/res/res/layout/common_tab_settings.xml b/core/res/res/layout/common_tab_settings.xml
new file mode 100644
index 0000000..d2a4acc
--- /dev/null
+++ b/core/res/res/layout/common_tab_settings.xml
@@ -0,0 +1,60 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2014 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<TabHost xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@android:id/tabhost"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+
+ <LinearLayout
+ android:id="@+id/tabs_container"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="vertical">
+
+ <HorizontalScrollView xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:scrollbars="none"
+ android:fillViewport="true">
+
+ <TabWidget
+ android:id="@android:id/tabs"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal"
+ style="?android:attr/tabWidgetStyle" />
+
+ </HorizontalScrollView>
+
+ <!-- give an empty content area to make tabhost happy -->
+ <FrameLayout
+ android:id="@android:id/tabcontent"
+ android:layout_width="0dip"
+ android:layout_height="0dip" />
+
+ <ListView
+ android:id="@android:id/list"
+ android:layout_width="match_parent"
+ android:layout_height="0dip"
+ android:layout_weight="1"
+ android:clipChildren="false"
+ android:clipToPadding="false"
+ android:smoothScrollbar="false" />
+
+ </LinearLayout>
+
+</TabHost>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index d41031c..b330423 100755
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -1360,6 +1360,7 @@
<java-symbol type="layout" name="restrictions_pin_setup" />
<java-symbol type="layout" name="immersive_mode_cling" />
<java-symbol type="layout" name="user_switching_dialog" />
+ <java-symbol type="layout" name="common_tab_settings" />
<java-symbol type="anim" name="slide_in_child_bottom" />
<java-symbol type="anim" name="slide_in_right" />
diff --git a/docs/html/distribute/essentials/quality/tv.jd b/docs/html/distribute/essentials/quality/tv.jd
index 20018c3..c7f6fcb 100644
--- a/docs/html/distribute/essentials/quality/tv.jd
+++ b/docs/html/distribute/essentials/quality/tv.jd
@@ -418,9 +418,9 @@
</td>
<td>
<p style="margin-bottom:.5em;">
- If the app continues to play sound after the user has left, the app provides a <em>Now
- Playing</em> card on the home screen recommendation row so users can return to the app to
- control playback.
+ If the app continues to play sound or video after the user has left, the
+ app provides a <em>Now Playing</em> card on the home screen recommendation
+ row so users can return to the app to control playback.
(<a href="{@docRoot}training/tv/playback/now-playing.html">Learn how</a>)
</p>
</td>
diff --git a/packages/SystemUI/res/layout/qs_detail_item.xml b/packages/SystemUI/res/layout/qs_detail_item.xml
index a519d3f..6facb71 100644
--- a/packages/SystemUI/res/layout/qs_detail_item.xml
+++ b/packages/SystemUI/res/layout/qs_detail_item.xml
@@ -20,6 +20,7 @@
android:minHeight="@dimen/qs_detail_item_height"
android:background="@drawable/btn_borderless_rect"
android:clickable="true"
+ android:focusable="true"
android:gravity="center_vertical"
android:orientation="horizontal" >
@@ -57,6 +58,7 @@
android:layout_width="48dp"
android:layout_height="48dp"
android:clickable="true"
+ android:focusable="true"
android:scaleType="center"
android:src="@drawable/ic_qs_cancel" />
diff --git a/packages/SystemUI/res/layout/status_bar_notification_dismiss_all.xml b/packages/SystemUI/res/layout/status_bar_notification_dismiss_all.xml
index 6a000fd..dc7577a 100644
--- a/packages/SystemUI/res/layout/status_bar_notification_dismiss_all.xml
+++ b/packages/SystemUI/res/layout/status_bar_notification_dismiss_all.xml
@@ -27,6 +27,7 @@
android:layout_width="48dp"
android:layout_height="48dp"
android:layout_gravity="end"
+ android:focusable="true"
android:background="@drawable/ripple_drawable"
android:contentDescription="@string/accessibility_clear_all"/>
</com.android.systemui.statusbar.DismissView>
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/SignalClusterView.java b/packages/SystemUI/src/com/android/systemui/statusbar/SignalClusterView.java
index da1f03e..f7c3c67 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/SignalClusterView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/SignalClusterView.java
@@ -478,7 +478,6 @@
}
private void setTint(ImageView v, int tint) {
- v.setImageTintMode(PorterDuff.Mode.SRC_ATOP);
v.setImageTintList(ColorStateList.valueOf(tint));
}
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 221e925..eb84e18 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -801,12 +801,9 @@
}
private static boolean hasValidDomains(ActivityIntentInfo filter) {
- boolean hasHTTPorHTTPS = filter.hasDataScheme(IntentFilter.SCHEME_HTTP) ||
- filter.hasDataScheme(IntentFilter.SCHEME_HTTPS);
- if (!hasHTTPorHTTPS) {
- return false;
- }
- return true;
+ return filter.hasCategory(Intent.CATEGORY_BROWSABLE)
+ && (filter.hasDataScheme(IntentFilter.SCHEME_HTTP) ||
+ filter.hasDataScheme(IntentFilter.SCHEME_HTTPS));
}
private IntentFilterVerifier mIntentFilterVerifier;
@@ -15075,8 +15072,9 @@
}
if (filters != null && filters.size() > 0) {
for (IntentFilter filter : filters) {
- if (filter.hasDataScheme(IntentFilter.SCHEME_HTTP) ||
- filter.hasDataScheme(IntentFilter.SCHEME_HTTPS)) {
+ if (filter.hasCategory(Intent.CATEGORY_BROWSABLE)
+ && (filter.hasDataScheme(IntentFilter.SCHEME_HTTP) ||
+ filter.hasDataScheme(IntentFilter.SCHEME_HTTPS))) {
result.addAll(filter.getHostsList());
}
}
diff --git a/services/core/java/com/android/server/pm/PackageSettingBase.java b/services/core/java/com/android/server/pm/PackageSettingBase.java
index 4faf75a..bbdfe31 100644
--- a/services/core/java/com/android/server/pm/PackageSettingBase.java
+++ b/services/core/java/com/android/server/pm/PackageSettingBase.java
@@ -238,6 +238,7 @@
installStatus = base.installStatus;
keySetData = base.keySetData;
verificationInfo = base.verificationInfo;
+ installerPackageName = base.installerPackageName;
}
private PackageUserState modifyUserState(int userId) {
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index 978ed51..13e075c 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -126,6 +126,7 @@
import java.io.FileReader;
import java.io.IOException;
import java.io.PrintWriter;
+import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
@@ -1875,21 +1876,33 @@
if (permission != null) {
if (permission == android.Manifest.permission.SYSTEM_ALERT_WINDOW) {
final int callingUid = Binder.getCallingUid();
- // check if this is a system uid first before bothering with
- // obtaining package name
+ // system processes will be automatically allowed privilege to draw
if (callingUid == Process.SYSTEM_UID) {
return WindowManagerGlobal.ADD_OKAY;
}
+ // check if user has enabled this operation. SecurityException will be thrown if
+ // this app has not been allowed by the user
final int mode = mAppOpsManager.checkOp(outAppOp[0], callingUid,
attrs.packageName);
- if (mode == AppOpsManager.MODE_DEFAULT) {
- if (mContext.checkCallingPermission(permission) !=
- PackageManager.PERMISSION_GRANTED) {
+ switch (mode) {
+ case AppOpsManager.MODE_ALLOWED:
+ case AppOpsManager.MODE_IGNORED:
+ // although we return ADD_OKAY for MODE_IGNORED, the added window will
+ // actually be hidden in WindowManagerService
+ return WindowManagerGlobal.ADD_OKAY;
+ case AppOpsManager.MODE_ERRORED:
return WindowManagerGlobal.ADD_PERMISSION_DENIED;
- }
+ default:
+ // in the default mode, we will make a decision here based on
+ // checkCallingPermission()
+ if (mContext.checkCallingPermission(permission) !=
+ PackageManager.PERMISSION_GRANTED) {
+ return WindowManagerGlobal.ADD_PERMISSION_DENIED;
+ } else {
+ return WindowManagerGlobal.ADD_OKAY;
+ }
}
- return WindowManagerGlobal.ADD_OKAY;
}
if (mContext.checkCallingOrSelfPermission(permission)
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 05c111c..bc63c69 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -2543,8 +2543,10 @@
win.attach();
mWindowMap.put(client.asBinder(), win);
if (win.mAppOp != AppOpsManager.OP_NONE) {
- if (mAppOps.startOpNoThrow(win.mAppOp, win.getOwningUid(), win.getOwningPackage())
- != AppOpsManager.MODE_ALLOWED) {
+ int startOpResult = mAppOps.startOpNoThrow(win.mAppOp, win.getOwningUid(),
+ win.getOwningPackage());
+ if ((startOpResult != AppOpsManager.MODE_ALLOWED) &&
+ (startOpResult != AppOpsManager.MODE_DEFAULT)) {
win.setAppOpVisibilityLw(false);
}
}
@@ -2899,7 +2901,8 @@
if (win.mAppOp != AppOpsManager.OP_NONE) {
final int mode = mAppOps.checkOpNoThrow(win.mAppOp, win.getOwningUid(),
win.getOwningPackage());
- win.setAppOpVisibilityLw(mode == AppOpsManager.MODE_ALLOWED);
+ win.setAppOpVisibilityLw(mode == AppOpsManager.MODE_ALLOWED ||
+ mode == AppOpsManager.MODE_DEFAULT);
}
}
}
diff --git a/telephony/java/android/telephony/SubscriptionManager.java b/telephony/java/android/telephony/SubscriptionManager.java
index f4a6064..32b7383 100644
--- a/telephony/java/android/telephony/SubscriptionManager.java
+++ b/telephony/java/android/telephony/SubscriptionManager.java
@@ -21,12 +21,15 @@
import android.annotation.SdkConstant.SdkConstantType;
import android.content.Context;
import android.content.Intent;
+import android.content.res.Configuration;
+import android.content.res.Resources;
import android.net.Uri;
import android.telephony.Rlog;
import android.os.Handler;
import android.os.Message;
import android.os.ServiceManager;
import android.os.RemoteException;
+import android.util.DisplayMetrics;
import com.android.internal.telephony.ISub;
import com.android.internal.telephony.IOnSubscriptionsChangedListener;
@@ -248,6 +251,78 @@
public static final String MNC = "mnc";
/**
+ * TelephonyProvider column name for extreme threat in CB settings
+ * @hide
+ */
+ public static final String CB_EXTREME_THREAT_ALERT = "enable_cmas_extreme_threat_alerts";
+
+ /**
+ * TelephonyProvider column name for severe threat in CB settings
+ *@hide
+ */
+ public static final String CB_SEVERE_THREAT_ALERT = "enable_cmas_severe_threat_alerts";
+
+ /**
+ * TelephonyProvider column name for amber alert in CB settings
+ *@hide
+ */
+ public static final String CB_AMBER_ALERT = "enable_cmas_amber_alerts";
+
+ /**
+ * TelephonyProvider column name for emergency alert in CB settings
+ *@hide
+ */
+ public static final String CB_EMERGENCY_ALERT = "enable_emergency_alerts";
+
+ /**
+ * TelephonyProvider column name for alert sound duration in CB settings
+ *@hide
+ */
+ public static final String CB_ALERT_SOUND_DURATION = "alert_sound_duration";
+
+ /**
+ * TelephonyProvider column name for alert reminder interval in CB settings
+ *@hide
+ */
+ public static final String CB_ALERT_REMINDER_INTERVAL = "alert_reminder_interval";
+
+ /**
+ * TelephonyProvider column name for enabling vibrate in CB settings
+ *@hide
+ */
+ public static final String CB_ALERT_VIBRATE = "enable_alert_vibrate";
+
+ /**
+ * TelephonyProvider column name for enabling alert speech in CB settings
+ *@hide
+ */
+ public static final String CB_ALERT_SPEECH = "enable_alert_speech";
+
+ /**
+ * TelephonyProvider column name for ETWS test alert in CB settings
+ *@hide
+ */
+ public static final String CB_ETWS_TEST_ALERT = "enable_etws_test_alerts";
+
+ /**
+ * TelephonyProvider column name for enable channel50 alert in CB settings
+ *@hide
+ */
+ public static final String CB_CHANNEL_50_ALERT = "enable_channel_50_alerts";
+
+ /**
+ * TelephonyProvider column name for CMAS test alert in CB settings
+ *@hide
+ */
+ public static final String CB_CMAS_TEST_ALERT= "enable_cmas_test_alerts";
+
+ /**
+ * TelephonyProvider column name for Opt out dialog in CB settings
+ *@hide
+ */
+ public static final String CB_OPT_OUT_DIALOG = "show_cmas_opt_out_dialog";
+
+ /**
* Broadcast Action: The user has changed one of the default subs related to
* data, phone calls, or sms</p>
*
@@ -1137,6 +1212,112 @@
}
/**
+ * Store properties associated with SubscriptionInfo in database
+ * @param subId Subscription Id of Subscription
+ * @param propKey Column name in database associated with SubscriptionInfo
+ * @param propValue Value to store in DB for particular subId & column name
+ * @hide
+ */
+ public static void setSubscriptionProperty(int subId, String propKey, String propValue) {
+ try {
+ ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub"));
+ if (iSub != null) {
+ iSub.setSubscriptionProperty(subId, propKey, propValue);
+ }
+ } catch (RemoteException ex) {
+ // ignore it
+ }
+ }
+
+ /**
+ * Store properties associated with SubscriptionInfo in database
+ * @param subId Subscription Id of Subscription
+ * @param propKey Column name in SubscriptionInfo database
+ * @return Value associated with subId and propKey column in database
+ * @hide
+ */
+ private static String getSubscriptionProperty(int subId, String propKey,
+ Context context) {
+ String resultValue = null;
+ try {
+ ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub"));
+ if (iSub != null) {
+ resultValue = iSub.getSubscriptionProperty(subId, propKey,
+ context.getOpPackageName());
+ }
+ } catch (RemoteException ex) {
+ // ignore it
+ }
+ return resultValue;
+ }
+
+ /**
+ * Returns boolean value corresponding to query result.
+ * @param subId Subscription Id of Subscription
+ * @param propKey Column name in SubscriptionInfo database
+ * @param defValue Default boolean value to be returned
+ * @return boolean result value to be returned
+ * @hide
+ */
+ public static boolean getBooleanSubscriptionProperty(int subId, String propKey,
+ boolean defValue, Context context) {
+ String result = getSubscriptionProperty(subId, propKey, context);
+ if (result != null) {
+ try {
+ return Integer.parseInt(result) == 1;
+ } catch (NumberFormatException err) {
+ logd("getBooleanSubscriptionProperty NumberFormat exception");
+ }
+ }
+ return defValue;
+ }
+
+ /**
+ * Returns integer value corresponding to query result.
+ * @param subId Subscription Id of Subscription
+ * @param propKey Column name in SubscriptionInfo database
+ * @param defValue Default integer value to be returned
+ * @return integer result value to be returned
+ * @hide
+ */
+ public static int getIntegerSubscriptionProperty(int subId, String propKey, int defValue,
+ Context context) {
+ String result = getSubscriptionProperty(subId, propKey, context);
+ if (result != null) {
+ try {
+ return Integer.parseInt(result);
+ } catch (NumberFormatException err) {
+ logd("getBooleanSubscriptionProperty NumberFormat exception");
+ }
+ }
+ return defValue;
+ }
+
+ /**
+ * Returns the resources associated with Subscription.
+ * @param context Context object
+ * @param subId Subscription Id of Subscription who's resources are required
+ * @return Resources associated with Subscription.
+ * @hide
+ */
+ public static Resources getResourcesForSubId(Context context, int subId) {
+ final SubscriptionInfo subInfo =
+ SubscriptionManager.from(context).getActiveSubscriptionInfo(subId);
+
+ Configuration config = context.getResources().getConfiguration();
+ Configuration newConfig = new Configuration();
+ newConfig.setTo(config);
+ if (subInfo != null) {
+ newConfig.mcc = subInfo.getMcc();
+ newConfig.mnc = subInfo.getMnc();
+ }
+ DisplayMetrics metrics = context.getResources().getDisplayMetrics();
+ DisplayMetrics newMetrics = new DisplayMetrics();
+ newMetrics.setTo(metrics);
+ return new Resources(context.getResources().getAssets(), newMetrics, newConfig);
+ }
+
+ /**
* @return true if the sub ID is active. i.e. The sub ID corresponds to a known subscription
* and the SIM providing the subscription is present in a slot and in "LOADED" state.
* @hide
diff --git a/telephony/java/com/android/internal/telephony/ISub.aidl b/telephony/java/com/android/internal/telephony/ISub.aidl
index 0555121..f6aef08 100755
--- a/telephony/java/com/android/internal/telephony/ISub.aidl
+++ b/telephony/java/com/android/internal/telephony/ISub.aidl
@@ -172,6 +172,10 @@
int[] getActiveSubIdList();
+ void setSubscriptionProperty(int subId, String propKey, String propValue);
+
+ String getSubscriptionProperty(int subId, String propKey, String callingPackage);
+
/**
* Get the SIM state for the slot idx
* @return SIM state as the ordinal of IccCardConstants.State
diff --git a/tools/layoutlib/bridge/src/android/view/BridgeInflater.java b/tools/layoutlib/bridge/src/android/view/BridgeInflater.java
index 20b6e41..1e33e3a 100644
--- a/tools/layoutlib/bridge/src/android/view/BridgeInflater.java
+++ b/tools/layoutlib/bridge/src/android/view/BridgeInflater.java
@@ -25,6 +25,7 @@
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.DrawerLayoutUtil;
import com.android.layoutlib.bridge.android.support.RecyclerViewUtil;
import com.android.layoutlib.bridge.impl.ParserFactory;
import com.android.layoutlib.bridge.util.ReflectionUtils;
@@ -33,10 +34,13 @@
import org.xmlpull.v1.XmlPullParser;
+import android.annotation.NonNull;
import android.content.Context;
import android.util.AttributeSet;
import java.io.File;
+import java.util.HashMap;
+import java.util.Map;
import static com.android.layoutlib.bridge.android.BridgeContext.getBaseContext;
@@ -48,6 +52,7 @@
private final LayoutlibCallback mLayoutlibCallback;
private boolean mIsInMerge = false;
private ResourceReference mResourceReference;
+ private Map<View, String> mOpenDrawerLayouts;
/**
* List of class prefixes which are tried first by default.
@@ -256,7 +261,14 @@
resourceId = 0;
}
RecyclerViewUtil.setAdapter(view, bc, mLayoutlibCallback, resourceId);
+ } else if (ReflectionUtils.isInstanceOf(view, DrawerLayoutUtil.CN_DRAWER_LAYOUT)) {
+ String attrVal = attrs.getAttributeValue(BridgeConstants.NS_TOOLS_URI,
+ BridgeConstants.ATTR_OPEN_DRAWER);
+ if (attrVal != null) {
+ getDrawerLayoutMap().put(view, attrVal);
+ }
}
+
}
}
@@ -312,4 +324,28 @@
return viewKey;
}
+
+ public void postInflateProcess(View view) {
+ if (mOpenDrawerLayouts != null) {
+ String gravity = mOpenDrawerLayouts.get(view);
+ if (gravity != null) {
+ DrawerLayoutUtil.openDrawer(view, gravity);
+ }
+ mOpenDrawerLayouts.remove(view);
+ }
+ }
+
+ @NonNull
+ private Map<View, String> getDrawerLayoutMap() {
+ if (mOpenDrawerLayouts == null) {
+ mOpenDrawerLayouts = new HashMap<View, String>(4);
+ }
+ return mOpenDrawerLayouts;
+ }
+
+ public void onDoneInflation() {
+ if (mOpenDrawerLayouts != null) {
+ mOpenDrawerLayouts.clear();
+ }
+ }
}
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 661c08b9..6228766 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/BridgeConstants.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/BridgeConstants.java
@@ -50,6 +50,9 @@
public final static String FILL_PARENT = "fill_parent";
public final static String WRAP_CONTENT = "wrap_content";
+ // Should be kept in sync with LayoutMetadata.KEY_LV_ITEM in tools/adt/idea
/** Attribute in the tools namespace used to specify layout manager for RecyclerView. */
- public static final String ATTR_LIST_ITEM = "list_item";
+ @SuppressWarnings("SpellCheckingInspection")
+ public static final String ATTR_LIST_ITEM = "listitem";
+ public static final String ATTR_OPEN_DRAWER = "openDrawer";
}
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/support/DrawerLayoutUtil.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/support/DrawerLayoutUtil.java
new file mode 100644
index 0000000..40d3811
--- /dev/null
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/support/DrawerLayoutUtil.java
@@ -0,0 +1,63 @@
+/*
+ * 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.ide.common.rendering.api.LayoutLog;
+import com.android.layoutlib.bridge.Bridge;
+import com.android.layoutlib.bridge.util.ReflectionUtils.ReflectionException;
+
+import android.annotation.Nullable;
+import android.view.View;
+
+import static android.view.Gravity.END;
+import static android.view.Gravity.LEFT;
+import static android.view.Gravity.RIGHT;
+import static android.view.Gravity.START;
+import static com.android.layoutlib.bridge.util.ReflectionUtils.getCause;
+import static com.android.layoutlib.bridge.util.ReflectionUtils.getMethod;
+import static com.android.layoutlib.bridge.util.ReflectionUtils.invoke;
+
+public class DrawerLayoutUtil {
+
+ public static final String CN_DRAWER_LAYOUT = "android.support.v4.widget.DrawerLayout";
+
+ public static void openDrawer(View drawerLayout, @Nullable String drawerGravity) {
+ int gravity = -1;
+ if ("left".equals(drawerGravity)) {
+ gravity = LEFT;
+ } else if ("right".equals(drawerGravity)) {
+ gravity = RIGHT;
+ } else if ("start".equals(drawerGravity)) {
+ gravity = START;
+ } else if ("end".equals(drawerGravity)) {
+ gravity = END;
+ }
+ if (gravity > 0) {
+ openDrawer(drawerLayout, gravity);
+ }
+ }
+
+ private static void openDrawer(View drawerLayout, int gravity) {
+ try {
+ invoke(getMethod(drawerLayout.getClass(), "openDrawer", int.class), drawerLayout,
+ gravity);
+ } catch (ReflectionException e) {
+ Bridge.getLog().error(LayoutLog.TAG_BROKEN, "Unable to open navigation drawer",
+ getCause(e), null);
+ }
+ }
+}
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
index 4182cd9..d14c80b 100644
--- 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
@@ -21,6 +21,7 @@
import com.android.layoutlib.bridge.Bridge;
import com.android.layoutlib.bridge.android.BridgeContext;
import com.android.layoutlib.bridge.android.RenderParamsFlags;
+import com.android.layoutlib.bridge.util.ReflectionUtils;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -30,6 +31,7 @@
import java.lang.reflect.Method;
import static com.android.layoutlib.bridge.util.ReflectionUtils.ReflectionException;
+import static com.android.layoutlib.bridge.util.ReflectionUtils.getCause;
import static com.android.layoutlib.bridge.util.ReflectionUtils.getMethod;
import static com.android.layoutlib.bridge.util.ReflectionUtils.invoke;
@@ -70,11 +72,6 @@
}
}
- private static Throwable getCause(Throwable throwable) {
- Throwable cause = throwable.getCause();
- return cause == null ? throwable : cause;
- }
-
private static void setLayoutManager(@NonNull View recyclerView, @NonNull BridgeContext context,
@NonNull LayoutlibCallback callback) throws ReflectionException {
if (getLayoutManager(recyclerView) == null) {
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 72e97ad..23df3f1 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
@@ -423,6 +423,7 @@
// post-inflate process. For now this supports TabHost/TabWidget
postInflateProcess(view, params.getLayoutlibCallback(), isPreference ? view : null);
+ mInflater.onDoneInflation();
setActiveToolbar(view, context, params);
@@ -1342,6 +1343,7 @@
}
}
} else if (view instanceof ViewGroup) {
+ mInflater.postInflateProcess(view);
ViewGroup group = (ViewGroup) view;
final int count = group.getChildCount();
for (int c = 0; c < count; c++) {
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
index b78b613..7ce27b6 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/util/ReflectionUtils.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/util/ReflectionUtils.java
@@ -27,7 +27,7 @@
*/
public class ReflectionUtils {
- @Nullable
+ @NonNull
public static Method getMethod(@NonNull Class<?> clazz, @NonNull String name,
@Nullable Class<?>... params) throws ReflectionException {
try {
@@ -67,6 +67,12 @@
return false;
}
+ @NonNull
+ public static Throwable getCause(@NonNull Throwable throwable) {
+ Throwable cause = throwable.getCause();
+ return cause == null ? throwable : cause;
+ }
+
/**
* Wraps all reflection related exceptions. Created since ReflectiveOperationException was
* introduced in 1.7 and we are still on 1.6