Merge "Keep default permission grants uninstalling a sys package upgrade" into mnc-dev
diff --git a/cmds/wm/src/com/android/commands/wm/Wm.java b/cmds/wm/src/com/android/commands/wm/Wm.java
index fb050e5..f7f7c88 100644
--- a/cmds/wm/src/com/android/commands/wm/Wm.java
+++ b/cmds/wm/src/com/android/commands/wm/Wm.java
@@ -65,7 +65,10 @@
"\n" +
"wm scaling: set display scaling mode.\n" +
"\n" +
- "wm screen-capture: enable/disable screen capture.\n"
+ "wm screen-capture: enable/disable screen capture.\n" +
+ "\n" +
+ "wm dismiss-keyguard: dismiss the keyguard, prompting the user for auth if " +
+ "necessary.\n"
);
}
@@ -90,6 +93,8 @@
runDisplayScaling();
} else if (op.equals("screen-capture")) {
runSetScreenCapture();
+ } else if (op.equals("dismiss-keyguard")) {
+ runDismissKeyguard();
} else {
showError("Error: unknown command '" + op + "'");
return;
@@ -240,6 +245,10 @@
}
}
+ private void runDismissKeyguard() throws Exception {
+ mWm.dismissKeyguard();
+ }
+
private int parseDimension(String s) throws NumberFormatException {
if (s.endsWith("px")) {
return Integer.parseInt(s.substring(0, s.length() - 2));
diff --git a/core/java/android/app/IUiAutomationConnection.aidl b/core/java/android/app/IUiAutomationConnection.aidl
index 8ab9ac3..474154b 100644
--- a/core/java/android/app/IUiAutomationConnection.aidl
+++ b/core/java/android/app/IUiAutomationConnection.aidl
@@ -38,10 +38,12 @@
boolean injectInputEvent(in InputEvent event, boolean sync);
boolean setRotation(int rotation);
Bitmap takeScreenshot(int width, int height);
- void shutdown();
boolean clearWindowContentFrameStats(int windowId);
WindowContentFrameStats getWindowContentFrameStats(int windowId);
void clearWindowAnimationFrameStats();
WindowAnimationFrameStats getWindowAnimationFrameStats();
void executeShellCommand(String command, in ParcelFileDescriptor fd);
+
+ // Called from the system process.
+ oneway void shutdown();
}
diff --git a/core/java/android/content/pm/IPackageManager.aidl b/core/java/android/content/pm/IPackageManager.aidl
index ceb610a..bc24d67 100644
--- a/core/java/android/content/pm/IPackageManager.aidl
+++ b/core/java/android/content/pm/IPackageManager.aidl
@@ -253,7 +253,7 @@
List<PackageInfo> getPreferredPackages(int flags);
- void resetPreferredActivities(int userId);
+ void resetApplicationPreferences(int userId);
ResolveInfo getLastChosenActivity(in Intent intent,
String resolvedType, int flags);
diff --git a/core/java/android/view/inputmethod/InputMethodManagerInternal.java b/core/java/android/view/inputmethod/InputMethodManagerInternal.java
new file mode 100644
index 0000000..c22127b
--- /dev/null
+++ b/core/java/android/view/inputmethod/InputMethodManagerInternal.java
@@ -0,0 +1,30 @@
+/*
+ * 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 android.view.inputmethod;
+
+/**
+ * Input method manager local system service interface.
+ *
+ * @hide Only for use within the system server.
+ */
+public interface InputMethodManagerInternal {
+ /**
+ * Called by the power manager to tell the input method manager whether it
+ * should start watching for wake events.
+ */
+ public void setInteractive(boolean interactive);
+}
diff --git a/libs/hwui/AnimatorManager.cpp b/libs/hwui/AnimatorManager.cpp
index 966959a..0dababd 100644
--- a/libs/hwui/AnimatorManager.cpp
+++ b/libs/hwui/AnimatorManager.cpp
@@ -117,7 +117,6 @@
uint32_t dirty = animateCommon(info);
- mParent.mProperties.updateMatrix();
info.damageAccumulator->pushTransform(&mParent);
mParent.damageSelf(info);
@@ -136,6 +135,7 @@
newEnd = std::remove_if(mAnimators.begin(), mAnimators.end(), functor);
mAnimators.erase(newEnd, mAnimators.end());
mAnimationHandle->notifyAnimationsRan();
+ mParent.mProperties.updateMatrix();
return functor.dirtyMask;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
index e1ba2d5..75710cd 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
@@ -436,6 +436,12 @@
public void onPlaybackStateChanged(PlaybackState state) {
super.onPlaybackStateChanged(state);
if (DEBUG_MEDIA) Log.v(TAG, "DEBUG_MEDIA: onPlaybackStateChanged: " + state);
+ if (state != null) {
+ if (!isPlaybackActive(state.getState())) {
+ clearCurrentMediaNotification();
+ updateMediaMetaData(true);
+ }
+ }
}
@Override
@@ -1199,6 +1205,10 @@
if (mHeadsUpManager.isHeadsUp(key)) {
deferRemoval = !mHeadsUpManager.removeNotification(key);
}
+ if (key.equals(mMediaNotificationKey)) {
+ clearCurrentMediaNotification();
+ updateMediaMetaData(true);
+ }
if (deferRemoval) {
mLatestRankingMap = ranking;
mHeadsUpEntriesToRemoveOnSwitch.add(mHeadsUpManager.getEntry(key));
@@ -1492,23 +1502,31 @@
synchronized (mNotificationData) {
ArrayList<Entry> activeNotifications = mNotificationData.getActiveNotifications();
final int N = activeNotifications.size();
+
+ // Promote the media notification with a controller in 'playing' state, if any.
Entry mediaNotification = null;
MediaController controller = null;
for (int i = 0; i < N; i++) {
final Entry entry = activeNotifications.get(i);
if (isMediaNotification(entry)) {
- final MediaSession.Token token = entry.notification.getNotification().extras
+ final MediaSession.Token token =
+ entry.notification.getNotification().extras
.getParcelable(Notification.EXTRA_MEDIA_SESSION);
if (token != null) {
- controller = new MediaController(mContext, token);
- if (controller != null) {
- // we've got a live one, here
+ MediaController aController = new MediaController(mContext, token);
+ if (PlaybackState.STATE_PLAYING ==
+ getMediaControllerPlaybackState(aController)) {
+ if (DEBUG_MEDIA) {
+ Log.v(TAG, "DEBUG_MEDIA: found mediastyle controller matching "
+ + entry.notification.getKey());
+ }
mediaNotification = entry;
+ controller = aController;
+ break;
}
}
}
}
-
if (mediaNotification == null) {
// Still nothing? OK, let's just look for live media sessions and see if they match
// one of our notifications. This will catch apps that aren't (yet!) using media
@@ -1521,83 +1539,88 @@
UserHandle.USER_ALL);
for (MediaController aController : sessions) {
- if (aController == null) continue;
- final PlaybackState state = aController.getPlaybackState();
- if (state == null) continue;
- switch (state.getState()) {
- case PlaybackState.STATE_STOPPED:
- case PlaybackState.STATE_ERROR:
- continue;
- default:
- // now to see if we have one like this
- final String pkg = aController.getPackageName();
+ if (PlaybackState.STATE_PLAYING ==
+ getMediaControllerPlaybackState(aController)) {
+ // now to see if we have one like this
+ final String pkg = aController.getPackageName();
- for (int i = 0; i < N; i++) {
- final Entry entry = activeNotifications.get(i);
- if (entry.notification.getPackageName().equals(pkg)) {
- if (DEBUG_MEDIA) {
- Log.v(TAG, "DEBUG_MEDIA: found controller matching "
- + entry.notification.getKey());
- }
- controller = aController;
- mediaNotification = entry;
- break;
+ for (int i = 0; i < N; i++) {
+ final Entry entry = activeNotifications.get(i);
+ if (entry.notification.getPackageName().equals(pkg)) {
+ if (DEBUG_MEDIA) {
+ Log.v(TAG, "DEBUG_MEDIA: found controller matching "
+ + entry.notification.getKey());
}
+ controller = aController;
+ mediaNotification = entry;
+ break;
}
+ }
}
}
}
}
- if (!sameSessions(mMediaController, controller)) {
+ if (controller != null && !sameSessions(mMediaController, controller)) {
// We have a new media session
-
- if (mMediaController != null) {
- // something old was playing
- Log.v(TAG, "DEBUG_MEDIA: Disconnecting from old controller: "
- + mMediaController);
- mMediaController.unregisterCallback(mMediaListener);
- }
+ clearCurrentMediaNotification();
mMediaController = controller;
-
- if (mMediaController != null) {
- mMediaController.registerCallback(mMediaListener);
- mMediaMetadata = mMediaController.getMetadata();
- if (DEBUG_MEDIA) {
- Log.v(TAG, "DEBUG_MEDIA: insert listener, receive metadata: "
- + mMediaMetadata);
- }
-
- final String notificationKey = mediaNotification == null
- ? null
- : mediaNotification.notification.getKey();
-
- if (notificationKey == null || !notificationKey.equals(mMediaNotificationKey)) {
- // we have a new notification!
- if (DEBUG_MEDIA) {
- Log.v(TAG, "DEBUG_MEDIA: Found new media notification: key="
- + notificationKey + " controller=" + controller);
- }
- mMediaNotificationKey = notificationKey;
- }
- } else {
- mMediaMetadata = null;
- mMediaNotificationKey = null;
- }
-
- metaDataChanged = true;
- } else {
- // Media session unchanged
-
+ mMediaController.registerCallback(mMediaListener);
+ mMediaMetadata = mMediaController.getMetadata();
if (DEBUG_MEDIA) {
- Log.v(TAG, "DEBUG_MEDIA: Continuing media notification: key=" + mMediaNotificationKey);
+ Log.v(TAG, "DEBUG_MEDIA: insert listener, receive metadata: "
+ + mMediaMetadata);
}
+
+ if (mediaNotification != null) {
+ mMediaNotificationKey = mediaNotification.notification.getKey();
+ if (DEBUG_MEDIA) {
+ Log.v(TAG, "DEBUG_MEDIA: Found new media notification: key="
+ + mMediaNotificationKey + " controller=" + mMediaController);
+ }
+ }
+ metaDataChanged = true;
}
}
+ if (metaDataChanged) {
+ updateNotifications();
+ }
updateMediaMetaData(metaDataChanged);
}
+ private int getMediaControllerPlaybackState(MediaController controller) {
+ if (controller != null) {
+ final PlaybackState playbackState = controller.getPlaybackState();
+ if (playbackState != null) {
+ return playbackState.getState();
+ }
+ }
+ return PlaybackState.STATE_NONE;
+ }
+
+ private boolean isPlaybackActive(int state) {
+ if (state != PlaybackState.STATE_STOPPED
+ && state != PlaybackState.STATE_ERROR
+ && state != PlaybackState.STATE_NONE) {
+ return true;
+ }
+ return false;
+ }
+
+ private void clearCurrentMediaNotification() {
+ mMediaNotificationKey = null;
+ mMediaMetadata = null;
+ if (mMediaController != null) {
+ if (DEBUG_MEDIA) {
+ Log.v(TAG, "DEBUG_MEDIA: Disconnecting from old controller: "
+ + mMediaController.getPackageName());
+ }
+ mMediaController.unregisterCallback(mMediaListener);
+ }
+ mMediaController = null;
+ }
+
private boolean sameSessions(MediaController a, MediaController b) {
if (a == b) return true;
if (a == null) return false;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java
index 7cd94d7..57dfff5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java
@@ -31,6 +31,7 @@
import android.os.Handler;
import android.os.Looper;
import android.provider.Settings;
+import android.telephony.ServiceState;
import android.telephony.SubscriptionInfo;
import android.telephony.SubscriptionManager;
import android.telephony.SubscriptionManager.OnSubscriptionsChangedListener;
@@ -117,6 +118,9 @@
// Handler that all callbacks are made on.
private final CallbackHandler mCallbackHandler;
+ @VisibleForTesting
+ ServiceState mLastServiceState;
+
/**
* Construct this controller object and register for updates.
*/
@@ -194,6 +198,7 @@
filter.addAction(TelephonyIntents.ACTION_SIM_STATE_CHANGED);
filter.addAction(TelephonyIntents.ACTION_DEFAULT_DATA_SUBSCRIPTION_CHANGED);
filter.addAction(TelephonyIntents.ACTION_DEFAULT_VOICE_SUBSCRIPTION_CHANGED);
+ filter.addAction(TelephonyIntents.ACTION_SERVICE_STATE_CHANGED);
filter.addAction(TelephonyIntents.SPN_STRINGS_UPDATED_ACTION);
filter.addAction(ConnectivityManager.CONNECTIVITY_ACTION);
filter.addAction(ConnectivityManager.INET_CONDITION_ACTION);
@@ -259,6 +264,11 @@
}
public boolean isEmergencyOnly() {
+ if (mMobileSignalControllers.size() == 0) {
+ // When there are no active subscriptions, determine emengency state from last
+ // broadcast.
+ return mLastServiceState != null && mLastServiceState.isEmergencyOnly();
+ }
int voiceSubId = mSubDefaults.getDefaultVoiceSubId();
if (!SubscriptionManager.isValidSubscriptionId(voiceSubId)) {
for (MobileSignalController mobileSignalController :
@@ -353,6 +363,13 @@
} else if (action.equals(TelephonyIntents.ACTION_SIM_STATE_CHANGED)) {
// Might have different subscriptions now.
updateMobileControllers();
+ } else if (action.equals(TelephonyIntents.ACTION_SERVICE_STATE_CHANGED)) {
+ mLastServiceState = ServiceState.newFromBundle(intent.getExtras());
+ if (mMobileSignalControllers.size() == 0) {
+ // If none of the subscriptions are active, we might need to recalculate
+ // emergency state.
+ recalculateEmergency();
+ }
} else {
int subId = intent.getIntExtra(PhoneConstants.SUBSCRIPTION_KEY,
SubscriptionManager.INVALID_SUBSCRIPTION_ID);
@@ -587,6 +604,8 @@
pw.println(mAirplaneMode);
pw.print(" mLocale=");
pw.println(mLocale);
+ pw.print(" mLastServiceState=");
+ pw.println(mLastServiceState);
for (MobileSignalController mobileSignalController : mMobileSignalControllers.values()) {
mobileSignalController.dump(pw);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerSignalTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerSignalTest.java
index 52dea40..d9b9063 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerSignalTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerSignalTest.java
@@ -21,6 +21,7 @@
import android.net.ConnectivityManager;
import android.net.NetworkCapabilities;
import android.os.Looper;
+import android.telephony.ServiceState;
import android.telephony.SignalStrength;
import android.telephony.SubscriptionInfo;
import android.telephony.TelephonyManager;
@@ -70,11 +71,20 @@
public void testEmergencyOnlyNoSubscriptions() {
setupDefaultSignal();
+ setSubscriptions();
+ mNetworkController.mLastServiceState = new ServiceState();
+ mNetworkController.mLastServiceState.setEmergencyOnly(true);
+ mNetworkController.recalculateEmergency();
+ verifyEmergencyOnly(true);
+ }
+
+ public void testNoEmengencyNoSubscriptions() {
+ setupDefaultSignal();
+ setSubscriptions();
+ mNetworkController.mLastServiceState = new ServiceState();
+ mNetworkController.mLastServiceState.setEmergencyOnly(false);
mNetworkController.recalculateEmergency();
verifyEmergencyOnly(false);
-
- setSubscriptions();
- verifyEmergencyOnly(true);
}
public void testNoSimlessIconWithoutMobile() {
diff --git a/services/core/java/com/android/server/InputMethodManagerService.java b/services/core/java/com/android/server/InputMethodManagerService.java
index dbe8781..64ee5f1 100644
--- a/services/core/java/com/android/server/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/InputMethodManagerService.java
@@ -114,6 +114,7 @@
import android.view.inputmethod.InputMethod;
import android.view.inputmethod.InputMethodInfo;
import android.view.inputmethod.InputMethodManager;
+import android.view.inputmethod.InputMethodManagerInternal;
import android.view.inputmethod.InputMethodSubtype;
import android.view.inputmethod.InputMethodSubtype.InputMethodSubtypeBuilder;
import android.widget.ArrayAdapter;
@@ -166,6 +167,7 @@
static final int MSG_UNBIND_METHOD = 3000;
static final int MSG_BIND_METHOD = 3010;
static final int MSG_SET_ACTIVE = 3020;
+ static final int MSG_SET_INTERACTIVE = 3030;
static final int MSG_SET_USER_ACTION_NOTIFICATION_SEQUENCE_NUMBER = 3040;
static final int MSG_HARD_KEYBOARD_SWITCH_CHANGED = 4000;
@@ -394,9 +396,9 @@
SessionState mEnabledSession;
/**
- * True if the screen is on. The value is true initially.
+ * True if the device is currently interactive with user. The value is true initially.
*/
- boolean mScreenOn = true;
+ boolean mIsInteractive = true;
int mCurUserActionNotificationSequenceNumber = 0;
@@ -492,30 +494,12 @@
}
class ImmsBroadcastReceiver extends android.content.BroadcastReceiver {
- private void updateActive() {
- // Inform the current client of the change in active status
- if (mCurClient != null && mCurClient.client != null) {
- executeOrSendMessage(mCurClient.client, mCaller.obtainMessageIO(
- MSG_SET_ACTIVE, mScreenOn ? 1 : 0, mCurClient));
- }
- }
-
@Override
public void onReceive(Context context, Intent intent) {
final String action = intent.getAction();
- if (Intent.ACTION_SCREEN_ON.equals(action)) {
- mScreenOn = true;
- updateSystemUi(mCurToken, mImeWindowVis, mBackDisposition);
- updateActive();
- return;
- } else if (Intent.ACTION_SCREEN_OFF.equals(action)) {
- mScreenOn = false;
- updateSystemUi(mCurToken, 0 /* vis */, mBackDisposition);
- updateActive();
- return;
- } else if (Intent.ACTION_CLOSE_SYSTEM_DIALOGS.equals(action)) {
+ if (Intent.ACTION_CLOSE_SYSTEM_DIALOGS.equals(action)) {
hideInputMethodMenu();
- // No need to updateActive
+ // No need to update mIsInteractive
return;
} else if (Intent.ACTION_USER_ADDED.equals(action)
|| Intent.ACTION_USER_REMOVED.equals(action)) {
@@ -817,8 +801,6 @@
mShowOngoingImeSwitcherForPhones = false;
final IntentFilter broadcastFilter = new IntentFilter();
- broadcastFilter.addAction(Intent.ACTION_SCREEN_ON);
- broadcastFilter.addAction(Intent.ACTION_SCREEN_OFF);
broadcastFilter.addAction(Intent.ACTION_CLOSE_SYSTEM_DIALOGS);
broadcastFilter.addAction(Intent.ACTION_USER_ADDED);
broadcastFilter.addAction(Intent.ACTION_USER_REMOVED);
@@ -938,6 +920,7 @@
}
}
}, filter);
+ LocalServices.addService(InputMethodManagerInternal.class, new LocalServiceImpl(mHandler));
}
private void resetDefaultImeLocked(Context context) {
@@ -1379,9 +1362,9 @@
+ cs.client.asBinder() + " keyguard=" + mCurClientInKeyguard);
// If the screen is on, inform the new client it is active
- if (mScreenOn) {
+ if (mIsInteractive) {
executeOrSendMessage(cs.client, mCaller.obtainMessageIO(
- MSG_SET_ACTIVE, mScreenOn ? 1 : 0, cs));
+ MSG_SET_ACTIVE, mIsInteractive ? 1 : 0, cs));
}
}
@@ -2851,6 +2834,9 @@
+ ((ClientState)msg.obj).uid);
}
return true;
+ case MSG_SET_INTERACTIVE:
+ handleSetInteractive(msg.arg1 != 0);
+ return true;
case MSG_SET_USER_ACTION_NOTIFICATION_SEQUENCE_NUMBER: {
final int sequenceNumber = msg.arg1;
final ClientState clientState = (ClientState)msg.obj;
@@ -2874,6 +2860,19 @@
return false;
}
+ private void handleSetInteractive(final boolean interactive) {
+ synchronized (mMethodMap) {
+ mIsInteractive = interactive;
+ updateSystemUiLocked(mCurToken, interactive ? mImeWindowVis : 0, mBackDisposition);
+
+ // Inform the current client of the change in active status
+ if (mCurClient != null && mCurClient.client != null) {
+ executeOrSendMessage(mCurClient.client, mCaller.obtainMessageIO(
+ MSG_SET_ACTIVE, mIsInteractive ? 1 : 0, mCurClient));
+ }
+ }
+ }
+
private boolean chooseNewDefaultIMELocked() {
final InputMethodInfo imi = InputMethodUtils.getMostApplicableDefaultIME(
mSettings.getEnabledInputMethodListLocked());
@@ -3734,6 +3733,22 @@
}
}
+ private static final class LocalServiceImpl implements InputMethodManagerInternal {
+ @NonNull
+ private final Handler mHandler;
+
+ LocalServiceImpl(@NonNull final Handler handler) {
+ mHandler = handler;
+ }
+
+ @Override
+ public void setInteractive(boolean interactive) {
+ // Do everything in handler so as not to block the caller.
+ mHandler.sendMessage(mHandler.obtainMessage(MSG_SET_INTERACTIVE,
+ interactive ? 1 : 0, 0));
+ }
+ }
+
@Override
protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
@@ -3784,7 +3799,7 @@
+ " mInputShown=" + mInputShown);
p.println(" mCurUserActionNotificationSequenceNumber="
+ mCurUserActionNotificationSequenceNumber);
- p.println(" mSystemReady=" + mSystemReady + " mInteractive=" + mScreenOn);
+ p.println(" mSystemReady=" + mSystemReady + " mInteractive=" + mIsInteractive);
p.println(" mSettingsObserver=" + mSettingsObserver);
p.println(" mSwitchingController:");
mSwitchingController.dump(p);
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index eb6579c9..4d9695d 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -1355,6 +1355,7 @@
static final int DISPATCH_UIDS_CHANGED_MSG = 54;
static final int REPORT_TIME_TRACKER_MSG = 55;
static final int REPORT_USER_SWITCH_COMPLETE_MSG = 56;
+ static final int SHUTDOWN_UI_AUTOMATION_CONNECTION_MSG = 57;
static final int FIRST_ACTIVITY_STACK_MSG = 100;
static final int FIRST_BROADCAST_QUEUE_MSG = 200;
@@ -2019,6 +2020,17 @@
case REPORT_USER_SWITCH_COMPLETE_MSG: {
dispatchUserSwitchComplete(msg.arg1);
} break;
+ case SHUTDOWN_UI_AUTOMATION_CONNECTION_MSG: {
+ IUiAutomationConnection connection = (IUiAutomationConnection) msg.obj;
+ try {
+ connection.shutdown();
+ } catch (RemoteException e) {
+ Slog.w(TAG, "Error shutting down UiAutomationConnection");
+ }
+ // Only a UiAutomation can set this flag and now that
+ // it is finished we make sure it is reset to its default.
+ mUserIsMonkey = false;
+ } break;
}
}
};
@@ -17102,16 +17114,11 @@
} catch (RemoteException e) {
}
}
- if (app.instrumentationUiAutomationConnection != null) {
- try {
- app.instrumentationUiAutomationConnection.shutdown();
- } catch (RemoteException re) {
- /* ignore */
- }
- // Only a UiAutomation can set this flag and now that
- // it is finished we make sure it is reset to its default.
- mUserIsMonkey = false;
- }
+
+ // Can't call out of the system process with a lock held, so post a message.
+ mHandler.obtainMessage(SHUTDOWN_UI_AUTOMATION_CONNECTION_MSG,
+ app.instrumentationUiAutomationConnection).sendToTarget();
+
app.instrumentationWatcher = null;
app.instrumentationUiAutomationConnection = null;
app.instrumentationClass = null;
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 70a220c..61ed1a7 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -3555,7 +3555,7 @@
continue;
}
PackageSetting ps = (PackageSetting) pkg.mExtras;
- resetUserChangesToRuntimePermissionsAndFlagsLocked(ps, userId);
+ resetUserChangesToRuntimePermissionsAndFlagsLPw(ps, userId);
}
}
}
@@ -13092,7 +13092,7 @@
if (clearPackagePreferredActivitiesLPw(packageName, removeUser)) {
scheduleWritePackageRestrictionsLocked(removeUser);
}
- resetUserChangesToRuntimePermissionsAndFlagsLocked(ps, removeUser);
+ resetUserChangesToRuntimePermissionsAndFlagsLPw(ps, removeUser);
}
return true;
}
@@ -13253,7 +13253,7 @@
}
PackageSetting ps = (PackageSetting) pkg.mExtras;
- resetUserChangesToRuntimePermissionsAndFlagsLocked(ps, userId);
+ resetUserChangesToRuntimePermissionsAndFlagsLPw(ps, userId);
}
// Always delete data directories for package, even if we found no other
@@ -13285,12 +13285,27 @@
}
/**
+ * Reverts user permission state changes (permissions and flags) in
+ * all packages for a given user.
+ *
+ * @param userId The device user for which to do a reset.
+ */
+ private void resetUserChangesToRuntimePermissionsAndFlagsLPw(int userId) {
+ final int packageCount = mPackages.size();
+ for (int i = 0; i < packageCount; i++) {
+ PackageParser.Package pkg = mPackages.valueAt(i);
+ PackageSetting ps = (PackageSetting) pkg.mExtras;
+ resetUserChangesToRuntimePermissionsAndFlagsLPw(ps, userId);
+ }
+ }
+
+ /**
* Reverts user permission state changes (permissions and flags).
*
* @param ps The package for which to reset.
* @param userId The device user for which to do a reset.
*/
- private void resetUserChangesToRuntimePermissionsAndFlagsLocked(
+ private void resetUserChangesToRuntimePermissionsAndFlagsLPw(
final PackageSetting ps, final int userId) {
if (ps.pkg == null) {
return;
@@ -13790,6 +13805,15 @@
}
/** This method takes a specific user id as well as UserHandle.USER_ALL. */
+ private void clearIntentFilterVerificationsLPw(int userId) {
+ final int packageCount = mPackages.size();
+ for (int i = 0; i < packageCount; i++) {
+ PackageParser.Package pkg = mPackages.valueAt(i);
+ clearIntentFilterVerificationsLPw(pkg.packageName, userId);
+ }
+ }
+
+ /** This method takes a specific user id as well as UserHandle.USER_ALL. */
void clearIntentFilterVerificationsLPw(String packageName, int userId) {
if (userId == UserHandle.USER_ALL) {
if (mSettings.removeIntentFilterVerificationLPw(packageName,
@@ -13805,7 +13829,6 @@
}
}
-
void clearDefaultBrowserIfNeeded(String packageName) {
for (int oneUserId : sUserManager.getUserIds()) {
String defaultBrowserPackageName = getDefaultBrowserPackageName(oneUserId);
@@ -13817,17 +13840,27 @@
}
@Override
- public void resetPreferredActivities(int userId) {
+ public void resetApplicationPreferences(int userId) {
mContext.enforceCallingOrSelfPermission(
android.Manifest.permission.SET_PREFERRED_APPLICATIONS, null);
// writer
synchronized (mPackages) {
- clearPackagePreferredActivitiesLPw(null, userId);
- mSettings.applyDefaultPreferredAppsLPw(this, userId);
- applyFactoryDefaultBrowserLPw(userId);
- primeDomainVerificationsLPw(userId);
-
- scheduleWritePackageRestrictionsLocked(userId);
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ clearPackagePreferredActivitiesLPw(null, userId);
+ mSettings.applyDefaultPreferredAppsLPw(this, userId);
+ // TODO: We have to reset the default SMS and Phone. This requires
+ // significant refactoring to keep all default apps in the package
+ // manager (cleaner but more work) or have the services provide
+ // callbacks to the package manager to request a default app reset.
+ applyFactoryDefaultBrowserLPw(userId);
+ clearIntentFilterVerificationsLPw(userId);
+ primeDomainVerificationsLPw(userId);
+ resetUserChangesToRuntimePermissionsAndFlagsLPw(userId);
+ scheduleWritePackageRestrictionsLocked(userId);
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
}
}
diff --git a/services/core/java/com/android/server/power/Notifier.java b/services/core/java/com/android/server/power/Notifier.java
index c5ad7fe..7108f4a 100644
--- a/services/core/java/com/android/server/power/Notifier.java
+++ b/services/core/java/com/android/server/power/Notifier.java
@@ -48,6 +48,7 @@
import android.util.EventLog;
import android.util.Slog;
import android.view.WindowManagerPolicy;
+import android.view.inputmethod.InputMethodManagerInternal;
/**
* Sends broadcasts about important power state changes.
@@ -89,6 +90,7 @@
private final WindowManagerPolicy mPolicy;
private final ActivityManagerInternal mActivityManagerInternal;
private final InputManagerInternal mInputManagerInternal;
+ private final InputMethodManagerInternal mInputMethodManagerInternal;
private final NotifierHandler mHandler;
private final Intent mScreenOnIntent;
@@ -133,6 +135,7 @@
mPolicy = policy;
mActivityManagerInternal = LocalServices.getService(ActivityManagerInternal.class);
mInputManagerInternal = LocalServices.getService(InputManagerInternal.class);
+ mInputMethodManagerInternal = LocalServices.getService(InputMethodManagerInternal.class);
mHandler = new NotifierHandler(looper);
mScreenOnIntent = new Intent(Intent.ACTION_SCREEN_ON);
@@ -314,6 +317,7 @@
// Start input as soon as we start waking up or going to sleep.
mInputManagerInternal.setInteractive(interactive);
+ mInputMethodManagerInternal.setInteractive(interactive);
// Notify battery stats.
try {
diff --git a/services/core/jni/com_android_server_input_InputManagerService.cpp b/services/core/jni/com_android_server_input_InputManagerService.cpp
index f3edbd1..e29d0a9 100644
--- a/services/core/jni/com_android_server_input_InputManagerService.cpp
+++ b/services/core/jni/com_android_server_input_InputManagerService.cpp
@@ -27,6 +27,8 @@
#include "JNIHelp.h"
#include "jni.h"
+#include <atomic>
+#include <cinttypes>
#include <limits.h>
#include <android_runtime/AndroidRuntime.h>
#include <android_runtime/Log.h>
@@ -56,6 +58,8 @@
#include "com_android_server_input_InputApplicationHandle.h"
#include "com_android_server_input_InputWindowHandle.h"
+#define INDENT " "
+
namespace android {
// The exponent used to calculate the pointer speed scaling factor.
@@ -126,6 +130,10 @@
return a > b ? a : b;
}
+static inline const char* toString(bool value) {
+ return value ? "true" : "false";
+}
+
static jobject getInputApplicationHandleObjLocalRef(JNIEnv* env,
const sp<InputApplicationHandle>& inputApplicationHandle) {
if (inputApplicationHandle == NULL) {
@@ -262,7 +270,7 @@
wp<PointerController> pointerController;
} mLocked;
- volatile bool mInteractive;
+ std::atomic<bool> mInteractive;
void updateInactivityTimeoutLocked(const sp<PointerController>& controller);
void handleInterceptActions(jint wmActions, nsecs_t when, uint32_t& policyFlags);
@@ -292,6 +300,7 @@
mLocked.pointerGesturesEnabled = true;
mLocked.showTouches = false;
}
+ mInteractive = true;
sp<EventHub> eventHub = new EventHub();
mInputManager = new InputManager(eventHub, this, this);
@@ -305,6 +314,21 @@
}
void NativeInputManager::dump(String8& dump) {
+ dump.append("Input Manager State:\n");
+ {
+ dump.appendFormat(INDENT "Interactive: %s\n", toString(mInteractive.load()));
+ }
+ {
+ AutoMutex _l(mLock);
+ dump.appendFormat(INDENT "System UI Visibility: 0x%0" PRIx32 "\n",
+ mLocked.systemUiVisibility);
+ dump.appendFormat(INDENT "Pointer Speed: %" PRId32 "\n", mLocked.pointerSpeed);
+ dump.appendFormat(INDENT "Pointer Gestures Enabled: %s\n",
+ toString(mLocked.pointerGesturesEnabled));
+ dump.appendFormat(INDENT "Show Touches: %s\n", toString(mLocked.showTouches));
+ }
+ dump.append("\n");
+
mInputManager->getReader()->dump(dump);
dump.append("\n");
@@ -830,7 +854,8 @@
// - Ignore untrusted events and pass them along.
// - Ask the window manager what to do with normal events and trusted injected events.
// - For normal events wake and brighten the screen if currently off or dim.
- if (mInteractive) {
+ bool interactive = mInteractive.load();
+ if (interactive) {
policyFlags |= POLICY_FLAG_INTERACTIVE;
}
if ((policyFlags & POLICY_FLAG_TRUSTED)) {
@@ -854,7 +879,7 @@
handleInterceptActions(wmActions, when, /*byref*/ policyFlags);
} else {
- if (mInteractive) {
+ if (interactive) {
policyFlags |= POLICY_FLAG_PASS_TO_USER;
}
}
@@ -866,7 +891,8 @@
// - No special filtering for injected events required at this time.
// - Filter normal events based on screen state.
// - For normal events brighten (but do not wake) the screen if currently dim.
- if (mInteractive) {
+ bool interactive = mInteractive.load();
+ if (interactive) {
policyFlags |= POLICY_FLAG_INTERACTIVE;
}
if ((policyFlags & POLICY_FLAG_TRUSTED) && !(policyFlags & POLICY_FLAG_INJECTED)) {
@@ -885,7 +911,7 @@
handleInterceptActions(wmActions, when, /*byref*/ policyFlags);
}
} else {
- if (mInteractive) {
+ if (interactive) {
policyFlags |= POLICY_FLAG_PASS_TO_USER;
}
}
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 3ae10f2..48ca7d8 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/Bridge.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/Bridge.java
@@ -50,6 +50,7 @@
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.Arrays;
+import java.util.Comparator;
import java.util.EnumMap;
import java.util.EnumSet;
import java.util.HashMap;
@@ -93,7 +94,7 @@
/**
* Same as sRMap except for int[] instead of int resources. This is for android.R only.
*/
- private final static Map<IntArray, String> sRArrayMap = new HashMap<IntArray, String>();
+ private final static Map<IntArray, String> sRArrayMap = new HashMap<IntArray, String>(384);
/**
* Reverse map compared to sRMap, resource type -> (resource name -> id).
* This is for com.android.internal.R.
@@ -182,7 +183,7 @@
*/
private static LayoutLog sCurrentLog = sDefaultLog;
- private static final int LAST_SUPPORTED_FEATURE = Features.RENDER_ALL_DRAWABLE_STATES;
+ private static final int LAST_SUPPORTED_FEATURE = Features.RECYCLER_VIEW_ADAPTER;
@Override
public int getApiLevel() {
@@ -249,37 +250,56 @@
// the internal version), and put the content in the maps.
try {
Class<?> r = com.android.internal.R.class;
+ // Parse the styleable class first, since it may contribute to attr values.
+ parseStyleable();
for (Class<?> inner : r.getDeclaredClasses()) {
+ if (inner == com.android.internal.R.styleable.class) {
+ // Already handled the styleable case. Not skipping attr, as there may be attrs
+ // that are not referenced from styleables.
+ continue;
+ }
String resTypeName = inner.getSimpleName();
ResourceType resType = ResourceType.getEnum(resTypeName);
if (resType != null) {
- Map<String, Integer> fullMap = new HashMap<String, Integer>();
- sRevRMap.put(resType, fullMap);
+ Map<String, Integer> fullMap = null;
+ switch (resType) {
+ case ATTR:
+ fullMap = sRevRMap.get(ResourceType.ATTR);
+ break;
+ case STRING:
+ case STYLE:
+ // Slightly less than thousand entries in each.
+ fullMap = new HashMap<String, Integer>(1280);
+ // no break.
+ default:
+ if (fullMap == null) {
+ fullMap = new HashMap<String, Integer>();
+ }
+ sRevRMap.put(resType, fullMap);
+ }
for (Field f : inner.getDeclaredFields()) {
// only process static final fields. Since the final attribute may have
// been altered by layoutlib_create, we only check static
- int modifiers = f.getModifiers();
- if (Modifier.isStatic(modifiers)) {
- Class<?> type = f.getType();
- if (type.isArray() && type.getComponentType() == int.class) {
- // if the object is an int[] we put it in sRArrayMap using an IntArray
- // wrapper that properly implements equals and hashcode for the array
- // objects, as required by the map contract.
- sRArrayMap.put(new IntArray((int[]) f.get(null)), f.getName());
- } else if (type == int.class) {
- Integer value = (Integer) f.get(null);
- sRMap.put(value, Pair.of(resType, f.getName()));
- fullMap.put(f.getName(), value);
- } else {
- assert false;
- }
+ if (!isValidRField(f)) {
+ continue;
+ }
+ Class<?> type = f.getType();
+ if (type.isArray()) {
+ // if the object is an int[] we put it in sRArrayMap using an IntArray
+ // wrapper that properly implements equals and hashcode for the array
+ // objects, as required by the map contract.
+ sRArrayMap.put(new IntArray((int[]) f.get(null)), f.getName());
+ } else {
+ Integer value = (Integer) f.get(null);
+ sRMap.put(value, Pair.of(resType, f.getName()));
+ fullMap.put(f.getName(), value);
}
}
}
}
- } catch (Throwable throwable) {
+ } catch (Exception throwable) {
if (log != null) {
log.error(LayoutLog.TAG_BROKEN,
"Failed to load com.android.internal.R from the layout library jar",
@@ -291,6 +311,90 @@
return true;
}
+ /**
+ * Tests if the field is pubic, static and one of int or int[].
+ */
+ private static boolean isValidRField(Field field) {
+ int modifiers = field.getModifiers();
+ boolean isAcceptable = Modifier.isPublic(modifiers) && Modifier.isStatic(modifiers);
+ Class<?> type = field.getType();
+ return isAcceptable && type == int.class ||
+ (type.isArray() && type.getComponentType() == int.class);
+
+ }
+
+ private static void parseStyleable() throws Exception {
+ // R.attr doesn't contain all the needed values. There are too many resources in the
+ // framework for all to be in the R class. Only the ones specified manually in
+ // res/values/symbols.xml are put in R class. Since, we need to create a map of all attr
+ // values, we try and find them from the styleables.
+
+ // There were 1500 elements in this map at M timeframe.
+ Map<String, Integer> revRAttrMap = new HashMap<String, Integer>(2048);
+ sRevRMap.put(ResourceType.ATTR, revRAttrMap);
+ // There were 2000 elements in this map at M timeframe.
+ Map<String, Integer> revRStyleableMap = new HashMap<String, Integer>(3072);
+ sRevRMap.put(ResourceType.STYLEABLE, revRStyleableMap);
+ Class<?> c = com.android.internal.R.styleable.class;
+ Field[] fields = c.getDeclaredFields();
+ // Sort the fields to bring all arrays to the beginning, so that indices into the array are
+ // able to refer back to the arrays (i.e. no forward references).
+ Arrays.sort(fields, new Comparator<Field>() {
+ @Override
+ public int compare(Field o1, Field o2) {
+ if (o1 == o2) {
+ return 0;
+ }
+ Class<?> t1 = o1.getType();
+ Class<?> t2 = o2.getType();
+ if (t1.isArray() && !t2.isArray()) {
+ return -1;
+ } else if (t2.isArray() && !t1.isArray()) {
+ return 1;
+ }
+ return o1.getName().compareTo(o2.getName());
+ }
+ });
+ Map<String, int[]> styleables = new HashMap<String, int[]>();
+ for (Field field : fields) {
+ if (!isValidRField(field)) {
+ // Only consider public static fields that are int or int[].
+ // Don't check the final flag as it may have been modified by layoutlib_create.
+ continue;
+ }
+ String name = field.getName();
+ if (field.getType().isArray()) {
+ int[] styleableValue = (int[]) field.get(null);
+ sRArrayMap.put(new IntArray(styleableValue), name);
+ styleables.put(name, styleableValue);
+ continue;
+ }
+ // Not an array.
+ String arrayName = name;
+ int[] arrayValue = null;
+ int index;
+ while ((index = arrayName.lastIndexOf('_')) >= 0) {
+ // Find the name of the corresponding styleable.
+ // Search in reverse order so that attrs like LinearLayout_Layout_layout_gravity
+ // are mapped to LinearLayout_Layout and not to LinearLayout.
+ arrayName = arrayName.substring(0, index);
+ arrayValue = styleables.get(arrayName);
+ if (arrayValue != null) {
+ break;
+ }
+ }
+ index = (Integer) field.get(null);
+ if (arrayValue != null) {
+ String attrName = name.substring(arrayName.length() + 1);
+ int attrValue = arrayValue[index];
+ sRMap.put(attrValue, Pair.of(ResourceType.ATTR, attrName));
+ revRAttrMap.put(attrName, attrValue);
+ }
+ sRMap.put(index, Pair.of(ResourceType.STYLEABLE, name));
+ revRStyleableMap.put(name, index);
+ }
+ }
+
@Override
public boolean dispose() {
BridgeAssetManager.clearSystem();
@@ -490,9 +594,13 @@
/**
* Returns the integer id of a framework resource, from a given resource type and resource name.
+ * <p/>
+ * If no resource is found, it creates a dynamic id for the resource.
+ *
* @param type the type of the resource
* @param name the name of the resource.
- * @return an {@link Integer} containing the resource id, or null if no resource were found.
+ *
+ * @return an {@link Integer} containing the resource id.
*/
@NonNull
public static Integer getResourceId(ResourceType type, String name) {
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/AppCompatActionBar.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/AppCompatActionBar.java
index 958b7aa..868c6d3 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/AppCompatActionBar.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/AppCompatActionBar.java
@@ -31,7 +31,6 @@
import android.view.ContextThemeWrapper;
import android.view.LayoutInflater;
import android.view.View;
-import android.view.ViewGroup;
import android.widget.FrameLayout;
import java.lang.reflect.InvocationTargetException;
@@ -51,9 +50,8 @@
/**
* Inflate the action bar and attach it to {@code parentView}
*/
- public AppCompatActionBar(@NonNull BridgeContext context, @NonNull SessionParams params,
- @NonNull ViewGroup parentView) {
- super(context, params, parentView);
+ public AppCompatActionBar(@NonNull BridgeContext context, @NonNull SessionParams params) {
+ super(context, params);
int contentRootId = context.getProjectResourceValue(ResourceType.ID,
"action_bar_activity_content", 0);
View contentView = getDecorContent().findViewById(contentRootId);
@@ -64,7 +62,9 @@
// Something went wrong. Create a new FrameLayout in the enclosing layout.
FrameLayout contentRoot = new FrameLayout(context);
setMatchParent(contentRoot);
- mEnclosingLayout.addView(contentRoot);
+ if (mEnclosingLayout != null) {
+ mEnclosingLayout.addView(contentRoot);
+ }
setContentRoot(contentRoot);
}
try {
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/BridgeActionBar.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/BridgeActionBar.java
index a19b689f1..f900b45 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/BridgeActionBar.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/BridgeActionBar.java
@@ -24,6 +24,7 @@
import com.android.layoutlib.bridge.android.BridgeContext;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
@@ -39,7 +40,7 @@
@NonNull protected final BridgeContext mBridgeContext;
@NonNull protected final SessionParams mParams;
// A Layout that contains the inflated action bar. The menu popup is added to this layout.
- @NonNull protected final ViewGroup mEnclosingLayout;
+ @Nullable protected final ViewGroup mEnclosingLayout;
private final View mDecorContent;
private final ActionBarCallback mCallback;
@@ -47,8 +48,7 @@
@SuppressWarnings("NullableProblems") // Should be initialized by subclasses.
@NonNull private FrameLayout mContentRoot;
- public BridgeActionBar(@NonNull BridgeContext context, @NonNull SessionParams params,
- @NonNull ViewGroup parentView) {
+ public BridgeActionBar(@NonNull BridgeContext context, @NonNull SessionParams params) {
mBridgeContext = context;
mParams = params;
mCallback = params.getLayoutlibCallback().getActionBarCallback();
@@ -75,14 +75,13 @@
// added.
mEnclosingLayout = new RelativeLayout(mBridgeContext);
setMatchParent(mEnclosingLayout);
- parentView.addView(mEnclosingLayout);
} else {
- mEnclosingLayout = parentView;
+ mEnclosingLayout = null;
}
// Inflate action bar layout.
- mDecorContent = getInflater(context).inflate(layoutId, mEnclosingLayout, true);
-
+ mDecorContent =
+ getInflater(context).inflate(layoutId, mEnclosingLayout, mEnclosingLayout != null);
}
/**
@@ -153,6 +152,13 @@
public abstract void createMenuPopup();
+ /**
+ * The root view that represents the action bar and possibly the content included in it.
+ */
+ public View getRootView() {
+ return mEnclosingLayout == null ? mDecorContent : mEnclosingLayout;
+ }
+
public ActionBarCallback getCallBack() {
return mCallback;
}
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/CustomBar.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/CustomBar.java
index 90a1c75..145a03a 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/CustomBar.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/CustomBar.java
@@ -32,7 +32,6 @@
import org.xmlpull.v1.XmlPullParserException;
import android.annotation.NonNull;
-import android.content.Context;
import android.content.res.ColorStateList;
import android.graphics.Bitmap;
import android.graphics.Bitmap_Delegate;
@@ -68,7 +67,7 @@
protected abstract TextView getStyleableTextView();
protected CustomBar(BridgeContext context, int orientation, String layoutPath,
- String name, int simulatedPlatformVersion) throws XmlPullParserException {
+ String name, int simulatedPlatformVersion) {
super(context);
mSimulatedPlatformVersion = simulatedPlatformVersion;
setOrientation(orientation);
@@ -78,14 +77,18 @@
setGravity(Gravity.CENTER_HORIZONTAL);
}
- LayoutInflater inflater = (LayoutInflater) getContext().getSystemService(
- Context.LAYOUT_INFLATER_SERVICE);
+ LayoutInflater inflater = LayoutInflater.from(mContext);
- XmlPullParser parser = ParserFactory.create(getClass().getResourceAsStream(layoutPath),
- name);
+ XmlPullParser parser;
+ try {
+ parser = ParserFactory.create(getClass().getResourceAsStream(layoutPath), name);
+ } catch (XmlPullParserException e) {
+ // Should not happen as the resource is bundled with the jar, and ParserFactory should
+ // have been initialized.
+ throw new AssertionError(e);
+ }
- BridgeXmlBlockParser bridgeParser = new BridgeXmlBlockParser(
- parser, (BridgeContext) context, false /*platformFile*/);
+ BridgeXmlBlockParser bridgeParser = new BridgeXmlBlockParser(parser, context, false);
try {
inflater.inflate(bridgeParser, this, true);
@@ -154,7 +157,7 @@
protected void setStyle(String themeEntryName) {
- BridgeContext bridgeContext = (BridgeContext) mContext;
+ BridgeContext bridgeContext = getContext();
RenderResources res = bridgeContext.getRenderResources();
ResourceValue value = res.findItemInTheme(themeEntryName, true /*isFrameworkAttr*/);
@@ -214,27 +217,47 @@
}
}
+ @Override
+ public BridgeContext getContext() {
+ return (BridgeContext) mContext;
+ }
+
/**
- * Given a theme attribute name, get the color referenced by it. The theme attribute may be
- * used in a layout like "?attr/foo".
+ * Find the background color for this bar from the theme attributes. Only relevant to StatusBar
+ * and NavigationBar.
* <p/>
* Returns 0 if not found.
*
+ * @param colorAttrName the attribute name for the background color
+ * @param translucentAttrName the attribute name for the translucency property of the bar.
+ *
* @throws NumberFormatException if color resolved to an invalid string.
*/
- protected int getThemeAttrColor(@NonNull String attrName, boolean isFramework) {
+ protected int getBarColor(@NonNull String colorAttrName, @NonNull String translucentAttrName) {
if (!Config.isGreaterOrEqual(mSimulatedPlatformVersion, LOLLIPOP)) {
return 0;
}
- assert mContext instanceof BridgeContext;
- BridgeContext context = ((BridgeContext) mContext);
- RenderResources renderResources = context.getRenderResources();
- // From ?attr/foo to @color/bar. This is most likely an ItemResourceValue.
- ResourceValue resource = renderResources.findItemInTheme(attrName, isFramework);
- if (resource != null) {
- // Form @color/bar to the #AARRGGBB
- resource = renderResources.resolveResValue(resource);
+ RenderResources renderResources = getContext().getRenderResources();
+ // First check if the bar is translucent.
+ boolean translucent = ResourceHelper.getBooleanThemeValue(renderResources,
+ translucentAttrName, true, false);
+ if (translucent) {
+ // Keep in sync with R.color.system_bar_background_semi_transparent from system ui.
+ return 0x66000000; // 40% black.
}
+ boolean transparent = ResourceHelper.getBooleanThemeValue(renderResources,
+ "windowDrawsSystemBarBackgrounds", true, false);
+ if (transparent) {
+ return getColor(renderResources, colorAttrName);
+ }
+ return 0;
+ }
+
+ private static int getColor(RenderResources renderResources, String attr) {
+ // From ?attr/foo to @color/bar. This is most likely an ItemResourceValue.
+ ResourceValue resource = renderResources.findItemInTheme(attr, true);
+ // Form @color/bar to the #AARRGGBB
+ resource = renderResources.resolveResValue(resource);
if (resource != null && ResourceType.COLOR.equals(resource.getResourceType())) {
return ResourceHelper.getColor(resource.getValue());
}
@@ -242,8 +265,7 @@
}
private ResourceValue getResourceValue(String reference) {
- BridgeContext bridgeContext = (BridgeContext) mContext;
- RenderResources res = bridgeContext.getRenderResources();
+ RenderResources res = getContext().getRenderResources();
// find the resource
ResourceValue value = res.findResValue(reference, false);
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/FrameworkActionBar.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/FrameworkActionBar.java
index bb3d13f..d2a5117 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/FrameworkActionBar.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/FrameworkActionBar.java
@@ -60,23 +60,24 @@
/**
* Inflate the action bar and attach it to {@code parentView}
*/
- public FrameworkActionBar(@NonNull BridgeContext context, @NonNull SessionParams params,
- @NonNull ViewGroup parentView) {
- super(context, params, parentView);
+ public FrameworkActionBar(@NonNull BridgeContext context, @NonNull SessionParams params) {
+ super(context, params);
View decorContent = getDecorContent();
mActionBar = FrameworkActionBarWrapper.getActionBarWrapper(context, getCallBack(),
decorContent);
- FrameLayout contentRoot = (FrameLayout) mEnclosingLayout.findViewById(android.R.id.content);
+ FrameLayout contentRoot = (FrameLayout) decorContent.findViewById(android.R.id.content);
// If something went wrong and we were not able to initialize the content root,
// just add a frame layout inside this and return.
if (contentRoot == null) {
contentRoot = new FrameLayout(context);
setMatchParent(contentRoot);
- mEnclosingLayout.addView(contentRoot);
+ if (mEnclosingLayout != null) {
+ mEnclosingLayout.addView(contentRoot);
+ }
setContentRoot(contentRoot);
} else {
setContentRoot(contentRoot);
@@ -162,6 +163,7 @@
listView.setDivider(a.getDrawable(R.attr.actionBarDivider));
a.recycle();
listView.setElevation(mActionBar.getMenuPopupElevation());
+ assert mEnclosingLayout != null : "Unable to find view to attach ActionMenuPopup.";
mEnclosingLayout.addView(listView);
}
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/FrameworkActionBarWrapper.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/FrameworkActionBarWrapper.java
index daad602..af6ba24 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/FrameworkActionBarWrapper.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/FrameworkActionBarWrapper.java
@@ -79,7 +79,7 @@
}
}
- FrameworkActionBarWrapper(@NonNull BridgeContext context, ActionBarCallback callback,
+ FrameworkActionBarWrapper(@NonNull BridgeContext context, @NonNull ActionBarCallback callback,
@NonNull ActionBar actionBar) {
mActionBar = actionBar;
mCallback = callback;
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/NavigationBar.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/NavigationBar.java
index dcf82a3..9c89bfe2 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/NavigationBar.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/NavigationBar.java
@@ -19,8 +19,6 @@
import com.android.layoutlib.bridge.android.BridgeContext;
import com.android.resources.Density;
-import org.xmlpull.v1.XmlPullParserException;
-
import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.util.AttributeSet;
@@ -33,6 +31,8 @@
/** Navigation bar background color attribute name. */
private static final String ATTR_COLOR = "navigationBarColor";
+ /** Attribute for translucency property. */
+ public static final String ATTR_TRANSLUCENT = "windowTranslucentNavigation";
// These correspond to @dimen/navigation_side_padding in the system ui code.
private static final int PADDING_WIDTH_DEFAULT = 36;
private static final int PADDING_WIDTH_SW360 = 40;
@@ -49,8 +49,8 @@
* Constructor to be used when creating the {@link NavigationBar} as a regular control.
* This is currently used by the theme editor.
*/
- public NavigationBar(Context context, AttributeSet attrs)
- throws XmlPullParserException {
+ @SuppressWarnings("unused")
+ public NavigationBar(Context context, AttributeSet attrs) {
this((BridgeContext) context,
Density.getEnum(((BridgeContext) context).getMetrics().densityDpi),
LinearLayout.HORIZONTAL, // In this mode, it doesn't need to be render vertically
@@ -61,11 +61,11 @@
}
public NavigationBar(BridgeContext context, Density density, int orientation, boolean isRtl,
- boolean rtlEnabled, int simulatedPlatformVersion) throws XmlPullParserException {
+ boolean rtlEnabled, int simulatedPlatformVersion) {
super(context, orientation, getShortestWidth(context)>= 600 ? LAYOUT_600DP_XML : LAYOUT_XML,
"navigation_bar.xml", simulatedPlatformVersion);
- int color = getThemeAttrColor(ATTR_COLOR, true);
+ int color = getBarColor(ATTR_COLOR, ATTR_TRANSLUCENT);
setBackgroundColor(color == 0 ? 0xFF000000 : color);
// Cannot access the inside items through id because no R.id values have been
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/StatusBar.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/StatusBar.java
index a0ed0e8..2dc7c65 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/StatusBar.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/StatusBar.java
@@ -26,6 +26,7 @@
import org.xmlpull.v1.XmlPullParserException;
import android.content.Context;
+import android.content.pm.ApplicationInfo;
import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
import android.view.Gravity;
@@ -42,22 +43,26 @@
private final int mSimulatedPlatformVersion;
/** Status bar background color attribute name. */
private static final String ATTR_COLOR = "statusBarColor";
+ /** Attribute for translucency property. */
+ public static final String ATTR_TRANSLUCENT = "windowTranslucentStatus";
/**
* Constructor to be used when creating the {@link StatusBar} as a regular control. This
* is currently used by the theme editor.
*/
- public StatusBar(Context context, AttributeSet attrs) throws XmlPullParserException {
+ @SuppressWarnings("UnusedParameters")
+ public StatusBar(Context context, AttributeSet attrs) {
this((BridgeContext) context,
Density.getEnum(((BridgeContext) context).getMetrics().densityDpi),
- LinearLayout.HORIZONTAL, // In this mode, it doesn't need to be render vertically
((BridgeContext) context).getConfiguration().getLayoutDirection() ==
View.LAYOUT_DIRECTION_RTL,
+ (context.getApplicationInfo().flags & ApplicationInfo.FLAG_SUPPORTS_RTL) != 0,
context.getApplicationInfo().targetSdkVersion);
}
- public StatusBar(BridgeContext context, Density density, int direction, boolean RtlEnabled,
- int simulatedPlatformVersion) throws XmlPullParserException {
+ @SuppressWarnings("UnusedParameters")
+ public StatusBar(BridgeContext context, Density density, boolean isRtl, boolean rtlEnabled,
+ int simulatedPlatformVersion) {
// FIXME: if direction is RTL but it's not enabled in application manifest, mirror this bar.
super(context, LinearLayout.HORIZONTAL, "/bars/status_bar.xml", "status_bar.xml",
simulatedPlatformVersion);
@@ -66,7 +71,7 @@
// FIXME: use FILL_H?
setGravity(Gravity.START | Gravity.TOP | Gravity.RIGHT);
- int color = getThemeAttrColor(ATTR_COLOR, true);
+ int color = getBarColor(ATTR_COLOR, ATTR_TRANSLUCENT);
setBackgroundColor(color == 0 ? Config.getStatusBarColor(simulatedPlatformVersion) : color);
// Cannot access the inside items through id because no R.id values have been
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/TitleBar.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/TitleBar.java
index c610601..4fe1001 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/TitleBar.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/TitleBar.java
@@ -27,8 +27,7 @@
private TextView mTextView;
- public TitleBar(BridgeContext context, String label, int simulatedPlatformVersion)
- throws XmlPullParserException {
+ public TitleBar(BridgeContext context, String label, int simulatedPlatformVersion) {
super(context, LinearLayout.HORIZONTAL, "/bars/title_bar.xml", "title_bar.xml",
simulatedPlatformVersion);
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/Layout.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/Layout.java
new file mode 100644
index 0000000..89d8319
--- /dev/null
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/Layout.java
@@ -0,0 +1,459 @@
+/*
+ * 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.impl;
+
+import com.android.ide.common.rendering.api.HardwareConfig;
+import com.android.ide.common.rendering.api.RenderResources;
+import com.android.ide.common.rendering.api.ResourceValue;
+import com.android.ide.common.rendering.api.SessionParams;
+import com.android.ide.common.rendering.api.StyleResourceValue;
+import com.android.layoutlib.bridge.Bridge;
+import com.android.layoutlib.bridge.android.BridgeContext;
+import com.android.layoutlib.bridge.bars.AppCompatActionBar;
+import com.android.layoutlib.bridge.bars.BridgeActionBar;
+import com.android.layoutlib.bridge.bars.Config;
+import com.android.layoutlib.bridge.bars.FrameworkActionBar;
+import com.android.layoutlib.bridge.bars.NavigationBar;
+import com.android.layoutlib.bridge.bars.StatusBar;
+import com.android.layoutlib.bridge.bars.TitleBar;
+import com.android.resources.Density;
+import com.android.resources.ResourceType;
+import com.android.resources.ScreenOrientation;
+
+import android.annotation.NonNull;
+import android.graphics.drawable.Drawable;
+import android.util.DisplayMetrics;
+import android.util.TypedValue;
+import android.view.View;
+import android.widget.FrameLayout;
+import android.widget.LinearLayout;
+import android.widget.RelativeLayout;
+
+import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
+import static android.widget.LinearLayout.VERTICAL;
+
+/**
+ * The Layout used to create the system decor.
+ *
+ * The layout inflated will contain a content frame where the user's layout can be inflated.
+ * <pre>
+ * +-------------------------------------------------+---+
+ * | Status bar | N |
+ * +-------------------------------------------------+ a |
+ * | Title/Action bar (optional) | v |
+ * +-------------------------------------------------+ |
+ * | Content, vertical extending | b |
+ * | | a |
+ * | | r |
+ * +-------------------------------------------------+---+
+ * </pre>
+ * or
+ * <pre>
+ * +-------------------------------------+
+ * | Status bar |
+ * +-------------------------------------+
+ * | Title/Action bar (optional) |
+ * +-------------------------------------+
+ * | Content, vertical extending |
+ * | |
+ * | |
+ * +-------------------------------------+
+ * | Nav bar |
+ * +-------------------------------------+
+ * </pre>
+ *
+ */
+class Layout extends RelativeLayout {
+
+ // Theme attributes used for configuring appearance of the system decor.
+ private static final String ATTR_WINDOW_FLOATING = "windowIsFloating";
+ private static final String ATTR_WINDOW_BACKGROUND = "windowBackground";
+ private static final String ATTR_WINDOW_FULL_SCREEN = "windowFullScreen";
+ private static final String ATTR_NAV_BAR_HEIGHT = "navigation_bar_height";
+ private static final String ATTR_NAV_BAR_WIDTH = "navigation_bar_width";
+ private static final String ATTR_STATUS_BAR_HEIGHT = "status_bar_height";
+ private static final String ATTR_WINDOW_ACTION_BAR = "windowActionBar";
+ private static final String ATTR_ACTION_BAR_SIZE = "actionBarSize";
+ private static final String ATTR_WINDOW_NO_TITLE = "windowNoTitle";
+ private static final String ATTR_WINDOW_TITLE_SIZE = "windowTitleSize";
+ private static final String ATTR_WINDOW_TRANSLUCENT_STATUS = StatusBar.ATTR_TRANSLUCENT;
+ private static final String ATTR_WINDOW_TRANSLUCENT_NAV = NavigationBar.ATTR_TRANSLUCENT;
+ private static final String PREFIX_THEME_APPCOMPAT = "Theme.AppCompat";
+
+ // Default sizes
+ private static final int DEFAULT_STATUS_BAR_HEIGHT = 25;
+ private static final int DEFAULT_TITLE_BAR_HEIGHT = 25;
+ private static final int DEFAULT_NAV_BAR_SIZE = 48;
+
+ // Ids assigned to components created. This is so that we can refer to other components in
+ // layout params.
+ private static final String ID_NAV_BAR = "navBar";
+ private static final String ID_STATUS_BAR = "statusBar";
+ private static final String ID_TITLE_BAR = "titleBar";
+ // Prefix used with the above ids in order to make them unique in framework namespace.
+ private static final String ID_PREFIX = "android_layoutlib_";
+
+ /**
+ * Temporarily store the builder so that it doesn't have to be passed to all methods used
+ * during inflation.
+ */
+ private Builder mBuilder;
+
+ /**
+ * This holds user's layout.
+ */
+ private FrameLayout mContentRoot;
+
+ public Layout(@NonNull Builder builder) {
+ super(builder.mContext);
+ mBuilder = builder;
+ if (builder.mWindowBackground != null) {
+ Drawable d = ResourceHelper.getDrawable(builder.mWindowBackground, builder.mContext);
+ setBackground(d);
+ }
+
+ int simulatedPlatformVersion = getParams().getSimulatedPlatformVersion();
+ HardwareConfig hwConfig = getParams().getHardwareConfig();
+ Density density = hwConfig.getDensity();
+ boolean isRtl = Bridge.isLocaleRtl(getParams().getLocale());
+
+ NavigationBar navBar = null;
+ if (mBuilder.hasNavBar()) {
+ navBar = createNavBar(getContext(), density, isRtl, getParams().isRtlSupported(),
+ simulatedPlatformVersion);
+ }
+
+ StatusBar statusBar = null;
+ if (builder.mStatusBarSize > 0) {
+ statusBar = createStatusBar(getContext(), density, isRtl, getParams().isRtlSupported(),
+ simulatedPlatformVersion);
+ }
+
+ View actionBar = null;
+ TitleBar titleBar = null;
+ if (builder.mActionBarSize > 0) {
+ BridgeActionBar bar = createActionBar(getContext(), getParams());
+ mContentRoot = bar.getContentRoot();
+ actionBar = bar.getRootView();
+ } else if (mBuilder.mTitleBarSize > 0) {
+ titleBar = createTitleBar(getContext(), getParams().getAppLabel(),
+ simulatedPlatformVersion);
+ }
+
+ addViews(titleBar, mContentRoot == null ? (mContentRoot = createContentFrame()) : actionBar,
+ statusBar, navBar);
+ // Done with the builder. Don't hold a reference to it.
+ mBuilder = null;
+ }
+
+ @NonNull
+ private FrameLayout createContentFrame() {
+ FrameLayout contentRoot = new FrameLayout(getContext());
+ LayoutParams params = createLayoutParams(MATCH_PARENT, MATCH_PARENT);
+ int rule = mBuilder.isNavBarVertical() ? START_OF : ABOVE;
+ if (mBuilder.solidBars()) {
+ params.addRule(rule, getId(ID_NAV_BAR));
+ }
+ int below = -1;
+ if (mBuilder.mActionBarSize <= 0 && mBuilder.mTitleBarSize > 0) {
+ below = getId(ID_TITLE_BAR);
+ } else if (mBuilder.solidBars()) {
+ below = getId(ID_STATUS_BAR);
+ }
+ if (below != -1) {
+ params.addRule(BELOW, below);
+ }
+ contentRoot.setLayoutParams(params);
+ return contentRoot;
+ }
+
+ @NonNull
+ private LayoutParams createLayoutParams(int width, int height) {
+ DisplayMetrics metrics = getContext().getResources().getDisplayMetrics();
+ if (width > 0) {
+ width = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, width, metrics);
+ }
+ if (height > 0) {
+ height = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, height, metrics);
+ }
+ return new LayoutParams(width, height);
+ }
+
+ @NonNull
+ public FrameLayout getContentRoot() {
+ return mContentRoot;
+ }
+
+ @NonNull
+ private SessionParams getParams() {
+ return mBuilder.mParams;
+ }
+
+ @NonNull
+ @Override
+ public BridgeContext getContext(){
+ return (BridgeContext) super.getContext();
+ }
+
+ /**
+ * @param isRtl whether the current locale is an RTL locale.
+ * @param isRtlSupported whether the applications supports RTL (i.e. has supportsRtl=true
+ * in the manifest and targetSdkVersion >= 17.
+ */
+ @NonNull
+ private StatusBar createStatusBar(BridgeContext context, Density density, boolean isRtl,
+ boolean isRtlSupported, int simulatedPlatformVersion) {
+ StatusBar statusBar =
+ new StatusBar(context, density, isRtl, isRtlSupported, simulatedPlatformVersion);
+ LayoutParams params = createLayoutParams(MATCH_PARENT, mBuilder.mStatusBarSize);
+ if (mBuilder.isNavBarVertical()) {
+ params.addRule(START_OF, getId(ID_NAV_BAR));
+ }
+ statusBar.setLayoutParams(params);
+ statusBar.setId(getId(ID_STATUS_BAR));
+ return statusBar;
+ }
+
+ private BridgeActionBar createActionBar(@NonNull BridgeContext context,
+ @NonNull SessionParams params) {
+ BridgeActionBar actionBar;
+ if (mBuilder.isThemeAppCompat()) {
+ actionBar = new AppCompatActionBar(context, params);
+ } else {
+ actionBar = new FrameworkActionBar(context, params);
+ }
+ LayoutParams layoutParams = createLayoutParams(MATCH_PARENT, MATCH_PARENT);
+ int rule = mBuilder.isNavBarVertical() ? START_OF : ABOVE;
+ if (mBuilder.solidBars()) {
+ layoutParams.addRule(rule, getId(ID_NAV_BAR));
+ }
+ if (mBuilder.solidBars()) {
+ layoutParams.addRule(BELOW, getId(ID_STATUS_BAR));
+ }
+ actionBar.getRootView().setLayoutParams(layoutParams);
+ actionBar.createMenuPopup();
+ return actionBar;
+ }
+
+ @NonNull
+ private TitleBar createTitleBar(BridgeContext context, String title,
+ int simulatedPlatformVersion) {
+ TitleBar titleBar = new TitleBar(context, title, simulatedPlatformVersion);
+ LayoutParams params = createLayoutParams(MATCH_PARENT, mBuilder.mTitleBarSize);
+ if (mBuilder.solidBars()) {
+ params.addRule(BELOW, getId(ID_STATUS_BAR));
+ }
+ if (mBuilder.isNavBarVertical() && mBuilder.solidBars()) {
+ params.addRule(START_OF, getId(ID_NAV_BAR));
+ }
+ titleBar.setLayoutParams(params);
+ titleBar.setId(getId(ID_TITLE_BAR));
+ return titleBar;
+ }
+
+ /**
+ * @param isRtl whether the current locale is an RTL locale.
+ * @param isRtlSupported whether the applications supports RTL (i.e. has supportsRtl=true
+ * in the manifest and targetSdkVersion >= 17.
+ */
+ @NonNull
+ private NavigationBar createNavBar(BridgeContext context, Density density, boolean isRtl,
+ boolean isRtlSupported, int simulatedPlatformVersion) {
+ int orientation = mBuilder.mNavBarOrientation;
+ int size = mBuilder.mNavBarSize;
+ NavigationBar navBar = new NavigationBar(context, density, orientation, isRtl,
+ isRtlSupported, simulatedPlatformVersion);
+ boolean isVertical = mBuilder.isNavBarVertical();
+ int w = isVertical ? size : MATCH_PARENT;
+ int h = isVertical ? MATCH_PARENT : size;
+ LayoutParams params = createLayoutParams(w, h);
+ params.addRule(isVertical ? ALIGN_PARENT_END : ALIGN_PARENT_BOTTOM);
+ navBar.setLayoutParams(params);
+ navBar.setId(getId(ID_NAV_BAR));
+ return navBar;
+ }
+
+ private void addViews(@NonNull View... views) {
+ for (View view : views) {
+ if (view != null) {
+ addView(view);
+ }
+ }
+ }
+
+ private int getId(String name) {
+ return Bridge.getResourceId(ResourceType.ID, ID_PREFIX + name);
+ }
+
+ /**
+ * A helper class to help initialize the Layout.
+ */
+ static class Builder {
+ @NonNull
+ private final SessionParams mParams;
+ @NonNull
+ private final BridgeContext mContext;
+ private final RenderResources mResources;
+
+ private final boolean mWindowIsFloating;
+ private ResourceValue mWindowBackground;
+ private int mStatusBarSize;
+ private int mNavBarSize;
+ private int mNavBarOrientation;
+ private int mActionBarSize;
+ private int mTitleBarSize;
+ private boolean mTranslucentStatus;
+ private boolean mTranslucentNav;
+
+ private Boolean mIsThemeAppCompat;
+
+ public Builder(@NonNull SessionParams params, @NonNull BridgeContext context) {
+ mParams = params;
+ mContext = context;
+ mResources = mParams.getResources();
+ mWindowIsFloating = ResourceHelper.getBooleanThemeValue(mResources, ATTR_WINDOW_FLOATING, true, true);
+
+ findBackground();
+ findStatusBar();
+ findActionBar();
+ findNavBar();
+ }
+
+ public boolean isNavBarVertical() {
+ return mNavBarOrientation == VERTICAL;
+ }
+
+ private void findBackground() {
+ if (!mParams.isBgColorOverridden()) {
+ mWindowBackground = mResources.findItemInTheme(ATTR_WINDOW_BACKGROUND, true);
+ mWindowBackground = mResources.resolveResValue(mWindowBackground);
+ }
+ }
+
+ private void findStatusBar() {
+ boolean windowFullScreen =
+ ResourceHelper.getBooleanThemeValue(mResources, ATTR_WINDOW_FULL_SCREEN, true, false);
+ if (!windowFullScreen && !mWindowIsFloating) {
+ mStatusBarSize =
+ getDimension(ATTR_STATUS_BAR_HEIGHT, true, DEFAULT_STATUS_BAR_HEIGHT);
+ mTranslucentStatus = ResourceHelper.getBooleanThemeValue(mResources,
+ ATTR_WINDOW_TRANSLUCENT_STATUS, true, false);
+ }
+ }
+
+ private void findActionBar() {
+ if (mWindowIsFloating) {
+ return;
+ }
+ // Check if an actionbar is needed
+ boolean windowActionBar = ResourceHelper.getBooleanThemeValue(mResources, ATTR_WINDOW_ACTION_BAR,
+ !isThemeAppCompat(), true);
+ if (windowActionBar) {
+ mActionBarSize = getDimension(ATTR_ACTION_BAR_SIZE, true, DEFAULT_TITLE_BAR_HEIGHT);
+ } else {
+ // Maybe the gingerbread era title bar is needed
+ boolean windowNoTitle =
+ ResourceHelper.getBooleanThemeValue(mResources, ATTR_WINDOW_NO_TITLE, true, false);
+ if (!windowNoTitle) {
+ mTitleBarSize =
+ getDimension(ATTR_WINDOW_TITLE_SIZE, true, DEFAULT_TITLE_BAR_HEIGHT);
+ }
+ }
+ }
+
+ private void findNavBar() {
+ if (hasSoftwareButtons() && !mWindowIsFloating) {
+
+ // get orientation
+ HardwareConfig hwConfig = mParams.getHardwareConfig();
+ boolean barOnBottom = true;
+
+ if (hwConfig.getOrientation() == ScreenOrientation.LANDSCAPE) {
+ int shortSize = hwConfig.getScreenHeight();
+ int shortSizeDp = shortSize * DisplayMetrics.DENSITY_DEFAULT /
+ hwConfig.getDensity().getDpiValue();
+
+ // 0-599dp: "phone" UI with bar on the side
+ // 600+dp: "tablet" UI with bar on the bottom
+ barOnBottom = shortSizeDp >= 600;
+ }
+
+ mNavBarOrientation = barOnBottom ? LinearLayout.HORIZONTAL : VERTICAL;
+ mNavBarSize = getDimension(barOnBottom ? ATTR_NAV_BAR_HEIGHT : ATTR_NAV_BAR_WIDTH,
+ true, DEFAULT_NAV_BAR_SIZE);
+ mTranslucentNav = ResourceHelper.getBooleanThemeValue(mResources,
+ ATTR_WINDOW_TRANSLUCENT_NAV, true, false);
+ }
+ }
+
+ private int getDimension(String attr, boolean isFramework, int defaultValue) {
+ ResourceValue value = mResources.findItemInTheme(attr, isFramework);
+ value = mResources.resolveResValue(value);
+ if (value != null) {
+ TypedValue typedValue = ResourceHelper.getValue(attr, value.getValue(), true);
+ if (typedValue != null) {
+ return (int) typedValue.getDimension(mContext.getMetrics());
+ }
+ }
+ return defaultValue;
+ }
+
+ private boolean hasSoftwareButtons() {
+ return mParams.getHardwareConfig().hasSoftwareButtons();
+ }
+
+ private boolean isThemeAppCompat() {
+ // If a cached value exists, return it.
+ if (mIsThemeAppCompat != null) {
+ return mIsThemeAppCompat;
+ }
+ // Ideally, we should check if the corresponding activity extends
+ // android.support.v7.app.ActionBarActivity, and not care about the theme name at all.
+ StyleResourceValue defaultTheme = mResources.getDefaultTheme();
+ // We can't simply check for parent using resources.themeIsParentOf() since the
+ // inheritance structure isn't really what one would expect. The first common parent
+ // between Theme.AppCompat.Light and Theme.AppCompat is Theme.Material (for v21).
+ boolean isThemeAppCompat = false;
+ for (int i = 0; i < 50; i++) {
+ if (defaultTheme == null) {
+ break;
+ }
+ // for loop ensures that we don't run into cyclic theme inheritance.
+ if (defaultTheme.getName().startsWith(PREFIX_THEME_APPCOMPAT)) {
+ isThemeAppCompat = true;
+ break;
+ }
+ defaultTheme = mResources.getParent(defaultTheme);
+ }
+ mIsThemeAppCompat = isThemeAppCompat;
+ return isThemeAppCompat;
+ }
+
+ /**
+ * Return if both status bar and nav bar are solid (content doesn't overlap with these
+ * bars).
+ */
+ private boolean solidBars() {
+ return hasNavBar() && !mTranslucentNav && !mTranslucentStatus && mStatusBarSize > 0;
+ }
+
+ private boolean hasNavBar() {
+ return Config.showOnScreenNavBar(mParams.getSimulatedPlatformVersion()) &&
+ hasSoftwareButtons() && mNavBarSize > 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 6a9d5dd6..ac7c409 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
@@ -29,10 +29,8 @@
import com.android.ide.common.rendering.api.Result.Status;
import com.android.ide.common.rendering.api.SessionParams;
import com.android.ide.common.rendering.api.SessionParams.RenderingMode;
-import com.android.ide.common.rendering.api.StyleResourceValue;
import com.android.ide.common.rendering.api.ViewInfo;
import com.android.ide.common.rendering.api.ViewType;
-import com.android.internal.util.XmlUtils;
import com.android.internal.view.menu.ActionMenuItemView;
import com.android.internal.view.menu.BridgeMenuItemImpl;
import com.android.internal.view.menu.IconMenuItemView;
@@ -45,22 +43,11 @@
import com.android.layoutlib.bridge.android.BridgeXmlBlockParser;
import com.android.layoutlib.bridge.android.RenderParamsFlags;
import com.android.layoutlib.bridge.android.support.DesignLibUtil;
-import com.android.layoutlib.bridge.bars.AppCompatActionBar;
-import com.android.layoutlib.bridge.bars.BridgeActionBar;
-import com.android.layoutlib.bridge.bars.Config;
-import com.android.layoutlib.bridge.bars.FrameworkActionBar;
-import com.android.layoutlib.bridge.bars.NavigationBar;
-import com.android.layoutlib.bridge.bars.StatusBar;
-import com.android.layoutlib.bridge.bars.TitleBar;
import com.android.layoutlib.bridge.impl.binding.FakeAdapter;
import com.android.layoutlib.bridge.impl.binding.FakeExpandableAdapter;
-import com.android.resources.Density;
import com.android.resources.ResourceType;
-import com.android.resources.ScreenOrientation;
import com.android.util.Pair;
-import org.xmlpull.v1.XmlPullParserException;
-
import android.animation.AnimationThread;
import android.animation.Animator;
import android.animation.AnimatorInflater;
@@ -72,10 +59,7 @@
import android.graphics.Bitmap;
import android.graphics.Bitmap_Delegate;
import android.graphics.Canvas;
-import android.graphics.drawable.Drawable;
import android.preference.Preference_Delegate;
-import android.util.DisplayMetrics;
-import android.util.TypedValue;
import android.view.AttachInfo_Accessor;
import android.view.BridgeInflater;
import android.view.IWindowManager;
@@ -126,33 +110,22 @@
*/
public class RenderSessionImpl extends RenderAction<SessionParams> {
- private static final int DEFAULT_TITLE_BAR_HEIGHT = 25;
- private static final int DEFAULT_STATUS_BAR_HEIGHT = 25;
-
// scene state
private RenderSession mScene;
private BridgeXmlBlockParser mBlockParser;
private BridgeInflater mInflater;
- private ResourceValue mWindowBackground;
private ViewGroup mViewRoot;
private FrameLayout mContentRoot;
private Canvas mCanvas;
private int mMeasuredScreenWidth = -1;
private int mMeasuredScreenHeight = -1;
private boolean mIsAlphaChannelImage;
- private boolean mWindowIsFloating;
- private Boolean mIsThemeAppCompat;
-
- private int mStatusBarSize;
- private int mNavigationBarSize;
- private int mNavigationBarOrientation = LinearLayout.HORIZONTAL;
- private int mTitleBarSize;
- private int mActionBarSize;
// information being returned through the API
private BufferedImage mImage;
private List<ViewInfo> mViewInfoList;
private List<ViewInfo> mSystemViewInfoList;
+ private Layout.Builder mLayoutBuilder;
private static final class PostInflateException extends Exception {
private static final long serialVersionUID = 1L;
@@ -196,34 +169,24 @@
SessionParams params = getParams();
BridgeContext context = getContext();
-
- RenderResources resources = getParams().getResources();
- DisplayMetrics metrics = getContext().getMetrics();
-
// use default of true in case it's not found to use alpha by default
- mIsAlphaChannelImage = getBooleanThemeValue(resources, "windowIsFloating", true, true);
- // FIXME: Find out why both variables are taking the same value.
- mWindowIsFloating = getBooleanThemeValue(resources, "windowIsFloating", true, true);
+ mIsAlphaChannelImage = ResourceHelper.getBooleanThemeValue(params.getResources(),
+ "windowIsFloating", true, true);
- findBackground(resources);
- findStatusBar(resources, metrics);
- findActionBar(resources, metrics);
- findNavigationBar(resources, metrics);
+ mLayoutBuilder = new Layout.Builder(params, context);
// FIXME: find those out, and possibly add them to the render params
boolean hasNavigationBar = true;
//noinspection ConstantConditions
IWindowManager iwm = new IWindowManagerImpl(getContext().getConfiguration(),
- metrics, Surface.ROTATION_0,
- hasNavigationBar);
+ context.getMetrics(), Surface.ROTATION_0, hasNavigationBar);
WindowManagerGlobal_Delegate.setWindowManagerService(iwm);
// build the inflater and parser.
mInflater = new BridgeInflater(context, params.getLayoutlibCallback());
context.setBridgeInflater(mInflater);
- mBlockParser = new BridgeXmlBlockParser(
- params.getLayoutDescription(), context, false /* platformResourceFlag */);
+ mBlockParser = new BridgeXmlBlockParser(params.getLayoutDescription(), context, false);
return SUCCESS.createResult();
}
@@ -240,164 +203,11 @@
checkLock();
try {
-
+ mViewRoot = new Layout(mLayoutBuilder);
+ mLayoutBuilder = null; // Done with the builder.
+ mContentRoot = ((Layout) mViewRoot).getContentRoot();
SessionParams params = getParams();
- HardwareConfig hardwareConfig = params.getHardwareConfig();
BridgeContext context = getContext();
- boolean isRtl = Bridge.isLocaleRtl(params.getLocale());
- int layoutDirection = isRtl ? View.LAYOUT_DIRECTION_RTL : View.LAYOUT_DIRECTION_LTR;
-
- // the view group that receives the window background.
- ViewGroup backgroundView;
-
- if (mWindowIsFloating || params.isForceNoDecor()) {
- backgroundView = mViewRoot = mContentRoot = new FrameLayout(context);
- mViewRoot.setLayoutDirection(layoutDirection);
- } else {
- int simulatedPlatformVersion = params.getSimulatedPlatformVersion();
- if (hasSoftwareButtons() && mNavigationBarOrientation == LinearLayout.VERTICAL) {
- /*
- * This is a special case where the navigation bar is on the right.
- +-------------------------------------------------+---+
- | Status bar (always) | |
- +-------------------------------------------------+ |
- | (Layout with background drawable) | |
- | +---------------------------------------------+ | |
- | | Title/Action bar (optional) | | |
- | +---------------------------------------------+ | |
- | | Content, vertical extending | | |
- | | | | |
- | +---------------------------------------------+ | |
- +-------------------------------------------------+---+
-
- So we create a horizontal layout, with the nav bar on the right,
- and the left part is the normal layout below without the nav bar at
- the bottom
- */
- LinearLayout topLayout = new LinearLayout(context);
- topLayout.setLayoutDirection(layoutDirection);
- mViewRoot = topLayout;
- topLayout.setOrientation(LinearLayout.HORIZONTAL);
-
- if (Config.showOnScreenNavBar(simulatedPlatformVersion)) {
- try {
- NavigationBar navigationBar = createNavigationBar(context,
- hardwareConfig.getDensity(), isRtl, params.isRtlSupported(),
- simulatedPlatformVersion);
- topLayout.addView(navigationBar);
- } catch (XmlPullParserException ignored) {
- }
- }
- }
-
- /*
- * we're creating the following layout
- *
- +-------------------------------------------------+
- | Status bar (always) |
- +-------------------------------------------------+
- | (Layout with background drawable) |
- | +---------------------------------------------+ |
- | | Title/Action bar (optional) | |
- | +---------------------------------------------+ |
- | | Content, vertical extending | |
- | | | |
- | +---------------------------------------------+ |
- +-------------------------------------------------+
- | Navigation bar for soft buttons, maybe see above|
- +-------------------------------------------------+
-
- */
-
- LinearLayout topLayout = new LinearLayout(context);
- topLayout.setOrientation(LinearLayout.VERTICAL);
- topLayout.setLayoutDirection(layoutDirection);
- // if we don't already have a view root this is it
- if (mViewRoot == null) {
- mViewRoot = topLayout;
- } else {
- int topLayoutWidth =
- params.getHardwareConfig().getScreenWidth() - mNavigationBarSize;
- LinearLayout.LayoutParams layoutParams = new LinearLayout.LayoutParams(
- topLayoutWidth, LayoutParams.MATCH_PARENT);
- topLayout.setLayoutParams(layoutParams);
-
- // this is the case of soft buttons + vertical bar.
- // this top layout is the first layout in the horizontal layout. see above)
- if (isRtl && params.isRtlSupported()) {
- // If RTL is enabled, layoutlib will mirror the layouts. So, add the
- // topLayout to the right of Navigation Bar and layoutlib will draw it
- // to the left.
- mViewRoot.addView(topLayout);
- } else {
- // Add the top layout to the left of the Navigation Bar.
- mViewRoot.addView(topLayout, 0);
- }
- }
-
- if (mStatusBarSize > 0) {
- // system bar
- try {
- StatusBar statusBar = createStatusBar(context, hardwareConfig.getDensity(),
- layoutDirection, params.isRtlSupported(),
- simulatedPlatformVersion);
- topLayout.addView(statusBar);
- } catch (XmlPullParserException ignored) {
-
- }
- }
-
- LinearLayout backgroundLayout = new LinearLayout(context);
- backgroundView = backgroundLayout;
- backgroundLayout.setOrientation(LinearLayout.VERTICAL);
- LinearLayout.LayoutParams layoutParams = new LinearLayout.LayoutParams(
- LayoutParams.MATCH_PARENT, 0);
- layoutParams.weight = 1;
- backgroundLayout.setLayoutParams(layoutParams);
- topLayout.addView(backgroundLayout);
-
-
- // if the theme says no title/action bar, then the size will be 0
- if (mActionBarSize > 0) {
- BridgeActionBar actionBar = createActionBar(context, params, backgroundLayout);
- actionBar.createMenuPopup();
- mContentRoot = actionBar.getContentRoot();
- } else if (mTitleBarSize > 0) {
- try {
- TitleBar titleBar = createTitleBar(context,
- params.getAppLabel(),
- simulatedPlatformVersion);
- backgroundLayout.addView(titleBar);
- } catch (XmlPullParserException ignored) {
-
- }
- }
-
- // content frame
- if (mContentRoot == null) {
- mContentRoot = new FrameLayout(context);
- layoutParams = new LinearLayout.LayoutParams(
- LayoutParams.MATCH_PARENT, 0);
- layoutParams.weight = 1;
- mContentRoot.setLayoutParams(layoutParams);
- backgroundLayout.addView(mContentRoot);
- }
-
- if (Config.showOnScreenNavBar(simulatedPlatformVersion) &&
- mNavigationBarOrientation == LinearLayout.HORIZONTAL &&
- mNavigationBarSize > 0) {
- // system bar
- try {
- NavigationBar navigationBar = createNavigationBar(context,
- hardwareConfig.getDensity(), isRtl, params.isRtlSupported(),
- simulatedPlatformVersion);
- topLayout.addView(navigationBar);
- } catch (XmlPullParserException ignored) {
-
- }
- }
- }
-
// Sets the project callback (custom view loader) to the fragment delegate so that
// it can instantiate the custom Fragment.
@@ -408,7 +218,7 @@
View view;
if (isPreference) {
view = Preference_Delegate.inflatePreference(getContext(), mBlockParser,
- mContentRoot);
+ mContentRoot);
} else {
view = mInflater.inflate(mBlockParser, mContentRoot);
}
@@ -427,12 +237,6 @@
setActiveToolbar(view, context, params);
- // get the background drawable
- if (mWindowBackground != null) {
- Drawable d = ResourceHelper.getDrawable(mWindowBackground, context);
- backgroundView.setBackground(d);
- }
-
return SUCCESS.createResult();
} catch (PostInflateException e) {
return ERROR_INFLATION.createResult(e.getMessage(), e);
@@ -1073,198 +877,6 @@
}
}
-
- private void findBackground(RenderResources resources) {
- if (!getParams().isBgColorOverridden()) {
- mWindowBackground = resources.findItemInTheme("windowBackground",
- true /*isFrameworkAttr*/);
- if (mWindowBackground != null) {
- mWindowBackground = resources.resolveResValue(mWindowBackground);
- }
- }
- }
-
- private boolean hasSoftwareButtons() {
- return getParams().getHardwareConfig().hasSoftwareButtons();
- }
-
- private void findStatusBar(RenderResources resources, DisplayMetrics metrics) {
- boolean windowFullscreen = getBooleanThemeValue(resources,
- "windowFullscreen", false, true);
-
- if (!windowFullscreen && !mWindowIsFloating) {
- // 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("status_bar_height",
- value.getValue(), true /*requireUnit*/);
- if (typedValue != null) {
- // compute the pixel value based on the display metrics
- mStatusBarSize = (int)typedValue.getDimension(metrics);
- }
- }
- }
- }
-
- private void findActionBar(RenderResources resources, DisplayMetrics metrics) {
- if (mWindowIsFloating) {
- return;
- }
-
- boolean windowActionBar = getBooleanThemeValue(resources,
- "windowActionBar", true, !isThemeAppCompat(resources));
-
- // if there's a value and it's false (default is true)
- if (windowActionBar) {
-
- // default size of the window title bar
- mActionBarSize = DEFAULT_TITLE_BAR_HEIGHT;
-
- // get value from the theme.
- ResourceValue value = resources.findItemInTheme("actionBarSize",
- true /*isFrameworkAttr*/);
-
- // resolve it
- value = resources.resolveResValue(value);
-
- if (value != null) {
- // get the numerical value, if available
- TypedValue typedValue = ResourceHelper.getValue("actionBarSize", value.getValue(),
- true /*requireUnit*/);
- if (typedValue != null) {
- // compute the pixel value based on the display 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, true);
-
- if (!windowNoTitle) {
-
- // default size of the window title bar
- mTitleBarSize = DEFAULT_TITLE_BAR_HEIGHT;
-
- // get value from the theme.
- ResourceValue value = resources.findItemInTheme("windowTitleSize",
- true /*isFrameworkAttr*/);
-
- // resolve it
- value = resources.resolveResValue(value);
-
- if (value != null) {
- // get the numerical value, if available
- TypedValue typedValue = ResourceHelper.getValue("windowTitleSize",
- value.getValue(), true /*requireUnit*/);
- if (typedValue != null) {
- // compute the pixel value based on the display metrics
- mTitleBarSize = (int)typedValue.getDimension(metrics);
- }
- }
- }
-
- }
- }
-
- private void findNavigationBar(RenderResources resources, DisplayMetrics metrics) {
- if (hasSoftwareButtons() && !mWindowIsFloating) {
-
- // default value
- mNavigationBarSize = 48; // ??
-
- HardwareConfig hardwareConfig = getParams().getHardwareConfig();
-
- boolean barOnBottom = true;
-
- if (hardwareConfig.getOrientation() == ScreenOrientation.LANDSCAPE) {
- // compute the dp of the screen.
- int shortSize = hardwareConfig.getScreenHeight();
-
- // compute in dp
- int shortSizeDp = shortSize * DisplayMetrics.DENSITY_DEFAULT /
- hardwareConfig.getDensity().getDpiValue();
-
- // 0-599dp: "phone" UI with bar on the side
- // 600+dp: "tablet" UI with bar on the bottom
- barOnBottom = shortSizeDp >= 600;
- }
-
- if (barOnBottom) {
- mNavigationBarOrientation = LinearLayout.HORIZONTAL;
- } else {
- mNavigationBarOrientation = LinearLayout.VERTICAL;
- }
-
- // get the real value
- ResourceValue value = resources.getFrameworkResource(ResourceType.DIMEN,
- barOnBottom ? "navigation_bar_height" : "navigation_bar_width");
-
- if (value != null) {
- TypedValue typedValue = ResourceHelper.getValue("navigation_bar_height",
- value.getValue(), true /*requireUnit*/);
- if (typedValue != null) {
- // compute the pixel value based on the display metrics
- mNavigationBarSize = (int)typedValue.getDimension(metrics);
- }
- }
- }
- }
-
- private boolean isThemeAppCompat(RenderResources resources) {
- // Ideally, we should check if the corresponding activity extends
- // android.support.v7.app.ActionBarActivity, and not care about the theme name at all.
- if (mIsThemeAppCompat == null) {
- StyleResourceValue defaultTheme = resources.getDefaultTheme();
- // We can't simply check for parent using resources.themeIsParentOf() since the
- // inheritance structure isn't really what one would expect. The first common parent
- // between Theme.AppCompat.Light and Theme.AppCompat is Theme.Material (for v21).
- boolean isThemeAppCompat = false;
- for (int i = 0; i < 50; i++) {
- if (defaultTheme == null) {
- break;
- }
- // for loop ensures that we don't run into cyclic theme inheritance.
- if (defaultTheme.getName().startsWith("Theme.AppCompat")) {
- isThemeAppCompat = true;
- break;
- }
- defaultTheme = resources.getParent(defaultTheme);
- }
- mIsThemeAppCompat = isThemeAppCompat;
- }
- return mIsThemeAppCompat;
- }
-
- /**
- * Looks for an attribute in the current theme.
- *
- * @param resources the render resources
- * @param name the name of the attribute
- * @param defaultValue the default value.
- * @param isFrameworkAttr if the attribute is in android namespace
- * @return the value of the attribute or the default one if not found.
- */
- private boolean getBooleanThemeValue(RenderResources resources,
- String name, boolean defaultValue, boolean isFrameworkAttr) {
-
- ResourceValue value = resources.findItemInTheme(name, isFrameworkAttr);
-
- // because it may reference something else, we resolve it.
- value = resources.resolveResValue(value);
-
- // if there's no value, return the default.
- if (value == null || value.getValue() == null) {
- return defaultValue;
- }
-
- return XmlUtils.convertValueToBoolean(value.getValue(), defaultValue);
- }
-
/**
* Post process on a view hierarchy that was just inflated.
* <p/>
@@ -1737,63 +1349,6 @@
mMeasuredScreenWidth = mMeasuredScreenHeight = -1;
}
- /**
- * Creates the status bar with wifi and battery icons.
- */
- private StatusBar createStatusBar(BridgeContext context, Density density, int direction,
- boolean isRtlSupported, int platformVersion) throws XmlPullParserException {
- StatusBar statusBar = new StatusBar(context, density,
- direction, isRtlSupported, platformVersion);
- statusBar.setLayoutParams(
- new LinearLayout.LayoutParams(
- LayoutParams.MATCH_PARENT, mStatusBarSize));
- return statusBar;
- }
-
- /**
- * Creates the navigation bar with back, home and recent buttons.
- *
- * @param isRtl true if the current locale is right-to-left
- * @param isRtlSupported true is the project manifest declares that the application
- * is RTL aware.
- */
- private NavigationBar createNavigationBar(BridgeContext context, Density density,
- boolean isRtl, boolean isRtlSupported, int simulatedPlatformVersion)
- throws XmlPullParserException {
- NavigationBar navigationBar = new NavigationBar(context,
- density, mNavigationBarOrientation, isRtl,
- isRtlSupported, simulatedPlatformVersion);
- if (mNavigationBarOrientation == LinearLayout.VERTICAL) {
- navigationBar.setLayoutParams(new LinearLayout.LayoutParams(mNavigationBarSize,
- LayoutParams.MATCH_PARENT));
- } else {
- navigationBar.setLayoutParams(new LinearLayout.LayoutParams(LayoutParams.MATCH_PARENT,
- mNavigationBarSize));
- }
- return navigationBar;
- }
-
- private TitleBar createTitleBar(BridgeContext context, String title,
- int simulatedPlatformVersion)
- throws XmlPullParserException {
- TitleBar titleBar = new TitleBar(context, title, simulatedPlatformVersion);
- titleBar.setLayoutParams(
- new LinearLayout.LayoutParams(LayoutParams.MATCH_PARENT, mTitleBarSize));
- return titleBar;
- }
-
- /**
- * Creates the action bar. Also queries the project callback for missing information.
- */
- private BridgeActionBar createActionBar(BridgeContext context, SessionParams params,
- ViewGroup parentView) {
- if (mIsThemeAppCompat == Boolean.TRUE) {
- return new AppCompatActionBar(context, params, parentView);
- } else {
- return new FrameworkActionBar(context, params, parentView);
- }
- }
-
public BufferedImage getImage() {
return mImage;
}
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/ResourceHelper.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/ResourceHelper.java
index ca77193..c72eeb1 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/ResourceHelper.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/ResourceHelper.java
@@ -21,6 +21,7 @@
import com.android.ide.common.rendering.api.LayoutLog;
import com.android.ide.common.rendering.api.RenderResources;
import com.android.ide.common.rendering.api.ResourceValue;
+import com.android.internal.util.XmlUtils;
import com.android.layoutlib.bridge.Bridge;
import com.android.layoutlib.bridge.android.BridgeContext;
import com.android.layoutlib.bridge.android.BridgeXmlBlockParser;
@@ -327,6 +328,25 @@
return null;
}
+ /**
+ * Looks for an attribute in the current theme.
+ *
+ * @param resources the render resources
+ * @param name the name of the attribute
+ * @param defaultValue the default value.
+ * @param isFrameworkAttr if the attribute is in android namespace
+ * @return the value of the attribute or the default one if not found.
+ */
+ public static boolean getBooleanThemeValue(@NonNull RenderResources resources, String name,
+ boolean isFrameworkAttr, boolean defaultValue) {
+ ResourceValue value = resources.findItemInTheme(name, isFrameworkAttr);
+ value = resources.resolveResValue(value);
+ if (value == null) {
+ return defaultValue;
+ }
+ return XmlUtils.convertValueToBoolean(value.getValue(), defaultValue);
+ }
+
// ------- TypedValue stuff
// This is taken from //device/libs/utils/ResourceTypes.cpp