Merge "AAPT2: Prevent duplicate enums/flags"
diff --git a/core/java/android/app/TimePickerDialog.java b/core/java/android/app/TimePickerDialog.java
index a3b3022..aca0763 100644
--- a/core/java/android/app/TimePickerDialog.java
+++ b/core/java/android/app/TimePickerDialog.java
@@ -23,10 +23,8 @@
import android.util.TypedValue;
import android.view.LayoutInflater;
import android.view.View;
-import android.widget.Button;
import android.widget.TimePicker;
import android.widget.TimePicker.OnTimeChangedListener;
-import android.widget.TimePicker.ValidationCallback;
import com.android.internal.R;
@@ -64,7 +62,7 @@
* @param hourOfDay the hour that was set
* @param minute the minute that was set
*/
- public void onTimeSet(TimePicker view, int hourOfDay, int minute);
+ void onTimeSet(TimePicker view, int hourOfDay, int minute);
}
/**
@@ -115,7 +113,6 @@
final TypedValue outValue = new TypedValue();
context.getTheme().resolveAttribute(R.attr.timePickerDialogTheme, outValue, true);
- final int layoutResId = outValue.resourceId;
final LayoutInflater inflater = LayoutInflater.from(themeContext);
final View view = inflater.inflate(R.layout.time_picker_dialog, null);
@@ -129,7 +126,6 @@
mTimePicker.setCurrentHour(mInitialHourOfDay);
mTimePicker.setCurrentMinute(mInitialMinute);
mTimePicker.setOnTimeChangedListener(this);
- mTimePicker.setValidationCallback(mValidationCallback);
}
@Override
@@ -181,14 +177,4 @@
mTimePicker.setCurrentHour(hour);
mTimePicker.setCurrentMinute(minute);
}
-
- private final ValidationCallback mValidationCallback = new ValidationCallback() {
- @Override
- public void onValidationChanged(boolean valid) {
- final Button positive = getButton(BUTTON_POSITIVE);
- if (positive != null) {
- positive.setEnabled(valid);
- }
- }
- };
}
diff --git a/core/java/android/content/pm/ApplicationInfo.java b/core/java/android/content/pm/ApplicationInfo.java
index 52ec4cc..eda4136 100644
--- a/core/java/android/content/pm/ApplicationInfo.java
+++ b/core/java/android/content/pm/ApplicationInfo.java
@@ -484,7 +484,7 @@
*
* {@hide}
*/
- public static final int PRIVATE_FLAG_AUTOPLAY = 1<<6;
+ public static final int PRIVATE_FLAG_AUTOPLAY = 1 << 7;
/**
* Private/hidden flags. See {@code PRIVATE_FLAG_...} constants.
diff --git a/core/java/android/widget/PopupMenu.java b/core/java/android/widget/PopupMenu.java
index 34a8439..a53df88 100644
--- a/core/java/android/widget/PopupMenu.java
+++ b/core/java/android/widget/PopupMenu.java
@@ -21,7 +21,6 @@
import com.android.internal.view.menu.MenuPopupHelper;
import com.android.internal.view.menu.MenuPresenter;
import com.android.internal.view.menu.ShowableListMenu;
-import com.android.internal.view.menu.SubMenuBuilder;
import android.annotation.MenuRes;
import android.content.Context;
@@ -33,35 +32,23 @@
import android.view.View.OnTouchListener;
/**
- * A PopupMenu displays a {@link Menu} in a modal popup window anchored to a {@link View}.
- * The popup will appear below the anchor view if there is room, or above it if there is not.
- * If the IME is visible the popup will not overlap it until it is touched. Touching outside
- * of the popup will dismiss it.
+ * A PopupMenu displays a {@link Menu} in a modal popup window anchored to a
+ * {@link View}. The popup will appear below the anchor view if there is room,
+ * or above it if there is not. If the IME is visible the popup will not
+ * overlap it until it is touched. Touching outside of the popup will dismiss
+ * it.
*/
-public class PopupMenu implements MenuBuilder.Callback, MenuPresenter.Callback {
+public class PopupMenu {
private final Context mContext;
private final MenuBuilder mMenu;
private final View mAnchor;
private final MenuPopupHelper mPopup;
- private final boolean mShowCascadingMenus;
private OnMenuItemClickListener mMenuItemClickListener;
private OnDismissListener mDismissListener;
private OnTouchListener mDragListener;
/**
- * Callback interface used to notify the application that the menu has closed.
- */
- public interface OnDismissListener {
- /**
- * Called when the associated menu has been dismissed.
- *
- * @param menu The PopupMenu that was dismissed.
- */
- public void onDismiss(PopupMenu menu);
- }
-
- /**
* Constructor to create a new popup menu with an anchor view.
*
* @param context Context the popup menu is running in, through which it
@@ -108,14 +95,40 @@
public PopupMenu(Context context, View anchor, int gravity, int popupStyleAttr,
int popupStyleRes) {
mContext = context;
- mShowCascadingMenus = context.getResources().getBoolean(
- com.android.internal.R.bool.config_enableCascadingSubmenus);
- mMenu = new MenuBuilder(context);
- mMenu.setCallback(this);
mAnchor = anchor;
+
+ mMenu = new MenuBuilder(context);
+ mMenu.setCallback(new MenuBuilder.Callback() {
+ @Override
+ public boolean onMenuItemSelected(MenuBuilder menu, MenuItem item) {
+ if (mMenuItemClickListener != null) {
+ return mMenuItemClickListener.onMenuItemClick(item);
+ }
+ return false;
+ }
+
+ @Override
+ public void onMenuModeChange(MenuBuilder menu) {
+ }
+ });
+
mPopup = new MenuPopupHelper(context, mMenu, anchor, false, popupStyleAttr, popupStyleRes);
mPopup.setGravity(gravity);
- mPopup.setCallback(this);
+ mPopup.setCallback(new MenuPresenter.Callback() {
+ @Override
+ public void onCloseMenu(MenuBuilder menu, boolean allMenusAreClosing) {
+ if (mDismissListener != null) {
+ mDismissListener.onDismiss(PopupMenu.this);
+ }
+ }
+
+ @Override
+ public boolean onOpenSubMenu(MenuBuilder subMenu) {
+ // The menu presenter will handle opening the submenu itself.
+ // Nothing to do here.
+ return false;
+ }
+ });
}
/**
@@ -125,7 +138,6 @@
* the next time the popup is shown.
*
* @param gravity the gravity used to align the popup window
- *
* @see #getGravity()
*/
public void setGravity(int gravity) {
@@ -134,7 +146,6 @@
/**
* @return the gravity used to align the popup window to its anchor view
- *
* @see #setGravity(int)
*/
public int getGravity() {
@@ -146,8 +157,8 @@
* to implement drag-to-open behavior.
* <p>
* When the listener is set on a view, touching that view and dragging
- * outside of its bounds will open the popup window. Lifting will select the
- * currently touched list item.
+ * outside of its bounds will open the popup window. Lifting will select
+ * the currently touched list item.
* <p>
* Example usage:
* <pre>
@@ -184,9 +195,10 @@
}
/**
- * @return the {@link Menu} associated with this popup. Populate the returned Menu with
- * items before calling {@link #show()}.
+ * Returns the {@link Menu} associated with this popup. Populate the
+ * returned Menu with items before calling {@link #show()}.
*
+ * @return the {@link Menu} associated with this popup
* @see #show()
* @see #getMenuInflater()
*/
@@ -195,9 +207,8 @@
}
/**
- * @return a {@link MenuInflater} that can be used to inflate menu items from XML into the
- * menu returned by {@link #getMenu()}.
- *
+ * @return a {@link MenuInflater} that can be used to inflate menu items
+ * from XML into the menu returned by {@link #getMenu()}
* @see #getMenu()
*/
public MenuInflater getMenuInflater() {
@@ -205,8 +216,9 @@
}
/**
- * Inflate a menu resource into this PopupMenu. This is equivalent to calling
- * popupMenu.getMenuInflater().inflate(menuRes, popupMenu.getMenu()).
+ * Inflate a menu resource into this PopupMenu. This is equivalent to
+ * calling {@code popupMenu.getMenuInflater().inflate(menuRes, popupMenu.getMenu())}.
+ *
* @param menuRes Menu resource to inflate
*/
public void inflate(@MenuRes int menuRes) {
@@ -215,6 +227,7 @@
/**
* Show the menu popup anchored to the view specified during construction.
+ *
* @see #dismiss()
*/
public void show() {
@@ -223,6 +236,7 @@
/**
* Dismiss the menu popup.
+ *
* @see #show()
*/
public void dismiss() {
@@ -230,68 +244,49 @@
}
/**
- * Set a listener that will be notified when the user selects an item from the menu.
+ * Sets a listener that will be notified when the user selects an item from
+ * the menu.
*
- * @param listener Listener to notify
+ * @param listener the listener to notify
*/
public void setOnMenuItemClickListener(OnMenuItemClickListener listener) {
mMenuItemClickListener = listener;
}
/**
- * Set a listener that will be notified when this menu is dismissed.
+ * Sets a listener that will be notified when this menu is dismissed.
*
- * @param listener Listener to notify
+ * @param listener the listener to notify
*/
public void setOnDismissListener(OnDismissListener listener) {
mDismissListener = listener;
}
/**
- * @hide
- */
- public boolean onMenuItemSelected(MenuBuilder menu, MenuItem item) {
- if (mMenuItemClickListener != null) {
- return mMenuItemClickListener.onMenuItemClick(item);
- }
- return false;
- }
-
- /**
- * @hide
- */
- public void onCloseMenu(MenuBuilder menu, boolean allMenusAreClosing) {
- if (mDismissListener != null) {
- mDismissListener.onDismiss(this);
- }
- }
-
- /**
- * @hide
- */
- public boolean onOpenSubMenu(MenuBuilder subMenu) {
- // The menu presenter will handle opening the submenu itself. Nothing to do here.
- return false;
- }
-
- /**
- * @hide
- */
- public void onMenuModeChange(MenuBuilder menu) {
- }
-
- /**
- * Interface responsible for receiving menu item click events if the items themselves
- * do not have individual item click listeners.
+ * Interface responsible for receiving menu item click events if the items
+ * themselves do not have individual item click listeners.
*/
public interface OnMenuItemClickListener {
/**
- * This method will be invoked when a menu item is clicked if the item itself did
- * not already handle the event.
+ * This method will be invoked when a menu item is clicked if the item
+ * itself did not already handle the event.
*
- * @param item {@link MenuItem} that was clicked
- * @return <code>true</code> if the event was handled, <code>false</code> otherwise.
+ * @param item the menu item that was clicked
+ * @return {@code true} if the event was handled, {@code false}
+ * otherwise
*/
- public boolean onMenuItemClick(MenuItem item);
+ boolean onMenuItemClick(MenuItem item);
+ }
+
+ /**
+ * Callback interface used to notify the application that the menu has closed.
+ */
+ public interface OnDismissListener {
+ /**
+ * Called when the associated menu has been dismissed.
+ *
+ * @param menu the popup menu that was dismissed
+ */
+ void onDismiss(PopupMenu menu);
}
}
diff --git a/core/java/android/widget/TimePicker.java b/core/java/android/widget/TimePicker.java
index 8e5af79..a24d37f 100644
--- a/core/java/android/widget/TimePicker.java
+++ b/core/java/android/widget/TimePicker.java
@@ -100,7 +100,7 @@
* @see #getHour()
*/
public void setHour(int hour) {
- mDelegate.setCurrentHour(hour);
+ mDelegate.setHour(hour);
}
/**
@@ -110,7 +110,7 @@
* @see #setHour(int)
*/
public int getHour() {
- return mDelegate.getCurrentHour();
+ return mDelegate.getHour();
}
/**
@@ -120,7 +120,7 @@
* @see #getMinute()
*/
public void setMinute(int minute) {
- mDelegate.setCurrentMinute(minute);
+ mDelegate.setMinute(minute);
}
/**
@@ -130,7 +130,7 @@
* @see #setMinute(int)
*/
public int getMinute() {
- return mDelegate.getCurrentMinute();
+ return mDelegate.getMinute();
}
/**
@@ -150,7 +150,7 @@
@NonNull
@Deprecated
public Integer getCurrentHour() {
- return mDelegate.getCurrentHour();
+ return mDelegate.getHour();
}
/**
@@ -160,7 +160,7 @@
*/
@Deprecated
public void setCurrentMinute(@NonNull Integer currentMinute) {
- mDelegate.setCurrentMinute(currentMinute);
+ mDelegate.setMinute(currentMinute);
}
/**
@@ -170,7 +170,7 @@
@NonNull
@Deprecated
public Integer getCurrentMinute() {
- return mDelegate.getCurrentMinute();
+ return mDelegate.getMinute();
}
/**
@@ -186,7 +186,7 @@
return;
}
- mDelegate.setIs24HourView(is24HourView);
+ mDelegate.setIs24Hour(is24HourView);
}
/**
@@ -195,7 +195,7 @@
* @see #setIs24HourView(Boolean)
*/
public boolean is24HourView() {
- return mDelegate.is24HourView();
+ return mDelegate.is24Hour();
}
/**
@@ -207,16 +207,6 @@
mDelegate.setOnTimeChangedListener(onTimeChangedListener);
}
- /**
- * Sets the callback that indicates the current time is valid.
- *
- * @param callback the callback, may be null
- * @hide
- */
- public void setValidationCallback(@Nullable ValidationCallback callback) {
- mDelegate.setValidationCallback(callback);
- }
-
@Override
public void setEnabled(boolean enabled) {
super.setEnabled(enabled);
@@ -234,12 +224,6 @@
}
@Override
- protected void onConfigurationChanged(Configuration newConfig) {
- super.onConfigurationChanged(newConfig);
- mDelegate.onConfigurationChanged(newConfig);
- }
-
- @Override
protected Parcelable onSaveInstanceState() {
Parcelable superState = super.onSaveInstanceState();
return mDelegate.onSaveInstanceState(superState);
@@ -269,25 +253,22 @@
* for the real behavior.
*/
interface TimePickerDelegate {
- void setCurrentHour(int currentHour);
- int getCurrentHour();
+ void setHour(int hour);
+ int getHour();
- void setCurrentMinute(int currentMinute);
- int getCurrentMinute();
+ void setMinute(int minute);
+ int getMinute();
- void setIs24HourView(boolean is24HourView);
- boolean is24HourView();
+ void setIs24Hour(boolean is24Hour);
+ boolean is24Hour();
void setOnTimeChangedListener(OnTimeChangedListener onTimeChangedListener);
- void setValidationCallback(ValidationCallback callback);
void setEnabled(boolean enabled);
boolean isEnabled();
int getBaseline();
- void onConfigurationChanged(Configuration newConfig);
-
Parcelable onSaveInstanceState(Parcelable superState);
void onRestoreInstanceState(Parcelable state);
@@ -295,16 +276,6 @@
void onPopulateAccessibilityEvent(AccessibilityEvent event);
}
- /**
- * A callback interface for updating input validity when the TimePicker
- * when included into a Dialog.
- *
- * @hide
- */
- public static interface ValidationCallback {
- void onValidationChanged(boolean valid);
- }
-
static String[] getAmPmStrings(Context context) {
final Locale locale = context.getResources().getConfiguration().locale;
final LocaleData d = LocaleData.get(locale);
@@ -319,43 +290,16 @@
* An abstract class which can be used as a start for TimePicker implementations
*/
abstract static class AbstractTimePickerDelegate implements TimePickerDelegate {
- // The delegator
- protected TimePicker mDelegator;
+ protected final TimePicker mDelegator;
+ protected final Context mContext;
+ protected final Locale mLocale;
- // The context
- protected Context mContext;
-
- // The current locale
- protected Locale mCurrentLocale;
-
- // Callbacks
protected OnTimeChangedListener mOnTimeChangedListener;
- protected ValidationCallback mValidationCallback;
- public AbstractTimePickerDelegate(TimePicker delegator, Context context) {
+ public AbstractTimePickerDelegate(@NonNull TimePicker delegator, @NonNull Context context) {
mDelegator = delegator;
mContext = context;
-
- // initialization based on locale
- setCurrentLocale(Locale.getDefault());
- }
-
- public void setCurrentLocale(Locale locale) {
- if (locale.equals(mCurrentLocale)) {
- return;
- }
- mCurrentLocale = locale;
- }
-
- @Override
- public void setValidationCallback(ValidationCallback callback) {
- mValidationCallback = callback;
- }
-
- protected void onValidationChanged(boolean valid) {
- if (mValidationCallback != null) {
- mValidationCallback.onValidationChanged(valid);
- }
+ mLocale = context.getResources().getConfiguration().locale;
}
}
}
diff --git a/core/java/android/widget/TimePickerClockDelegate.java b/core/java/android/widget/TimePickerClockDelegate.java
index 4dc5fd3e..38ce033 100644
--- a/core/java/android/widget/TimePickerClockDelegate.java
+++ b/core/java/android/widget/TimePickerClockDelegate.java
@@ -19,7 +19,6 @@
import android.annotation.Nullable;
import android.content.Context;
import android.content.res.ColorStateList;
-import android.content.res.Configuration;
import android.content.res.Resources;
import android.content.res.TypedArray;
import android.os.Parcel;
@@ -89,7 +88,7 @@
private boolean mAllowAutoAdvance;
private int mInitialHourOfDay;
private int mInitialMinute;
- private boolean mIs24HourView;
+ private boolean mIs24Hour;
private boolean mIsAmPmAtStart;
// Accessibility strings.
@@ -200,19 +199,19 @@
mAllowAutoAdvance = true;
// Updates mHourFormat variables used below.
- updateHourFormat(mCurrentLocale, mIs24HourView);
+ updateHourFormat(mLocale, mIs24Hour);
// Update hour text field.
final int minHour = mHourFormatStartsAtZero ? 0 : 1;
- final int maxHour = (mIs24HourView ? 23 : 11) + minHour;
+ final int maxHour = (mIs24Hour ? 23 : 11) + minHour;
mHourView.setRange(minHour, maxHour);
mHourView.setShowLeadingZeroes(mHourFormatShowLeadingZero);
// Initialize with current time.
- mTempCalendar = Calendar.getInstance(mCurrentLocale);
+ mTempCalendar = Calendar.getInstance(mLocale);
final int currentHour = mTempCalendar.get(Calendar.HOUR_OF_DAY);
final int currentMinute = mTempCalendar.get(Calendar.MINUTE);
- initialize(currentHour, currentMinute, mIs24HourView, HOUR_INDEX);
+ initialize(currentHour, currentMinute, mIs24Hour, HOUR_INDEX);
}
/**
@@ -333,7 +332,7 @@
private void initialize(int hourOfDay, int minute, boolean is24HourView, int index) {
mInitialHourOfDay = hourOfDay;
mInitialMinute = minute;
- mIs24HourView = is24HourView;
+ mIs24Hour = is24HourView;
updateUI(index);
}
@@ -352,17 +351,16 @@
}
private void updateRadialPicker(int index) {
- mRadialTimePickerView.initialize(mInitialHourOfDay, mInitialMinute, mIs24HourView);
+ mRadialTimePickerView.initialize(mInitialHourOfDay, mInitialMinute, mIs24Hour);
setCurrentItemShowing(index, false, true);
}
private void updateHeaderAmPm() {
-
- if (mIs24HourView) {
+ if (mIs24Hour) {
mAmPmLayout.setVisibility(View.GONE);
} else {
// Ensure that AM/PM layout is in the correct position.
- final String dateTimePattern = DateFormat.getBestDateTimePattern(mCurrentLocale, "hm");
+ final String dateTimePattern = DateFormat.getBestDateTimePattern(mLocale, "hm");
final boolean isAmPmAtStart = dateTimePattern.startsWith("a");
setAmPmAtStart(isAmPmAtStart);
@@ -395,35 +393,32 @@
* Set the current hour.
*/
@Override
- public void setCurrentHour(int currentHour) {
- if (mInitialHourOfDay == currentHour) {
- return;
+ public void setHour(int hour) {
+ if (mInitialHourOfDay != hour) {
+ mInitialHourOfDay = hour;
+ updateHeaderHour(hour, true);
+ updateHeaderAmPm();
+ mRadialTimePickerView.setCurrentHour(hour);
+ mRadialTimePickerView.setAmOrPm(mInitialHourOfDay < 12 ? AM : PM);
+ mDelegator.invalidate();
+ onTimeChanged();
}
- mInitialHourOfDay = currentHour;
- updateHeaderHour(currentHour, true);
- updateHeaderAmPm();
- mRadialTimePickerView.setCurrentHour(currentHour);
- mRadialTimePickerView.setAmOrPm(mInitialHourOfDay < 12 ? AM : PM);
- mDelegator.invalidate();
- onTimeChanged();
}
/**
- * @return The current hour in the range (0-23).
+ * @return the current hour in the range (0-23)
*/
@Override
- public int getCurrentHour() {
- int currentHour = mRadialTimePickerView.getCurrentHour();
- if (mIs24HourView) {
+ public int getHour() {
+ final int currentHour = mRadialTimePickerView.getCurrentHour();
+ if (mIs24Hour) {
return currentHour;
+ }
+
+ if (mRadialTimePickerView.getAmOrPm() == PM) {
+ return (currentHour % HOURS_IN_HALF_DAY) + HOURS_IN_HALF_DAY;
} else {
- switch(mRadialTimePickerView.getAmOrPm()) {
- case PM:
- return (currentHour % HOURS_IN_HALF_DAY) + HOURS_IN_HALF_DAY;
- case AM:
- default:
- return currentHour % HOURS_IN_HALF_DAY;
- }
+ return currentHour % HOURS_IN_HALF_DAY;
}
}
@@ -431,48 +426,48 @@
* Set the current minute (0-59).
*/
@Override
- public void setCurrentMinute(int currentMinute) {
- if (mInitialMinute == currentMinute) {
- return;
+ public void setMinute(int minute) {
+ if (mInitialMinute != minute) {
+ mInitialMinute = minute;
+ updateHeaderMinute(minute, true);
+ mRadialTimePickerView.setCurrentMinute(minute);
+ mDelegator.invalidate();
+ onTimeChanged();
}
- mInitialMinute = currentMinute;
- updateHeaderMinute(currentMinute, true);
- mRadialTimePickerView.setCurrentMinute(currentMinute);
- mDelegator.invalidate();
- onTimeChanged();
}
/**
* @return The current minute.
*/
@Override
- public int getCurrentMinute() {
+ public int getMinute() {
return mRadialTimePickerView.getCurrentMinute();
}
/**
- * Set whether in 24 hour or AM/PM mode.
+ * Sets whether time is displayed in 24-hour mode or 12-hour mode with
+ * AM/PM indicators.
*
- * @param is24HourView True = 24 hour mode. False = AM/PM.
+ * @param is24Hour {@code true} to display time in 24-hour mode or
+ * {@code false} for 12-hour mode with AM/PM
*/
- @Override
- public void setIs24HourView(boolean is24HourView) {
- if (is24HourView == mIs24HourView) {
- return;
+ public void setIs24Hour(boolean is24Hour) {
+ if (mIs24Hour != is24Hour) {
+ mIs24Hour = is24Hour;
+ mInitialHourOfDay = getHour();
+
+ updateUI(mRadialTimePickerView.getCurrentItemShowing());
}
-
- mIs24HourView = is24HourView;
- mInitialHourOfDay = getCurrentHour();
-
- updateUI(mRadialTimePickerView.getCurrentItemShowing());
}
/**
- * @return true if this is in 24 hour view else false.
+ * @return {@code true} if time is displayed in 24-hour mode, or
+ * {@code false} if time is displayed in 12-hour mode with AM/PM
+ * indicators
*/
@Override
- public boolean is24HourView() {
- return mIs24HourView;
+ public boolean is24Hour() {
+ return mIs24Hour;
}
@Override
@@ -502,14 +497,9 @@
}
@Override
- public void onConfigurationChanged(Configuration newConfig) {
- updateUI(mRadialTimePickerView.getCurrentItemShowing());
- }
-
- @Override
public Parcelable onSaveInstanceState(Parcelable superState) {
- return new SavedState(superState, getCurrentHour(), getCurrentMinute(),
- is24HourView(), getCurrentItemShowing());
+ return new SavedState(superState, getHour(), getMinute(),
+ is24Hour(), getCurrentItemShowing());
}
@Override
@@ -520,12 +510,6 @@
}
@Override
- public void setCurrentLocale(Locale locale) {
- super.setCurrentLocale(locale);
- mTempCalendar = Calendar.getInstance(locale);
- }
-
- @Override
public boolean dispatchPopulateAccessibilityEvent(AccessibilityEvent event) {
onPopulateAccessibilityEvent(event);
return true;
@@ -534,13 +518,13 @@
@Override
public void onPopulateAccessibilityEvent(AccessibilityEvent event) {
int flags = DateUtils.FORMAT_SHOW_TIME;
- if (mIs24HourView) {
+ if (mIs24Hour) {
flags |= DateUtils.FORMAT_24HOUR;
} else {
flags |= DateUtils.FORMAT_12HOUR;
}
- mTempCalendar.set(Calendar.HOUR_OF_DAY, getCurrentHour());
- mTempCalendar.set(Calendar.MINUTE, getCurrentMinute());
+ mTempCalendar.set(Calendar.HOUR_OF_DAY, getHour());
+ mTempCalendar.set(Calendar.MINUTE, getMinute());
String selectedDate = DateUtils.formatDateTime(mContext,
mTempCalendar.getTimeInMillis(), flags);
event.getText().add(selectedDate);
@@ -559,8 +543,7 @@
private void onTimeChanged() {
mDelegator.sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_SELECTED);
if (mOnTimeChangedListener != null) {
- mOnTimeChangedListener.onTimeChanged(mDelegator,
- getCurrentHour(), getCurrentMinute());
+ mOnTimeChangedListener.onTimeChanged(mDelegator, getHour(), getMinute());
}
}
@@ -666,7 +649,7 @@
}
if (mOnTimeChangedListener != null) {
- mOnTimeChangedListener.onTimeChanged(mDelegator, getCurrentHour(), getCurrentMinute());
+ mOnTimeChangedListener.onTimeChanged(mDelegator, getHour(), getMinute());
}
}
@@ -677,14 +660,14 @@
* @return a localized hour number
*/
private int getLocalizedHour(int hourOfDay) {
- if (!mIs24HourView) {
+ if (!mIs24Hour) {
// Convert to hour-of-am-pm.
hourOfDay %= 12;
}
if (!mHourFormatStartsAtZero && hourOfDay == 0) {
// Convert to clock-hour (either of-day or of-am-pm).
- hourOfDay = mIs24HourView ? 24 : 12;
+ hourOfDay = mIs24Hour ? 24 : 12;
}
return hourOfDay;
@@ -716,8 +699,8 @@
* separator as the character which is just after the hour marker in the returned pattern.
*/
private void updateHeaderSeparator() {
- final String bestDateTimePattern = DateFormat.getBestDateTimePattern(mCurrentLocale,
- (mIs24HourView) ? "Hm" : "hm");
+ final String bestDateTimePattern = DateFormat.getBestDateTimePattern(mLocale,
+ (mIs24Hour) ? "Hm" : "hm");
final String separatorText;
// See http://www.unicode.org/reports/tr35/tr35-dates.html for hour formats
final char[] hourFormats = {'H', 'h', 'K', 'k'};
@@ -819,14 +802,14 @@
private final Runnable mCommitHour = new Runnable() {
@Override
public void run() {
- setCurrentHour(mHourView.getValue());
+ setHour(mHourView.getValue());
}
};
private final Runnable mCommitMinute = new Runnable() {
@Override
public void run() {
- setCurrentMinute(mMinuteView.getValue());
+ setMinute(mMinuteView.getValue());
}
};
diff --git a/core/java/android/widget/TimePickerSpinnerDelegate.java b/core/java/android/widget/TimePickerSpinnerDelegate.java
index 8741cc3..2ed230b 100644
--- a/core/java/android/widget/TimePickerSpinnerDelegate.java
+++ b/core/java/android/widget/TimePickerSpinnerDelegate.java
@@ -17,7 +17,6 @@
package android.widget;
import android.content.Context;
-import android.content.res.Configuration;
import android.content.res.TypedArray;
import android.os.Parcel;
import android.os.Parcelable;
@@ -33,7 +32,6 @@
import com.android.internal.R;
import java.util.Calendar;
-import java.util.Locale;
import libcore.icu.LocaleData;
@@ -92,7 +90,7 @@
mHourSpinner.setOnValueChangedListener(new NumberPicker.OnValueChangeListener() {
public void onValueChange(NumberPicker spinner, int oldVal, int newVal) {
updateInputState();
- if (!is24HourView()) {
+ if (!is24Hour()) {
if ((oldVal == HOURS_IN_HALF_DAY - 1 && newVal == HOURS_IN_HALF_DAY) ||
(oldVal == HOURS_IN_HALF_DAY && newVal == HOURS_IN_HALF_DAY - 1)) {
mIsAm = !mIsAm;
@@ -124,14 +122,14 @@
int maxValue = mMinuteSpinner.getMaxValue();
if (oldVal == maxValue && newVal == minValue) {
int newHour = mHourSpinner.getValue() + 1;
- if (!is24HourView() && newHour == HOURS_IN_HALF_DAY) {
+ if (!is24Hour() && newHour == HOURS_IN_HALF_DAY) {
mIsAm = !mIsAm;
updateAmPmControl();
}
mHourSpinner.setValue(newHour);
} else if (oldVal == minValue && newVal == maxValue) {
int newHour = mHourSpinner.getValue() - 1;
- if (!is24HourView() && newHour == HOURS_IN_HALF_DAY - 1) {
+ if (!is24Hour() && newHour == HOURS_IN_HALF_DAY - 1) {
mIsAm = !mIsAm;
updateAmPmControl();
}
@@ -204,8 +202,8 @@
updateAmPmControl();
// set to current time
- setCurrentHour(mTempCalendar.get(Calendar.HOUR_OF_DAY));
- setCurrentMinute(mTempCalendar.get(Calendar.MINUTE));
+ setHour(mTempCalendar.get(Calendar.HOUR_OF_DAY));
+ setMinute(mTempCalendar.get(Calendar.MINUTE));
if (!isEnabled()) {
setEnabled(false);
@@ -221,7 +219,7 @@
}
private void getHourFormatData() {
- final String bestDateTimePattern = DateFormat.getBestDateTimePattern(mCurrentLocale,
+ final String bestDateTimePattern = DateFormat.getBestDateTimePattern(mLocale,
(mIs24HourView) ? "Hm" : "hm");
final int lengthPattern = bestDateTimePattern.length();
mHourWithTwoDigit = false;
@@ -241,7 +239,7 @@
}
private boolean isAmPmAtStart() {
- final String bestDateTimePattern = DateFormat.getBestDateTimePattern(mCurrentLocale,
+ final String bestDateTimePattern = DateFormat.getBestDateTimePattern(mLocale,
"hm" /* skeleton */);
return bestDateTimePattern.startsWith("a");
@@ -257,7 +255,7 @@
*/
private void setDividerText() {
final String skeleton = (mIs24HourView) ? "Hm" : "hm";
- final String bestDateTimePattern = DateFormat.getBestDateTimePattern(mCurrentLocale,
+ final String bestDateTimePattern = DateFormat.getBestDateTimePattern(mLocale,
skeleton);
final String separatorText;
int hourIndex = bestDateTimePattern.lastIndexOf('H');
@@ -279,16 +277,16 @@
}
@Override
- public void setCurrentHour(int currentHour) {
- setCurrentHour(currentHour, true);
+ public void setHour(int hour) {
+ setCurrentHour(hour, true);
}
private void setCurrentHour(int currentHour, boolean notifyTimeChanged) {
// why was Integer used in the first place?
- if (currentHour == getCurrentHour()) {
+ if (currentHour == getHour()) {
return;
}
- if (!is24HourView()) {
+ if (!is24Hour()) {
// convert [0,23] ordinal to wall clock display
if (currentHour >= HOURS_IN_HALF_DAY) {
mIsAm = false;
@@ -310,9 +308,9 @@
}
@Override
- public int getCurrentHour() {
+ public int getHour() {
int currentHour = mHourSpinner.getValue();
- if (is24HourView()) {
+ if (is24Hour()) {
return currentHour;
} else if (mIsAm) {
return currentHour % HOURS_IN_HALF_DAY;
@@ -322,28 +320,27 @@
}
@Override
- public void setCurrentMinute(int currentMinute) {
- if (currentMinute == getCurrentMinute()) {
+ public void setMinute(int minute) {
+ if (minute == getMinute()) {
return;
}
- mMinuteSpinner.setValue(currentMinute);
+ mMinuteSpinner.setValue(minute);
onTimeChanged();
}
@Override
- public int getCurrentMinute() {
+ public int getMinute() {
return mMinuteSpinner.getValue();
}
- @Override
- public void setIs24HourView(boolean is24HourView) {
- if (mIs24HourView == is24HourView) {
+ public void setIs24Hour(boolean is24Hour) {
+ if (mIs24HourView == is24Hour) {
return;
}
// cache the current hour since spinner range changes and BEFORE changing mIs24HourView!!
- int currentHour = getCurrentHour();
+ int currentHour = getHour();
// Order is important here.
- mIs24HourView = is24HourView;
+ mIs24HourView = is24Hour;
getHourFormatData();
updateHourControl();
// set value after spinner range is updated
@@ -353,7 +350,7 @@
}
@Override
- public boolean is24HourView() {
+ public boolean is24Hour() {
return mIs24HourView;
}
@@ -388,20 +385,15 @@
}
@Override
- public void onConfigurationChanged(Configuration newConfig) {
- setCurrentLocale(newConfig.locale);
- }
-
- @Override
public Parcelable onSaveInstanceState(Parcelable superState) {
- return new SavedState(superState, getCurrentHour(), getCurrentMinute());
+ return new SavedState(superState, getHour(), getMinute());
}
@Override
public void onRestoreInstanceState(Parcelable state) {
SavedState ss = (SavedState) state;
- setCurrentHour(ss.getHour());
- setCurrentMinute(ss.getMinute());
+ setHour(ss.getHour());
+ setMinute(ss.getMinute());
}
@Override
@@ -418,8 +410,8 @@
} else {
flags |= DateUtils.FORMAT_12HOUR;
}
- mTempCalendar.set(Calendar.HOUR_OF_DAY, getCurrentHour());
- mTempCalendar.set(Calendar.MINUTE, getCurrentMinute());
+ mTempCalendar.set(Calendar.HOUR_OF_DAY, getHour());
+ mTempCalendar.set(Calendar.MINUTE, getMinute());
String selectedDateUtterance = DateUtils.formatDateTime(mContext,
mTempCalendar.getTimeInMillis(), flags);
event.getText().add(selectedDateUtterance);
@@ -447,7 +439,7 @@
}
private void updateAmPmControl() {
- if (is24HourView()) {
+ if (is24Hour()) {
if (mAmPmSpinner != null) {
mAmPmSpinner.setVisibility(View.GONE);
} else {
@@ -466,27 +458,16 @@
mDelegator.sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_SELECTED);
}
- /**
- * Sets the current locale.
- *
- * @param locale The current locale.
- */
- @Override
- public void setCurrentLocale(Locale locale) {
- super.setCurrentLocale(locale);
- mTempCalendar = Calendar.getInstance(locale);
- }
-
private void onTimeChanged() {
mDelegator.sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_SELECTED);
if (mOnTimeChangedListener != null) {
- mOnTimeChangedListener.onTimeChanged(mDelegator, getCurrentHour(),
- getCurrentMinute());
+ mOnTimeChangedListener.onTimeChanged(mDelegator, getHour(),
+ getMinute());
}
}
private void updateHourControl() {
- if (is24HourView()) {
+ if (is24Hour()) {
// 'k' means 1-24 hour
if (mHourFormat == 'k') {
mHourSpinner.setMinValue(1);
@@ -509,7 +490,7 @@
}
private void updateMinuteControl() {
- if (is24HourView()) {
+ if (is24Hour()) {
mMinuteSpinnerInput.setImeOptions(EditorInfo.IME_ACTION_DONE);
} else {
mMinuteSpinnerInput.setImeOptions(EditorInfo.IME_ACTION_NEXT);
diff --git a/libs/hwui/Android.mk b/libs/hwui/Android.mk
index 4acad67..8565372 100644
--- a/libs/hwui/Android.mk
+++ b/libs/hwui/Android.mk
@@ -206,7 +206,9 @@
LOCAL_MODULE_TAGS := tests
LOCAL_SHARED_LIBRARIES := $(hwui_shared_libraries)
LOCAL_STATIC_LIBRARIES := libhwui_static_null_gpu
-LOCAL_CFLAGS := $(hwui_cflags)
+LOCAL_CFLAGS := \
+ $(hwui_cflags) \
+ -DHWUI_NULL_GPU
LOCAL_SRC_FILES += \
unit_tests/CanvasStateTests.cpp \
diff --git a/libs/hwui/BakedOpRenderer.cpp b/libs/hwui/BakedOpRenderer.cpp
index 1aa291f..d2d3285 100644
--- a/libs/hwui/BakedOpRenderer.cpp
+++ b/libs/hwui/BakedOpRenderer.cpp
@@ -35,11 +35,11 @@
LOG_ALWAYS_FATAL_IF(mRenderTarget.offscreenBuffer, "already has layer...");
OffscreenBuffer* buffer = mRenderState.layerPool().get(mRenderState, width, height);
- startRepaintLayer(buffer);
+ startRepaintLayer(buffer, Rect(width, height));
return buffer;
}
-void BakedOpRenderer::startRepaintLayer(OffscreenBuffer* offscreenBuffer) {
+void BakedOpRenderer::startRepaintLayer(OffscreenBuffer* offscreenBuffer, const Rect& repaintRect) {
LOG_ALWAYS_FATAL_IF(mRenderTarget.offscreenBuffer, "already has layer...");
mRenderTarget.offscreenBuffer = offscreenBuffer;
@@ -55,12 +55,10 @@
LOG_ALWAYS_FATAL_IF(glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE,
"framebuffer incomplete!");
- // Clear the FBO
- mRenderState.scissor().setEnabled(false);
- glClear(GL_COLOR_BUFFER_BIT);
-
// Change the viewport & ortho projection
setViewport(offscreenBuffer->viewportWidth, offscreenBuffer->viewportHeight);
+
+ clearColorBuffer(repaintRect);
}
void BakedOpRenderer::endLayer() {
@@ -74,16 +72,13 @@
mRenderTarget.frameBufferId = -1;
}
-void BakedOpRenderer::startFrame(uint32_t width, uint32_t height) {
+void BakedOpRenderer::startFrame(uint32_t width, uint32_t height, const Rect& repaintRect) {
mRenderState.bindFramebuffer(0);
setViewport(width, height);
mCaches.clearGarbage();
if (!mOpaque) {
- // TODO: partial invalidate!
- mRenderState.scissor().setEnabled(false);
- glClear(GL_COLOR_BUFFER_BIT);
- mHasDrawn = true;
+ clearColorBuffer(repaintRect);
}
}
@@ -113,6 +108,20 @@
mRenderState.blend().syncEnabled();
}
+void BakedOpRenderer::clearColorBuffer(const Rect& rect) {
+ if (Rect(mRenderTarget.viewportWidth, mRenderTarget.viewportHeight).contains(rect)) {
+ // Full viewport is being cleared - disable scissor
+ mRenderState.scissor().setEnabled(false);
+ } else {
+ // Requested rect is subset of viewport - scissor to it to avoid over-clearing
+ mRenderState.scissor().setEnabled(true);
+ mRenderState.scissor().set(rect.left, mRenderTarget.viewportHeight - rect.bottom,
+ rect.getWidth(), rect.getHeight());
+ }
+ glClear(GL_COLOR_BUFFER_BIT);
+ if (!mRenderTarget.frameBufferId) mHasDrawn = true;
+}
+
Texture* BakedOpRenderer::getTexture(const SkBitmap* bitmap) {
Texture* texture = mRenderState.assetAtlas().getEntryTexture(bitmap);
if (!texture) {
@@ -136,7 +145,7 @@
mRenderTarget.offscreenBuffer->region.orSelf(dirty);
}
mRenderState.render(glop, mRenderTarget.orthoMatrix);
- mHasDrawn = true;
+ if (!mRenderTarget.frameBufferId) mHasDrawn = true;
}
////////////////////////////////////////////////////////////////////////////////
@@ -217,7 +226,7 @@
paint.setAntiAlias(true); // want to use AlphaVertex
// The caller has made sure casterAlpha > 0.
- uint8_t ambientShadowAlpha = 128u; //TODO: mAmbientShadowAlpha;
+ uint8_t ambientShadowAlpha = renderer.getLightInfo().ambientShadowAlpha;
if (CC_UNLIKELY(Properties::overrideAmbientShadowStrength >= 0)) {
ambientShadowAlpha = Properties::overrideAmbientShadowStrength;
}
@@ -227,7 +236,7 @@
paint, VertexBufferRenderFlags::ShadowInterp);
}
- uint8_t spotShadowAlpha = 128u; //TODO: mSpotShadowAlpha;
+ uint8_t spotShadowAlpha = renderer.getLightInfo().spotShadowAlpha;
if (CC_UNLIKELY(Properties::overrideSpotShadowStrength >= 0)) {
spotShadowAlpha = Properties::overrideSpotShadowStrength;
}
@@ -240,12 +249,10 @@
void BakedOpDispatcher::onShadowOp(BakedOpRenderer& renderer, const ShadowOp& op, const BakedOpState& state) {
TessellationCache::vertexBuffer_pair_t buffers;
- Vector3 lightCenter = { 300, 300, 300 }; // TODO!
- float lightRadius = 150; // TODO!
-
renderer.caches().tessellationCache.getShadowBuffers(&state.computedState.transform,
op.localClipRect, op.casterAlpha >= 1.0f, op.casterPath,
- &op.shadowMatrixXY, &op.shadowMatrixZ, lightCenter, lightRadius,
+ &op.shadowMatrixXY, &op.shadowMatrixZ,
+ op.lightCenter, renderer.getLightInfo().lightRadius,
buffers);
renderShadow(renderer, state, op.casterAlpha, buffers.first, buffers.second);
diff --git a/libs/hwui/BakedOpRenderer.h b/libs/hwui/BakedOpRenderer.h
index d6d9cb1..29f9a6f 100644
--- a/libs/hwui/BakedOpRenderer.h
+++ b/libs/hwui/BakedOpRenderer.h
@@ -39,27 +39,39 @@
*/
class BakedOpRenderer {
public:
- BakedOpRenderer(Caches& caches, RenderState& renderState, bool opaque)
+ /**
+ * Position agnostic shadow lighting info. Used with all shadow ops in scene.
+ */
+ struct LightInfo {
+ float lightRadius = 0;
+ uint8_t ambientShadowAlpha = 0;
+ uint8_t spotShadowAlpha = 0;
+ };
+
+ BakedOpRenderer(Caches& caches, RenderState& renderState, bool opaque, const LightInfo& lightInfo)
: mRenderState(renderState)
, mCaches(caches)
- , mOpaque(opaque) {
+ , mOpaque(opaque)
+ , mLightInfo(lightInfo) {
}
RenderState& renderState() { return mRenderState; }
Caches& caches() { return mCaches; }
- void startFrame(uint32_t width, uint32_t height);
+ void startFrame(uint32_t width, uint32_t height, const Rect& repaintRect);
void endFrame();
OffscreenBuffer* startTemporaryLayer(uint32_t width, uint32_t height);
- void startRepaintLayer(OffscreenBuffer* offscreenBuffer);
+ void startRepaintLayer(OffscreenBuffer* offscreenBuffer, const Rect& repaintRect);
void endLayer();
Texture* getTexture(const SkBitmap* bitmap);
+ const LightInfo& getLightInfo() { return mLightInfo; }
void renderGlop(const BakedOpState& state, const Glop& glop);
bool didDraw() { return mHasDrawn; }
private:
void setViewport(uint32_t width, uint32_t height);
+ void clearColorBuffer(const Rect& clearRect);
RenderState& mRenderState;
Caches& mCaches;
@@ -75,6 +87,8 @@
uint32_t viewportHeight = 0;
Matrix4 orthoMatrix;
} mRenderTarget;
+
+ const LightInfo mLightInfo;
};
/**
diff --git a/libs/hwui/OpReorderer.cpp b/libs/hwui/OpReorderer.cpp
index 80efaed..b04f16f 100644
--- a/libs/hwui/OpReorderer.cpp
+++ b/libs/hwui/OpReorderer.cpp
@@ -18,6 +18,7 @@
#include "LayerUpdateQueue.h"
#include "RenderNode.h"
+#include "renderstate/OffscreenBufferPool.h"
#include "utils/FatVector.h"
#include "utils/PaintUtils.h"
@@ -33,8 +34,8 @@
public:
BatchBase(batchid_t batchId, BakedOpState* op, bool merging)
- : mBatchId(batchId)
- , mMerging(merging) {
+ : mBatchId(batchId)
+ , mMerging(merging) {
mBounds = op->computedState.clippedBounds;
mOps.push_back(op);
}
@@ -207,9 +208,10 @@
};
OpReorderer::LayerReorderer::LayerReorderer(uint32_t width, uint32_t height,
- const BeginLayerOp* beginLayerOp, RenderNode* renderNode)
+ const Rect& repaintRect, const BeginLayerOp* beginLayerOp, RenderNode* renderNode)
: width(width)
, height(height)
+ , repaintRect(repaintRect)
, offscreenBuffer(renderNode ? renderNode->getLayer() : nullptr)
, beginLayerOp(beginLayerOp)
, renderNode(renderNode) {}
@@ -309,15 +311,19 @@
OpReorderer::OpReorderer(const LayerUpdateQueue& layers, const SkRect& clip,
uint32_t viewportWidth, uint32_t viewportHeight,
- const std::vector< sp<RenderNode> >& nodes)
+ const std::vector< sp<RenderNode> >& nodes, const Vector3& lightCenter)
: mCanvasState(*this) {
ATRACE_NAME("prepare drawing commands");
- mLayerReorderers.emplace_back(viewportWidth, viewportHeight);
- mLayerStack.push_back(0);
+ mLayerReorderers.reserve(layers.entries().size());
+ mLayerStack.reserve(layers.entries().size());
+
+ // Prepare to defer Fbo0
+ mLayerReorderers.emplace_back(viewportWidth, viewportHeight, Rect(clip));
+ mLayerStack.push_back(0);
mCanvasState.initializeSaveStack(viewportWidth, viewportHeight,
clip.fLeft, clip.fTop, clip.fRight, clip.fBottom,
- Vector3());
+ lightCenter);
// Render all layers to be updated, in order. Defer in reverse order, so that they'll be
// updated in the order they're passed in (mLayerReorderers are issued to Renderer in reverse)
@@ -325,7 +331,8 @@
RenderNode* layerNode = layers.entries()[i].renderNode;
const Rect& layerDamage = layers.entries()[i].damage;
- saveForLayer(layerNode->getWidth(), layerNode->getHeight(), nullptr, layerNode);
+ saveForLayer(layerNode->getWidth(), layerNode->getHeight(),
+ layerDamage, nullptr, layerNode);
mCanvasState.writableSnapshot()->setClip(
layerDamage.left, layerDamage.top, layerDamage.right, layerDamage.bottom);
@@ -345,14 +352,17 @@
}
}
-OpReorderer::OpReorderer(int viewportWidth, int viewportHeight, const DisplayList& displayList)
+OpReorderer::OpReorderer(int viewportWidth, int viewportHeight, const DisplayList& displayList,
+ const Vector3& lightCenter)
: mCanvasState(*this) {
ATRACE_NAME("prepare drawing commands");
- mLayerReorderers.emplace_back(viewportWidth, viewportHeight);
+ // Prepare to defer Fbo0
+ mLayerReorderers.emplace_back(viewportWidth, viewportHeight,
+ Rect(viewportWidth, viewportHeight));
mLayerStack.push_back(0);
-
mCanvasState.initializeSaveStack(viewportWidth, viewportHeight,
- 0, 0, viewportWidth, viewportHeight, Vector3());
+ 0, 0, viewportWidth, viewportHeight, lightCenter);
+
deferImpl(displayList);
}
@@ -508,7 +518,8 @@
}
ShadowOp* shadowOp = new (mAllocator) ShadowOp(casterNodeOp, casterAlpha, casterPath,
- mCanvasState.getLocalClipBounds());
+ mCanvasState.getLocalClipBounds(),
+ mCanvasState.currentSnapshot()->getRelativeLightCenter());
BakedOpState* bakedOpState = BakedOpState::tryShadowOpConstruct(
mAllocator, *mCanvasState.currentSnapshot(), shadowOp);
if (CC_LIKELY(bakedOpState)) {
@@ -589,17 +600,36 @@
currentLayer().deferUnmergeableOp(mAllocator, bakedStateOp, OpBatchType::Vertices);
}
-void OpReorderer::saveForLayer(uint32_t layerWidth, uint32_t layerHeight,
+void OpReorderer::saveForLayer(uint32_t layerWidth, uint32_t layerHeight, const Rect& repaintRect,
const BeginLayerOp* beginLayerOp, RenderNode* renderNode) {
+ auto previous = mCanvasState.currentSnapshot();
mCanvasState.save(SkCanvas::kClip_SaveFlag | SkCanvas::kMatrix_SaveFlag);
mCanvasState.writableSnapshot()->transform->loadIdentity();
mCanvasState.writableSnapshot()->initializeViewport(layerWidth, layerHeight);
mCanvasState.writableSnapshot()->roundRectClipState = nullptr;
+ Vector3 lightCenter = previous->getRelativeLightCenter();
+ if (renderNode) {
+ Matrix4& inverse = renderNode->getLayer()->inverseTransformInWindow;
+ inverse.mapPoint3d(lightCenter);
+ } else {
+ // Combine all transforms used to present saveLayer content:
+ // parent content transform * canvas transform * bounds offset
+ Matrix4 contentTransform(*previous->transform);
+ contentTransform.multiply(beginLayerOp->localMatrix);
+ contentTransform.translate(beginLayerOp->unmappedBounds.left, beginLayerOp->unmappedBounds.top);
+
+ // inverse the total transform, to map light center into layer-relative space
+ Matrix4 inverse;
+ inverse.loadInverse(contentTransform);
+ inverse.mapPoint3d(lightCenter);
+ }
+ mCanvasState.writableSnapshot()->setRelativeLightCenter(lightCenter);
+
// create a new layer, and push its index on the stack
mLayerStack.push_back(mLayerReorderers.size());
- mLayerReorderers.emplace_back(layerWidth, layerHeight, beginLayerOp, renderNode);
+ mLayerReorderers.emplace_back(layerWidth, layerHeight, repaintRect, beginLayerOp, renderNode);
}
void OpReorderer::restoreForLayer() {
@@ -612,7 +642,7 @@
void OpReorderer::onBeginLayerOp(const BeginLayerOp& op) {
const uint32_t layerWidth = (uint32_t) op.unmappedBounds.getWidth();
const uint32_t layerHeight = (uint32_t) op.unmappedBounds.getHeight();
- saveForLayer(layerWidth, layerHeight, &op, nullptr);
+ saveForLayer(layerWidth, layerHeight, Rect(layerWidth, layerHeight), &op, nullptr);
}
void OpReorderer::onEndLayerOp(const EndLayerOp& /* ignored */) {
diff --git a/libs/hwui/OpReorderer.h b/libs/hwui/OpReorderer.h
index 2c30f0d..09d5cbc 100644
--- a/libs/hwui/OpReorderer.h
+++ b/libs/hwui/OpReorderer.h
@@ -67,13 +67,13 @@
class LayerReorderer {
public:
// Create LayerReorderer for Fbo0
- LayerReorderer(uint32_t width, uint32_t height)
- : LayerReorderer(width, height, nullptr, nullptr) {};
+ LayerReorderer(uint32_t width, uint32_t height, const Rect& repaintRect)
+ : LayerReorderer(width, height, repaintRect, nullptr, nullptr) {};
// Create LayerReorderer for an offscreen layer, where beginLayerOp is present for a
// saveLayer, renderNode is present for a HW layer.
LayerReorderer(uint32_t width, uint32_t height,
- const BeginLayerOp* beginLayerOp, RenderNode* renderNode);
+ const Rect& repaintRect, const BeginLayerOp* beginLayerOp, RenderNode* renderNode);
// iterate back toward target to see if anything drawn since should overlap the new op
// if no target, merging ops still iterate to find similar batch to insert after
@@ -101,6 +101,7 @@
const uint32_t width;
const uint32_t height;
+ const Rect repaintRect;
OffscreenBuffer* offscreenBuffer;
const BeginLayerOp* beginLayerOp;
const RenderNode* renderNode;
@@ -116,14 +117,15 @@
// Maps batch ids to the most recent *non-merging* batch of that id
OpBatch* mBatchLookup[OpBatchType::Count] = { nullptr };
-
};
+
public:
OpReorderer(const LayerUpdateQueue& layers, const SkRect& clip,
uint32_t viewportWidth, uint32_t viewportHeight,
- const std::vector< sp<RenderNode> >& nodes);
+ const std::vector< sp<RenderNode> >& nodes, const Vector3& lightCenter);
- OpReorderer(int viewportWidth, int viewportHeight, const DisplayList& displayList);
+ OpReorderer(int viewportWidth, int viewportHeight, const DisplayList& displayList,
+ const Vector3& lightCenter);
virtual ~OpReorderer() {}
@@ -153,7 +155,7 @@
LayerReorderer& layer = mLayerReorderers[i];
if (layer.renderNode) {
// cached HW layer - can't skip layer if empty
- renderer.startRepaintLayer(layer.offscreenBuffer);
+ renderer.startRepaintLayer(layer.offscreenBuffer, layer.repaintRect);
layer.replayBakedOpsImpl((void*)&renderer, receivers);
renderer.endLayer();
} else if (!layer.empty()) { // save layer - skip entire layer if empty
@@ -164,7 +166,7 @@
}
const LayerReorderer& fbo0 = mLayerReorderers[0];
- renderer.startFrame(fbo0.width, fbo0.height);
+ renderer.startFrame(fbo0.width, fbo0.height, fbo0.repaintRect);
fbo0.replayBakedOpsImpl((void*)&renderer, receivers);
renderer.endFrame();
}
@@ -188,7 +190,7 @@
Positive
};
void saveForLayer(uint32_t layerWidth, uint32_t layerHeight,
- const BeginLayerOp* beginLayerOp, RenderNode* renderNode);
+ const Rect& repaintRect, const BeginLayerOp* beginLayerOp, RenderNode* renderNode);
void restoreForLayer();
LayerReorderer& currentLayer() { return mLayerReorderers[mLayerStack.back()]; }
diff --git a/libs/hwui/RecordedOp.h b/libs/hwui/RecordedOp.h
index bb7a0a7c..ef05367 100644
--- a/libs/hwui/RecordedOp.h
+++ b/libs/hwui/RecordedOp.h
@@ -17,10 +17,11 @@
#ifndef ANDROID_HWUI_RECORDED_OP_H
#define ANDROID_HWUI_RECORDED_OP_H
-#include "utils/LinearAllocator.h"
-#include "Rect.h"
#include "Matrix.h"
+#include "Rect.h"
#include "RenderNode.h"
+#include "utils/LinearAllocator.h"
+#include "Vector.h"
#include "SkXfermode.h"
@@ -116,15 +117,17 @@
* Uses invalid/empty bounds and matrix since ShadowOp bounds aren't known at defer time,
* and are resolved dynamically, and transform isn't needed.
*
- * State construction handles these properties specially.
+ * State construction handles these properties specially, ignoring matrix/bounds.
*/
struct ShadowOp : RecordedOp {
- ShadowOp(const RenderNodeOp& casterOp, float casterAlpha, const SkPath* casterPath, const Rect& clipRect)
+ ShadowOp(const RenderNodeOp& casterOp, float casterAlpha, const SkPath* casterPath,
+ const Rect& clipRect, const Vector3& lightCenter)
: RecordedOp(RecordedOpId::ShadowOp, Rect(), Matrix4::identity(), clipRect, nullptr)
, shadowMatrixXY(casterOp.localMatrix)
, shadowMatrixZ(casterOp.localMatrix)
, casterAlpha(casterAlpha)
- , casterPath(casterPath) {
+ , casterPath(casterPath)
+ , lightCenter(lightCenter) {
const RenderNode& node = *casterOp.renderNode;
node.applyViewPropertyTransforms(shadowMatrixXY, false);
node.applyViewPropertyTransforms(shadowMatrixZ, true);
@@ -133,6 +136,7 @@
Matrix4 shadowMatrixZ;
const float casterAlpha;
const SkPath* casterPath;
+ const Vector3 lightCenter;
};
struct SimpleRectsOp : RecordedOp { // Filled, no AA (TODO: better name?)
diff --git a/libs/hwui/RecordingCanvas.cpp b/libs/hwui/RecordingCanvas.cpp
index e988555..6ab253c 100644
--- a/libs/hwui/RecordingCanvas.cpp
+++ b/libs/hwui/RecordingCanvas.cpp
@@ -156,7 +156,7 @@
snapshot.flags |= Snapshot::kFlagFboTarget | Snapshot::kFlagIsFboLayer;
snapshot.initializeViewport(untransformedBounds.getWidth(), untransformedBounds.getHeight());
- snapshot.resetTransform(-untransformedBounds.left, -untransformedBounds.top, 0.0f);
+ snapshot.transform->loadTranslate(-untransformedBounds.left, -untransformedBounds.top, 0.0f);
Rect clip = layerBounds;
clip.translate(-untransformedBounds.left, -untransformedBounds.top);
diff --git a/libs/hwui/RenderNode.cpp b/libs/hwui/RenderNode.cpp
index e177f9a..2713f46 100644
--- a/libs/hwui/RenderNode.cpp
+++ b/libs/hwui/RenderNode.cpp
@@ -326,15 +326,11 @@
return;
}
- if (transformUpdateNeeded) {
+ if (transformUpdateNeeded && mLayer) {
// update the transform in window of the layer to reset its origin wrt light source position
Matrix4 windowTransform;
info.damageAccumulator->computeCurrentTransform(&windowTransform);
-#if HWUI_NEW_OPS
- // TODO: update layer transform (perhaps as part of enqueueLayerWithDamage)
-#else
mLayer->setWindowTransform(windowTransform);
-#endif
}
#if HWUI_NEW_OPS
diff --git a/libs/hwui/Snapshot.cpp b/libs/hwui/Snapshot.cpp
index 0a58f4b..2f535bb 100644
--- a/libs/hwui/Snapshot.cpp
+++ b/libs/hwui/Snapshot.cpp
@@ -130,6 +130,9 @@
///////////////////////////////////////////////////////////////////////////////
void Snapshot::resetTransform(float x, float y, float z) {
+#if HWUI_NEW_OPS
+ LOG_ALWAYS_FATAL("not supported - light center managed differently");
+#else
// before resetting, map current light pos with inverse of current transform
Vector3 center = mRelativeLightCenter;
mat4 inverse;
@@ -139,6 +142,7 @@
transform = &mTransformRoot;
transform->loadTranslate(x, y, z);
+#endif
}
void Snapshot::buildScreenSpaceTransform(Matrix4* outTransform) const {
diff --git a/libs/hwui/microbench/OpReordererBench.cpp b/libs/hwui/microbench/OpReordererBench.cpp
index 7b8d0e5..b24858e 100644
--- a/libs/hwui/microbench/OpReordererBench.cpp
+++ b/libs/hwui/microbench/OpReordererBench.cpp
@@ -48,7 +48,7 @@
void BM_OpReorderer_defer::Run(int iters) {
StartBenchmarkTiming();
for (int i = 0; i < iters; i++) {
- OpReorderer reorderer(200, 200, *sReorderingDisplayList);
+ OpReorderer reorderer(200, 200, *sReorderingDisplayList, (Vector3) { 100, 100, 100 });
MicroBench::DoNotOptimize(&reorderer);
}
StopBenchmarkTiming();
@@ -59,11 +59,13 @@
TestUtils::runOnRenderThread([this, iters](renderthread::RenderThread& thread) {
RenderState& renderState = thread.renderState();
Caches& caches = Caches::getInstance();
+ BakedOpRenderer::LightInfo lightInfo = { 50.0f, 128, 128 };
+
StartBenchmarkTiming();
for (int i = 0; i < iters; i++) {
- OpReorderer reorderer(200, 200, *sReorderingDisplayList);
+ OpReorderer reorderer(200, 200, *sReorderingDisplayList, (Vector3) { 100, 100, 100 });
- BakedOpRenderer renderer(caches, renderState, true);
+ BakedOpRenderer renderer(caches, renderState, true, lightInfo);
reorderer.replayBakedOps<BakedOpDispatcher>(renderer);
MicroBench::DoNotOptimize(&renderer);
}
diff --git a/libs/hwui/renderstate/OffscreenBufferPool.h b/libs/hwui/renderstate/OffscreenBufferPool.h
index f0fd82d..fac6c35 100644
--- a/libs/hwui/renderstate/OffscreenBufferPool.h
+++ b/libs/hwui/renderstate/OffscreenBufferPool.h
@@ -34,6 +34,11 @@
* Lightweight alternative to Layer. Owns the persistent state of an offscreen render target, and
* encompasses enough information to draw it back on screen (minus paint properties, which are held
* by LayerOp).
+ *
+ * Has two distinct sizes - viewportWidth/viewportHeight describe content area,
+ * texture.width/.height are actual allocated texture size. Texture will tend to be larger than the
+ * viewport bounds, since textures are always allocated with width / height as a multiple of 64, for
+ * the purpose of improving reuse.
*/
class OffscreenBuffer {
public:
@@ -44,11 +49,17 @@
// must be called prior to rendering, to construct/update vertex buffer
void updateMeshFromRegion();
+ // Set by RenderNode for HW layers, TODO for clipped saveLayers
+ void setWindowTransform(const Matrix4& transform) {
+ inverseTransformInWindow.loadInverse(transform);
+ }
+
static uint32_t computeIdealDimension(uint32_t dimension);
uint32_t getSizeInBytes() { return texture.width * texture.height * 4; }
RenderState& renderState;
+
uint32_t viewportWidth;
uint32_t viewportHeight;
Texture texture;
@@ -56,6 +67,10 @@
// Portion of layer that has been drawn to. Used to minimize drawing area when
// drawing back to screen / parent FBO.
Region region;
+
+ Matrix4 inverseTransformInWindow;
+
+ // vbo / size of mesh
GLsizei elementCount = 0;
GLuint vbo = 0;
};
diff --git a/libs/hwui/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp
index f094b2d..89cadea 100644
--- a/libs/hwui/renderthread/CanvasContext.cpp
+++ b/libs/hwui/renderthread/CanvasContext.cpp
@@ -31,7 +31,6 @@
#include "utils/TimeUtils.h"
#if HWUI_NEW_OPS
-#include "BakedOpRenderer.h"
#include "OpReorderer.h"
#endif
@@ -150,13 +149,23 @@
// TODO: don't pass viewport size, it's automatic via EGL
void CanvasContext::setup(int width, int height, float lightRadius,
uint8_t ambientShadowAlpha, uint8_t spotShadowAlpha) {
+#if HWUI_NEW_OPS
+ mLightInfo.lightRadius = lightRadius;
+ mLightInfo.ambientShadowAlpha = ambientShadowAlpha;
+ mLightInfo.spotShadowAlpha = spotShadowAlpha;
+#else
if (!mCanvas) return;
mCanvas->initLight(lightRadius, ambientShadowAlpha, spotShadowAlpha);
+#endif
}
void CanvasContext::setLightCenter(const Vector3& lightCenter) {
+#if HWUI_NEW_OPS
+ mLightCenter = lightCenter;
+#else
if (!mCanvas) return;
mCanvas->setLightCenter(lightCenter);
+#endif
}
void CanvasContext::setOpaque(bool opaque) {
@@ -340,9 +349,11 @@
mEglManager.damageFrame(frame, dirty);
#if HWUI_NEW_OPS
- OpReorderer reorderer(mLayerUpdateQueue, dirty, frame.width(), frame.height(), mRenderNodes);
+ OpReorderer reorderer(mLayerUpdateQueue, dirty, frame.width(), frame.height(),
+ mRenderNodes, mLightCenter);
mLayerUpdateQueue.clear();
- BakedOpRenderer renderer(Caches::getInstance(), mRenderThread.renderState(), mOpaque);
+ BakedOpRenderer renderer(Caches::getInstance(), mRenderThread.renderState(),
+ mOpaque, mLightInfo);
// TODO: profiler().draw(mCanvas);
reorderer.replayBakedOps<BakedOpDispatcher>(renderer);
@@ -576,8 +587,12 @@
// purposes when the frame is actually drawn
node->setPropertyFieldsDirty(RenderNode::GENERIC);
+#if HWUI_NEW_OPS
+ LOG_ALWAYS_FATAL("unsupported");
+#else
mCanvas->markLayersAsBuildLayers();
mCanvas->flushLayerUpdates();
+#endif
node->incStrong(nullptr);
mPrefetechedLayers.insert(node);
diff --git a/libs/hwui/renderthread/CanvasContext.h b/libs/hwui/renderthread/CanvasContext.h
index d656014..c3cfc94 100644
--- a/libs/hwui/renderthread/CanvasContext.h
+++ b/libs/hwui/renderthread/CanvasContext.h
@@ -27,6 +27,10 @@
#include "renderthread/RenderTask.h"
#include "renderthread/RenderThread.h"
+#if HWUI_NEW_OPS
+#include "BakedOpRenderer.h"
+#endif
+
#include <cutils/compiler.h>
#include <EGL/egl.h>
#include <SkBitmap.h>
@@ -165,6 +169,11 @@
bool mOpaque;
OpenGLRenderer* mCanvas = nullptr;
+#if HWUI_NEW_OPS
+ BakedOpRenderer::LightInfo mLightInfo;
+ Vector3 mLightCenter = { 0, 0, 0 };
+#endif
+
bool mHaveNewSurface = false;
DamageAccumulator mDamageAccumulator;
LayerUpdateQueue mLayerUpdateQueue;
diff --git a/libs/hwui/unit_tests/OpReordererTests.cpp b/libs/hwui/unit_tests/OpReordererTests.cpp
index 07080a2..a8c9bba 100644
--- a/libs/hwui/unit_tests/OpReordererTests.cpp
+++ b/libs/hwui/unit_tests/OpReordererTests.cpp
@@ -29,6 +29,14 @@
namespace uirenderer {
LayerUpdateQueue sEmptyLayerUpdateQueue;
+Vector3 sLightCenter = {100, 100, 100};
+
+static std::vector<sp<RenderNode>> createSyncedNodeList(sp<RenderNode>& node) {
+ TestUtils::syncHierarchyPropertiesAndDisplayList(node);
+ std::vector<sp<RenderNode>> vec;
+ vec.emplace_back(node);
+ return vec;
+}
/**
* Virtual class implemented by each test to redirect static operation / state transitions to
@@ -48,13 +56,13 @@
ADD_FAILURE() << "Layer creation not expected in this test";
return nullptr;
}
- virtual void startRepaintLayer(OffscreenBuffer*) {
+ virtual void startRepaintLayer(OffscreenBuffer*, const Rect& repaintRect) {
ADD_FAILURE() << "Layer repaint not expected in this test";
}
virtual void endLayer() {
ADD_FAILURE() << "Layer updates not expected in this test";
}
- virtual void startFrame(uint32_t width, uint32_t height) {}
+ virtual void startFrame(uint32_t width, uint32_t height, const Rect& repaintRect) {}
virtual void endFrame() {}
// define virtual defaults for direct
@@ -87,7 +95,7 @@
TEST(OpReorderer, simple) {
class SimpleTestRenderer : public TestRendererBase {
public:
- void startFrame(uint32_t width, uint32_t height) override {
+ void startFrame(uint32_t width, uint32_t height, const Rect& repaintRect) override {
EXPECT_EQ(0, mIndex++);
EXPECT_EQ(100u, width);
EXPECT_EQ(200u, height);
@@ -108,7 +116,7 @@
canvas.drawRect(0, 0, 100, 200, SkPaint());
canvas.drawBitmap(bitmap, 10, 10, nullptr);
});
- OpReorderer reorderer(100, 200, *dl);
+ OpReorderer reorderer(100, 200, *dl, sLightCenter);
SimpleTestRenderer renderer;
reorderer.replayBakedOps<TestDispatcher>(renderer);
@@ -122,7 +130,7 @@
canvas.drawRect(0, 0, 400, 400, SkPaint());
canvas.restore();
});
- OpReorderer reorderer(200, 200, *dl);
+ OpReorderer reorderer(200, 200, *dl, sLightCenter);
FailRenderer renderer;
reorderer.replayBakedOps<TestDispatcher>(renderer);
@@ -154,7 +162,7 @@
canvas.restore();
});
- OpReorderer reorderer(200, 200, *dl);
+ OpReorderer reorderer(200, 200, *dl, sLightCenter);
SimpleBatchingTestRenderer renderer;
reorderer.replayBakedOps<TestDispatcher>(renderer);
@@ -198,13 +206,8 @@
canvas.restore();
});
- TestUtils::syncHierarchyPropertiesAndDisplayList(parent);
-
- std::vector< sp<RenderNode> > nodes;
- nodes.push_back(parent.get());
-
- OpReorderer reorderer(sEmptyLayerUpdateQueue,
- SkRect::MakeWH(200, 200), 200, 200, nodes);
+ OpReorderer reorderer(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
+ createSyncedNodeList(parent), sLightCenter);
RenderNodeTestRenderer renderer;
reorderer.replayBakedOps<TestDispatcher>(renderer);
@@ -225,13 +228,10 @@
SkBitmap bitmap = TestUtils::createSkBitmap(200, 200);
canvas.drawBitmap(bitmap, 0, 0, nullptr);
});
- TestUtils::syncHierarchyPropertiesAndDisplayList(node);
- std::vector< sp<RenderNode> > nodes;
- nodes.push_back(node.get());
OpReorderer reorderer(sEmptyLayerUpdateQueue,
SkRect::MakeLTRB(10, 20, 30, 40), // clip to small area, should see in receiver
- 200, 200, nodes);
+ 200, 200, createSyncedNodeList(node), sLightCenter);
ClippedTestRenderer renderer;
reorderer.replayBakedOps<TestDispatcher>(renderer);
@@ -273,7 +273,7 @@
canvas.restore();
});
- OpReorderer reorderer(200, 200, *dl);
+ OpReorderer reorderer(200, 200, *dl, sLightCenter);
SaveLayerSimpleTestRenderer renderer;
reorderer.replayBakedOps<TestDispatcher>(renderer);
@@ -305,7 +305,7 @@
int index = mIndex++;
EXPECT_TRUE(index == 2 || index == 6);
}
- void startFrame(uint32_t width, uint32_t height) override {
+ void startFrame(uint32_t width, uint32_t height, const Rect& repaintRect) override {
EXPECT_EQ(7, mIndex++);
}
void endFrame() override {
@@ -344,7 +344,7 @@
canvas.restore();
});
- OpReorderer reorderer(800, 800, *dl);
+ OpReorderer reorderer(800, 800, *dl, sLightCenter);
SaveLayerNestedTestRenderer renderer;
reorderer.replayBakedOps<TestDispatcher>(renderer);
@@ -363,19 +363,21 @@
canvas.restore();
canvas.restore();
});
- OpReorderer reorderer(200, 200, *dl);
+ OpReorderer reorderer(200, 200, *dl, sLightCenter);
FailRenderer renderer;
// should see no ops, even within the layer, since the layer should be rejected
reorderer.replayBakedOps<TestDispatcher>(renderer);
}
-TEST(OpReorderer, hwLayerSimple) {
+RENDERTHREAD_TEST(OpReorderer, hwLayerSimple) {
class HwLayerSimpleTestRenderer : public TestRendererBase {
public:
- void startRepaintLayer(OffscreenBuffer* offscreenBuffer) override {
+ void startRepaintLayer(OffscreenBuffer* offscreenBuffer, const Rect& repaintRect) override {
EXPECT_EQ(0, mIndex++);
- EXPECT_EQ(offscreenBuffer, (OffscreenBuffer*) 0x0124);
+ EXPECT_EQ(100u, offscreenBuffer->viewportWidth);
+ EXPECT_EQ(100u, offscreenBuffer->viewportHeight);
+ EXPECT_EQ(Rect(25, 25, 75, 75), repaintRect);
}
void onRectOp(const RectOp& op, const BakedOpState& state) override {
EXPECT_EQ(1, mIndex++);
@@ -389,7 +391,7 @@
void endLayer() override {
EXPECT_EQ(2, mIndex++);
}
- void startFrame(uint32_t width, uint32_t height) override {
+ void startFrame(uint32_t width, uint32_t height, const Rect& repaintRect) override {
EXPECT_EQ(3, mIndex++);
}
void onLayerOp(const LayerOp& op, const BakedOpState& state) override {
@@ -405,29 +407,29 @@
paint.setColor(SK_ColorWHITE);
canvas.drawRect(0, 0, 100, 100, paint);
}, TestUtils::getHwLayerSetupCallback());
- OffscreenBuffer** bufferHandle = node->getLayerHandle();
- *bufferHandle = (OffscreenBuffer*) 0x0124;
+ OffscreenBuffer** layerHandle = node->getLayerHandle();
- TestUtils::syncHierarchyPropertiesAndDisplayList(node);
+ // create RenderNode's layer here in same way prepareTree would
+ OffscreenBuffer layer(renderThread.renderState(), Caches::getInstance(), 100, 100);
+ *layerHandle = &layer;
- std::vector< sp<RenderNode> > nodes;
- nodes.push_back(node.get());
+ auto syncedNodeList = createSyncedNodeList(node);
// only enqueue partial damage
- LayerUpdateQueue layerUpdateQueue;
+ LayerUpdateQueue layerUpdateQueue; // Note: enqueue damage post-sync, so bounds are valid
layerUpdateQueue.enqueueLayerWithDamage(node.get(), Rect(25, 25, 75, 75));
- OpReorderer reorderer(layerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200, nodes);
-
+ OpReorderer reorderer(layerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
+ syncedNodeList, sLightCenter);
HwLayerSimpleTestRenderer renderer;
reorderer.replayBakedOps<TestDispatcher>(renderer);
EXPECT_EQ(6, renderer.getIndex());
// clean up layer pointer, so we can safely destruct RenderNode
- *bufferHandle = nullptr;
+ *layerHandle = nullptr;
}
-TEST(OpReorderer, hwLayerComplex) {
+RENDERTHREAD_TEST(OpReorderer, hwLayerComplex) {
/* parentLayer { greyRect, saveLayer { childLayer { whiteRect } } } will play back as:
* - startRepaintLayer(child), rect(grey), endLayer
* - startTemporaryLayer, drawLayer(child), endLayer
@@ -440,14 +442,16 @@
EXPECT_EQ(3, mIndex++); // savelayer first
return (OffscreenBuffer*)0xabcd;
}
- void startRepaintLayer(OffscreenBuffer* offscreenBuffer) override {
+ void startRepaintLayer(OffscreenBuffer* offscreenBuffer, const Rect& repaintRect) override {
int index = mIndex++;
if (index == 0) {
// starting inner layer
- EXPECT_EQ((OffscreenBuffer*)0x4567, offscreenBuffer);
+ EXPECT_EQ(100u, offscreenBuffer->viewportWidth);
+ EXPECT_EQ(100u, offscreenBuffer->viewportHeight);
} else if (index == 6) {
// starting outer layer
- EXPECT_EQ((OffscreenBuffer*)0x0123, offscreenBuffer);
+ EXPECT_EQ(200u, offscreenBuffer->viewportWidth);
+ EXPECT_EQ(200u, offscreenBuffer->viewportHeight);
} else { ADD_FAILURE(); }
}
void onRectOp(const RectOp& op, const BakedOpState& state) override {
@@ -464,17 +468,20 @@
int index = mIndex++;
EXPECT_TRUE(index == 2 || index == 5 || index == 9);
}
- void startFrame(uint32_t width, uint32_t height) override {
+ void startFrame(uint32_t width, uint32_t height, const Rect& repaintRect) override {
EXPECT_EQ(10, mIndex++);
}
void onLayerOp(const LayerOp& op, const BakedOpState& state) override {
+ OffscreenBuffer* layer = *op.layerHandle;
int index = mIndex++;
if (index == 4) {
- EXPECT_EQ((OffscreenBuffer*)0x4567, *op.layerHandle);
+ EXPECT_EQ(100u, layer->viewportWidth);
+ EXPECT_EQ(100u, layer->viewportHeight);
} else if (index == 8) {
EXPECT_EQ((OffscreenBuffer*)0xabcd, *op.layerHandle);
} else if (index == 11) {
- EXPECT_EQ((OffscreenBuffer*)0x0123, *op.layerHandle);
+ EXPECT_EQ(200u, layer->viewportWidth);
+ EXPECT_EQ(200u, layer->viewportHeight);
} else { ADD_FAILURE(); }
}
void endFrame() override {
@@ -488,7 +495,8 @@
paint.setColor(SK_ColorWHITE);
canvas.drawRect(0, 0, 100, 100, paint);
}, TestUtils::getHwLayerSetupCallback());
- *(child->getLayerHandle()) = (OffscreenBuffer*) 0x4567;
+ OffscreenBuffer childLayer(renderThread.renderState(), Caches::getInstance(), 100, 100);
+ *(child->getLayerHandle()) = &childLayer;
RenderNode* childPtr = child.get();
auto parent = TestUtils::createNode<RecordingCanvas>(0, 0, 200, 200,
@@ -501,18 +509,17 @@
canvas.drawRenderNode(childPtr);
canvas.restore();
}, TestUtils::getHwLayerSetupCallback());
- *(parent->getLayerHandle()) = (OffscreenBuffer*) 0x0123;
+ OffscreenBuffer parentLayer(renderThread.renderState(), Caches::getInstance(), 200, 200);
+ *(parent->getLayerHandle()) = &parentLayer;
- TestUtils::syncHierarchyPropertiesAndDisplayList(parent);
+ auto syncedList = createSyncedNodeList(parent);
- std::vector< sp<RenderNode> > nodes;
- nodes.push_back(parent.get());
-
- LayerUpdateQueue layerUpdateQueue;
+ LayerUpdateQueue layerUpdateQueue; // Note: enqueue damage post-sync, so bounds are valid
layerUpdateQueue.enqueueLayerWithDamage(child.get(), Rect(100, 100));
layerUpdateQueue.enqueueLayerWithDamage(parent.get(), Rect(200, 200));
- OpReorderer reorderer(layerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200, nodes);
+ OpReorderer reorderer(layerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
+ syncedList, sLightCenter);
HwLayerComplexTestRenderer renderer;
reorderer.replayBakedOps<TestDispatcher>(renderer);
@@ -561,58 +568,184 @@
drawOrderedRect(&canvas, 8);
drawOrderedNode(&canvas, 9, -10.0f); // in reorder=false at this point, so played inorder
});
- TestUtils::syncHierarchyPropertiesAndDisplayList(parent);
-
- std::vector< sp<RenderNode> > nodes;
- nodes.push_back(parent.get());
- OpReorderer reorderer(sEmptyLayerUpdateQueue, SkRect::MakeWH(100, 100), 100, 100, nodes);
+ OpReorderer reorderer(sEmptyLayerUpdateQueue, SkRect::MakeWH(100, 100), 100, 100,
+ createSyncedNodeList(parent), sLightCenter);
ZReorderTestRenderer renderer;
reorderer.replayBakedOps<TestDispatcher>(renderer);
EXPECT_EQ(10, renderer.getIndex());
};
+// creates a 100x100 shadow casting node with provided translationZ
+static sp<RenderNode> createWhiteRectShadowCaster(float translationZ) {
+ return TestUtils::createNode<RecordingCanvas>(0, 0, 100, 100,
+ [](RecordingCanvas& canvas) {
+ SkPaint paint;
+ paint.setColor(SK_ColorWHITE);
+ canvas.drawRect(0, 0, 100, 100, paint);
+ }, [translationZ] (RenderProperties& properties) {
+ properties.setTranslationZ(translationZ);
+ properties.mutableOutline().setRoundRect(0, 0, 100, 100, 0.0f, 1.0f);
+ return RenderNode::GENERIC | RenderNode::TRANSLATION_Z;
+ });
+}
+
TEST(OpReorderer, shadow) {
class ShadowTestRenderer : public TestRendererBase {
public:
void onShadowOp(const ShadowOp& op, const BakedOpState& state) override {
EXPECT_EQ(0, mIndex++);
+ EXPECT_FLOAT_EQ(1.0f, op.casterAlpha);
+ EXPECT_TRUE(op.casterPath->isRect(nullptr));
+ EXPECT_MATRIX_APPROX_EQ(Matrix4::identity(), op.shadowMatrixXY);
+
+ Matrix4 expectedZ;
+ expectedZ.loadTranslate(0, 0, 5);
+ EXPECT_MATRIX_APPROX_EQ(expectedZ, op.shadowMatrixZ);
}
void onRectOp(const RectOp& op, const BakedOpState& state) override {
EXPECT_EQ(1, mIndex++);
}
};
- sp<RenderNode> caster = TestUtils::createNode<RecordingCanvas>(0, 0, 100, 100,
- [](RecordingCanvas& canvas) {
- SkPaint paint;
- paint.setColor(SK_ColorWHITE);
- canvas.drawRect(0, 0, 100, 100, paint);
- }, [] (RenderProperties& properties) {
- properties.setTranslationZ(5.0f);
- properties.mutableOutline().setRoundRect(0, 0, 100, 100, 5, 1.0f);
- return RenderNode::GENERIC | RenderNode::TRANSLATION_Z;
- });
sp<RenderNode> parent = TestUtils::createNode<RecordingCanvas>(0, 0, 200, 200,
- [&caster] (RecordingCanvas& canvas) {
+ [] (RecordingCanvas& canvas) {
canvas.insertReorderBarrier(true);
- canvas.drawRenderNode(caster.get());
+ canvas.drawRenderNode(createWhiteRectShadowCaster(5.0f).get());
});
- TestUtils::syncHierarchyPropertiesAndDisplayList(parent);
-
- std::vector< sp<RenderNode> > nodes;
- nodes.push_back(parent.get());
-
- OpReorderer reorderer(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200, nodes);
+ OpReorderer reorderer(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
+ createSyncedNodeList(parent), sLightCenter);
ShadowTestRenderer renderer;
reorderer.replayBakedOps<TestDispatcher>(renderer);
EXPECT_EQ(2, renderer.getIndex());
}
-static void testProperty(
- TestUtils::PropSetupCallback propSetupCallback,
+TEST(OpReorderer, shadowSaveLayer) {
+ class ShadowSaveLayerTestRenderer : public TestRendererBase {
+ public:
+ OffscreenBuffer* startTemporaryLayer(uint32_t width, uint32_t height) override {
+ EXPECT_EQ(0, mIndex++);
+ return nullptr;
+ }
+ void onShadowOp(const ShadowOp& op, const BakedOpState& state) override {
+ EXPECT_EQ(1, mIndex++);
+ EXPECT_FLOAT_EQ(50, op.lightCenter.x);
+ EXPECT_FLOAT_EQ(40, op.lightCenter.y);
+ }
+ void onRectOp(const RectOp& op, const BakedOpState& state) override {
+ EXPECT_EQ(2, mIndex++);
+ }
+ void endLayer() override {
+ EXPECT_EQ(3, mIndex++);
+ }
+ void onLayerOp(const LayerOp& op, const BakedOpState& state) override {
+ EXPECT_EQ(4, mIndex++);
+ }
+ };
+
+ sp<RenderNode> parent = TestUtils::createNode<RecordingCanvas>(0, 0, 200, 200,
+ [] (RecordingCanvas& canvas) {
+ // save/restore outside of reorderBarrier, so they don't get moved out of place
+ canvas.translate(20, 10);
+ int count = canvas.saveLayerAlpha(30, 50, 130, 150, 128, SkCanvas::kClipToLayer_SaveFlag);
+ canvas.insertReorderBarrier(true);
+ canvas.drawRenderNode(createWhiteRectShadowCaster(5.0f).get());
+ canvas.insertReorderBarrier(false);
+ canvas.restoreToCount(count);
+ });
+
+ OpReorderer reorderer(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
+ createSyncedNodeList(parent), (Vector3) { 100, 100, 100 });
+
+ ShadowSaveLayerTestRenderer renderer;
+ reorderer.replayBakedOps<TestDispatcher>(renderer);
+ EXPECT_EQ(5, renderer.getIndex());
+}
+
+RENDERTHREAD_TEST(OpReorderer, shadowHwLayer) {
+ class ShadowHwLayerTestRenderer : public TestRendererBase {
+ public:
+ void startRepaintLayer(OffscreenBuffer* offscreenBuffer, const Rect& repaintRect) override {
+ EXPECT_EQ(0, mIndex++);
+ }
+ void onShadowOp(const ShadowOp& op, const BakedOpState& state) override {
+ EXPECT_EQ(1, mIndex++);
+ EXPECT_FLOAT_EQ(50, op.lightCenter.x);
+ EXPECT_FLOAT_EQ(40, op.lightCenter.y);
+ }
+ void onRectOp(const RectOp& op, const BakedOpState& state) override {
+ EXPECT_EQ(2, mIndex++);
+ }
+ void endLayer() override {
+ EXPECT_EQ(3, mIndex++);
+ }
+ void onLayerOp(const LayerOp& op, const BakedOpState& state) override {
+ EXPECT_EQ(4, mIndex++);
+ }
+ };
+
+ sp<RenderNode> parent = TestUtils::createNode<RecordingCanvas>(50, 60, 150, 160,
+ [] (RecordingCanvas& canvas) {
+ canvas.insertReorderBarrier(true);
+ canvas.save(SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag);
+ canvas.translate(20, 10);
+ canvas.drawRenderNode(createWhiteRectShadowCaster(5.0f).get());
+ canvas.restore();
+ }, TestUtils::getHwLayerSetupCallback());
+ OffscreenBuffer** layerHandle = parent->getLayerHandle();
+
+ // create RenderNode's layer here in same way prepareTree would, setting windowTransform
+ OffscreenBuffer layer(renderThread.renderState(), Caches::getInstance(), 100, 100);
+ Matrix4 windowTransform;
+ windowTransform.loadTranslate(50, 60, 0); // total transform of layer's origin
+ layer.setWindowTransform(windowTransform);
+ *layerHandle = &layer;
+
+ auto syncedList = createSyncedNodeList(parent);
+ LayerUpdateQueue layerUpdateQueue; // Note: enqueue damage post-sync, so bounds are valid
+ layerUpdateQueue.enqueueLayerWithDamage(parent.get(), Rect(100, 100));
+ OpReorderer reorderer(layerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
+ syncedList, (Vector3) { 100, 100, 100 });
+
+ ShadowHwLayerTestRenderer renderer;
+ reorderer.replayBakedOps<TestDispatcher>(renderer);
+ EXPECT_EQ(5, renderer.getIndex());
+
+ // clean up layer pointer, so we can safely destruct RenderNode
+ *layerHandle = nullptr;
+}
+
+TEST(OpReorderer, shadowLayering) {
+ class ShadowLayeringTestRenderer : public TestRendererBase {
+ public:
+ void onShadowOp(const ShadowOp& op, const BakedOpState& state) override {
+ int index = mIndex++;
+ EXPECT_TRUE(index == 0 || index == 1);
+ }
+ void onRectOp(const RectOp& op, const BakedOpState& state) override {
+ int index = mIndex++;
+ EXPECT_TRUE(index == 2 || index == 3);
+ }
+ };
+ sp<RenderNode> parent = TestUtils::createNode<RecordingCanvas>(0, 0, 200, 200,
+ [] (RecordingCanvas& canvas) {
+ canvas.insertReorderBarrier(true);
+ canvas.drawRenderNode(createWhiteRectShadowCaster(5.0f).get());
+ canvas.drawRenderNode(createWhiteRectShadowCaster(5.0001f).get());
+ });
+
+ OpReorderer reorderer(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
+ createSyncedNodeList(parent), sLightCenter);
+
+ ShadowLayeringTestRenderer renderer;
+ reorderer.replayBakedOps<TestDispatcher>(renderer);
+ EXPECT_EQ(4, renderer.getIndex());
+}
+
+
+static void testProperty(TestUtils::PropSetupCallback propSetupCallback,
std::function<void(const RectOp&, const BakedOpState&)> opValidateCallback) {
class PropertyTestRenderer : public TestRendererBase {
public:
@@ -630,13 +763,9 @@
paint.setColor(SK_ColorWHITE);
canvas.drawRect(0, 0, 100, 100, paint);
}, propSetupCallback);
- TestUtils::syncHierarchyPropertiesAndDisplayList(node);
- std::vector< sp<RenderNode> > nodes;
- nodes.push_back(node.get());
-
- OpReorderer reorderer(sEmptyLayerUpdateQueue,
- SkRect::MakeWH(100, 100), 200, 200, nodes);
+ OpReorderer reorderer(sEmptyLayerUpdateQueue, SkRect::MakeWH(100, 100), 200, 200,
+ createSyncedNodeList(node), sLightCenter);
PropertyTestRenderer renderer(opValidateCallback);
reorderer.replayBakedOps<TestDispatcher>(renderer);
diff --git a/libs/hwui/unit_tests/TestUtils.h b/libs/hwui/unit_tests/TestUtils.h
index efa28ae..38bafd5 100644
--- a/libs/hwui/unit_tests/TestUtils.h
+++ b/libs/hwui/unit_tests/TestUtils.h
@@ -45,6 +45,20 @@
&& MathUtils::areEqual(a.right, b.right) \
&& MathUtils::areEqual(a.bottom, b.bottom));
+/**
+ * Like gtest's TEST, but runs on the RenderThread, and 'renderThread' is passed, in top level scope
+ * (for e.g. accessing its RenderState)
+ */
+#define RENDERTHREAD_TEST(test_case_name, test_name) \
+ class test_case_name##_##test_name##_RenderThreadTest { \
+ public: \
+ static void doTheThing(renderthread::RenderThread& renderThread); \
+ }; \
+ TEST(test_case_name, test_name) { \
+ TestUtils::runOnRenderThread(test_case_name##_##test_name##_RenderThreadTest::doTheThing); \
+ }; \
+ void test_case_name##_##test_name##_RenderThreadTest::doTheThing(renderthread::RenderThread& renderThread)
+
class TestUtils {
public:
class SignalingDtor {
diff --git a/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDatabase.java b/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDatabase.java
index 3151ccb..874eeb3 100644
--- a/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDatabase.java
+++ b/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDatabase.java
@@ -79,8 +79,8 @@
private final Map<String, Integer> mMappingMode = new HashMap<>();
@VisibleForTesting
- MtpDatabase(Context context) {
- mDatabase = new MtpDatabaseInternal(context);
+ MtpDatabase(Context context, boolean inMemory) {
+ mDatabase = new MtpDatabaseInternal(context, inMemory);
}
/**
diff --git a/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDatabaseInternal.java b/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDatabaseInternal.java
index 7328f05..df2875e 100644
--- a/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDatabaseInternal.java
+++ b/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDatabaseInternal.java
@@ -35,8 +35,8 @@
*/
class MtpDatabaseInternal {
private static class OpenHelper extends SQLiteOpenHelper {
- public OpenHelper(Context context) {
- super(context, DATABASE_NAME, null, DATABASE_VERSION);
+ public OpenHelper(Context context, boolean inMemory) {
+ super(context, inMemory ? null : DATABASE_NAME, null, DATABASE_VERSION);
}
@Override
@@ -54,8 +54,8 @@
private final SQLiteDatabase mDatabase;
- MtpDatabaseInternal(Context context) {
- final OpenHelper helper = new OpenHelper(context);
+ MtpDatabaseInternal(Context context, boolean inMemory) {
+ final OpenHelper helper = new OpenHelper(context, inMemory);
mDatabase = helper.getWritableDatabase();
}
diff --git a/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDocumentsProvider.java b/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDocumentsProvider.java
index 0931445..f7e9463 100644
--- a/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDocumentsProvider.java
+++ b/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDocumentsProvider.java
@@ -78,7 +78,7 @@
mMtpManager = new MtpManager(getContext());
mResolver = getContext().getContentResolver();
mDeviceToolkits = new HashMap<Integer, DeviceToolkit>();
- mDatabase = new MtpDatabase(getContext());
+ mDatabase = new MtpDatabase(getContext(), false);
mRootScanner = new RootScanner(mResolver, mResources, mMtpManager, mDatabase);
return true;
}
diff --git a/packages/MtpDocumentsProvider/tests/AndroidManifest.xml b/packages/MtpDocumentsProvider/tests/AndroidManifest.xml
index 28ad3f4..e1307e9 100644
--- a/packages/MtpDocumentsProvider/tests/AndroidManifest.xml
+++ b/packages/MtpDocumentsProvider/tests/AndroidManifest.xml
@@ -18,7 +18,4 @@
<instrumentation android:name="com.android.mtp.TestResultInstrumentation"
android:targetPackage="com.android.mtp"
android:label="Tests for MtpDocumentsProvider with the UI for output." />
- <instrumentation android:name="android.test.InstrumentationTestRunner"
- android:targetPackage="com.android.mtp"
- android:label="Tests for MtpDocumentsProvider." />
</manifest>
diff --git a/packages/MtpDocumentsProvider/tests/src/com/android/mtp/DocumentLoaderTest.java b/packages/MtpDocumentsProvider/tests/src/com/android/mtp/DocumentLoaderTest.java
index a012d7f..f6d6d44 100644
--- a/packages/MtpDocumentsProvider/tests/src/com/android/mtp/DocumentLoaderTest.java
+++ b/packages/MtpDocumentsProvider/tests/src/com/android/mtp/DocumentLoaderTest.java
@@ -22,15 +22,14 @@
import android.net.Uri;
import android.provider.DocumentsContract;
import android.test.AndroidTestCase;
-import android.test.suitebuilder.annotation.SmallTest;
+import android.test.suitebuilder.annotation.MediumTest;
import java.io.IOException;
-import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.CountDownLatch;
-@SmallTest
+@MediumTest
public class DocumentLoaderTest extends AndroidTestCase {
private BlockableTestMtpManager mManager;
private TestContentResolver mResolver;
diff --git a/packages/MtpDocumentsProvider/tests/src/com/android/mtp/MtpDatabaseTest.java b/packages/MtpDocumentsProvider/tests/src/com/android/mtp/MtpDatabaseTest.java
index ce4cf14..7641e3a 100644
--- a/packages/MtpDocumentsProvider/tests/src/com/android/mtp/MtpDatabaseTest.java
+++ b/packages/MtpDocumentsProvider/tests/src/com/android/mtp/MtpDatabaseTest.java
@@ -41,18 +41,29 @@
};
private final TestResources resources = new TestResources();
+ MtpDatabase mDatabase;
+
+ @Override
+ public void setUp() {
+ mDatabase = new MtpDatabase(getContext(), true);
+ }
+
+ @Override
+ public void tearDown() {
+ mDatabase.close();
+ mDatabase = null;
+ }
public void testPutRootDocuments() throws Exception {
- final MtpDatabase database = new MtpDatabase(getContext());
- database.startAddingRootDocuments(0);
- database.putRootDocuments(0, resources, new MtpRoot[] {
+ mDatabase.startAddingRootDocuments(0);
+ mDatabase.putRootDocuments(0, resources, new MtpRoot[] {
new MtpRoot(0, 1, "Device", "Storage", 1000, 2000, ""),
new MtpRoot(0, 2, "Device", "Storage", 2000, 4000, ""),
new MtpRoot(0, 3, "Device", "/@#%&<>Storage", 3000, 6000,"")
});
{
- final Cursor cursor = database.queryRootDocuments(COLUMN_NAMES);
+ final Cursor cursor = mDatabase.queryRootDocuments(COLUMN_NAMES);
assertEquals(3, cursor.getCount());
cursor.moveToNext();
@@ -80,7 +91,7 @@
}
{
- final Cursor cursor = database.queryRoots(new String [] {
+ final Cursor cursor = mDatabase.queryRoots(new String [] {
Root.COLUMN_ROOT_ID,
Root.COLUMN_FLAGS,
Root.COLUMN_ICON,
@@ -136,15 +147,14 @@
}
public void testPutChildDocuments() throws Exception {
- final MtpDatabase database = new MtpDatabase(getContext());
- database.startAddingChildDocuments("parentId");
- database.putChildDocuments(0, "parentId", new MtpObjectInfo[] {
+ mDatabase.startAddingChildDocuments("parentId");
+ mDatabase.putChildDocuments(0, "parentId", new MtpObjectInfo[] {
createDocument(100, "note.txt", MtpConstants.FORMAT_TEXT, 1024),
createDocument(101, "image.jpg", MtpConstants.FORMAT_EXIF_JPEG, 2 * 1024 * 1024),
createDocument(102, "music.mp3", MtpConstants.FORMAT_MP3, 3 * 1024 * 1024)
});
- final Cursor cursor = database.queryChildDocuments(COLUMN_NAMES, "parentId");
+ final Cursor cursor = mDatabase.queryChildDocuments(COLUMN_NAMES, "parentId");
assertEquals(3, cursor.getCount());
cursor.moveToNext();
@@ -202,7 +212,6 @@
}
public void testRestoreIdForRootDocuments() throws Exception {
- final MtpDatabase database = new MtpDatabase(getContext());
final String[] columns = new String[] {
DocumentsContract.Document.COLUMN_DOCUMENT_ID,
MtpDatabaseConstants.COLUMN_STORAGE_ID,
@@ -212,14 +221,15 @@
Root.COLUMN_ROOT_ID,
Root.COLUMN_AVAILABLE_BYTES
};
- database.startAddingRootDocuments(0);
- database.putRootDocuments(0, resources, new MtpRoot[] {
+
+ mDatabase.startAddingRootDocuments(0);
+ mDatabase.putRootDocuments(0, resources, new MtpRoot[] {
new MtpRoot(0, 100, "Device", "Storage A", 1000, 0, ""),
new MtpRoot(0, 101, "Device", "Storage B", 1001, 0, "")
});
{
- final Cursor cursor = database.queryRootDocuments(columns);
+ final Cursor cursor = mDatabase.queryRootDocuments(columns);
assertEquals(2, cursor.getCount());
cursor.moveToNext();
assertEquals("documentId", 1, cursor.getInt(0));
@@ -233,7 +243,7 @@
}
{
- final Cursor cursor = database.queryRoots(rootColumns);
+ final Cursor cursor = mDatabase.queryRoots(rootColumns);
assertEquals(2, cursor.getCount());
cursor.moveToNext();
assertEquals("rootId", 1, cursor.getInt(0));
@@ -244,10 +254,10 @@
cursor.close();
}
- database.clearMapping();
+ mDatabase.clearMapping();
{
- final Cursor cursor = database.queryRootDocuments(columns);
+ final Cursor cursor = mDatabase.queryRootDocuments(columns);
assertEquals(2, cursor.getCount());
cursor.moveToNext();
assertEquals("documentId", 1, cursor.getInt(0));
@@ -261,7 +271,7 @@
}
{
- final Cursor cursor = database.queryRoots(rootColumns);
+ final Cursor cursor = mDatabase.queryRoots(rootColumns);
assertEquals(2, cursor.getCount());
cursor.moveToNext();
assertEquals("rootId", 1, cursor.getInt(0));
@@ -272,14 +282,14 @@
cursor.close();
}
- database.startAddingRootDocuments(0);
- database.putRootDocuments(0, resources, new MtpRoot[] {
+ mDatabase.startAddingRootDocuments(0);
+ mDatabase.putRootDocuments(0, resources, new MtpRoot[] {
new MtpRoot(0, 200, "Device", "Storage A", 2000, 0, ""),
new MtpRoot(0, 202, "Device", "Storage C", 2002, 0, "")
});
{
- final Cursor cursor = database.queryRootDocuments(columns);
+ final Cursor cursor = mDatabase.queryRootDocuments(columns);
assertEquals(3, cursor.getCount());
cursor.moveToNext();
assertEquals("documentId", 1, cursor.getInt(0));
@@ -297,7 +307,7 @@
}
{
- final Cursor cursor = database.queryRoots(rootColumns);
+ final Cursor cursor = mDatabase.queryRoots(rootColumns);
assertEquals(3, cursor.getCount());
cursor.moveToNext();
assertEquals("rootId", 1, cursor.getInt(0));
@@ -311,10 +321,10 @@
cursor.close();
}
- database.stopAddingRootDocuments(0);
+ mDatabase.stopAddingRootDocuments(0);
{
- final Cursor cursor = database.queryRootDocuments(columns);
+ final Cursor cursor = mDatabase.queryRootDocuments(columns);
assertEquals(2, cursor.getCount());
cursor.moveToNext();
assertEquals("documentId", 1, cursor.getInt(0));
@@ -328,7 +338,7 @@
}
{
- final Cursor cursor = database.queryRoots(rootColumns);
+ final Cursor cursor = mDatabase.queryRoots(rootColumns);
assertEquals(2, cursor.getCount());
cursor.moveToNext();
assertEquals("rootId", 1, cursor.getInt(0));
@@ -341,22 +351,21 @@
}
public void testRestoreIdForChildDocuments() throws Exception {
- final MtpDatabase database = new MtpDatabase(getContext());
final String[] columns = new String[] {
DocumentsContract.Document.COLUMN_DOCUMENT_ID,
MtpDatabaseConstants.COLUMN_OBJECT_HANDLE,
DocumentsContract.Document.COLUMN_DISPLAY_NAME
};
- database.startAddingChildDocuments("parentId");
- database.putChildDocuments(0, "parentId", new MtpObjectInfo[] {
+ mDatabase.startAddingChildDocuments("parentId");
+ mDatabase.putChildDocuments(0, "parentId", new MtpObjectInfo[] {
createDocument(100, "note.txt", MtpConstants.FORMAT_TEXT, 1024),
createDocument(101, "image.jpg", MtpConstants.FORMAT_EXIF_JPEG, 2 * 1024 * 1024),
createDocument(102, "music.mp3", MtpConstants.FORMAT_MP3, 3 * 1024 * 1024)
});
- database.clearMapping();
+ mDatabase.clearMapping();
{
- final Cursor cursor = database.queryChildDocuments(columns, "parentId");
+ final Cursor cursor = mDatabase.queryChildDocuments(columns, "parentId");
assertEquals(3, cursor.getCount());
cursor.moveToNext();
@@ -377,14 +386,14 @@
cursor.close();
}
- database.startAddingChildDocuments("parentId");
- database.putChildDocuments(0, "parentId", new MtpObjectInfo[] {
+ mDatabase.startAddingChildDocuments("parentId");
+ mDatabase.putChildDocuments(0, "parentId", new MtpObjectInfo[] {
createDocument(200, "note.txt", MtpConstants.FORMAT_TEXT, 1024),
createDocument(203, "video.mp4", MtpConstants.FORMAT_MP4_CONTAINER, 1024),
});
{
- final Cursor cursor = database.queryChildDocuments(columns, "parentId");
+ final Cursor cursor = mDatabase.queryChildDocuments(columns, "parentId");
assertEquals(4, cursor.getCount());
cursor.moveToPosition(3);
@@ -395,10 +404,10 @@
cursor.close();
}
- database.stopAddingChildDocuments("parentId");
+ mDatabase.stopAddingChildDocuments("parentId");
{
- final Cursor cursor = database.queryChildDocuments(columns, "parentId");
+ final Cursor cursor = mDatabase.queryChildDocuments(columns, "parentId");
assertEquals(2, cursor.getCount());
cursor.moveToNext();
@@ -415,7 +424,6 @@
}
public void testRestoreIdForDifferentDevices() throws Exception {
- final MtpDatabase database = new MtpDatabase(getContext());
final String[] columns = new String[] {
DocumentsContract.Document.COLUMN_DOCUMENT_ID,
MtpDatabaseConstants.COLUMN_STORAGE_ID,
@@ -425,17 +433,17 @@
Root.COLUMN_ROOT_ID,
Root.COLUMN_AVAILABLE_BYTES
};
- database.startAddingRootDocuments(0);
- database.startAddingRootDocuments(1);
- database.putRootDocuments(0, resources, new MtpRoot[] {
+ mDatabase.startAddingRootDocuments(0);
+ mDatabase.startAddingRootDocuments(1);
+ mDatabase.putRootDocuments(0, resources, new MtpRoot[] {
new MtpRoot(0, 100, "Device", "Storage", 0, 0, "")
});
- database.putRootDocuments(1, resources, new MtpRoot[] {
+ mDatabase.putRootDocuments(1, resources, new MtpRoot[] {
new MtpRoot(1, 100, "Device", "Storage", 0, 0, "")
});
{
- final Cursor cursor = database.queryRootDocuments(columns);
+ final Cursor cursor = mDatabase.queryRootDocuments(columns);
assertEquals(2, cursor.getCount());
cursor.moveToNext();
assertEquals("documentId", 1, cursor.getInt(0));
@@ -449,7 +457,7 @@
}
{
- final Cursor cursor = database.queryRoots(rootColumns);
+ final Cursor cursor = mDatabase.queryRoots(rootColumns);
assertEquals(2, cursor.getCount());
cursor.moveToNext();
assertEquals("rootId", 1, cursor.getInt(0));
@@ -460,21 +468,21 @@
cursor.close();
}
- database.clearMapping();
+ mDatabase.clearMapping();
- database.startAddingRootDocuments(0);
- database.startAddingRootDocuments(1);
- database.putRootDocuments(0, resources, new MtpRoot[] {
+ mDatabase.startAddingRootDocuments(0);
+ mDatabase.startAddingRootDocuments(1);
+ mDatabase.putRootDocuments(0, resources, new MtpRoot[] {
new MtpRoot(0, 200, "Device", "Storage", 2000, 0, "")
});
- database.putRootDocuments(1, resources, new MtpRoot[] {
+ mDatabase.putRootDocuments(1, resources, new MtpRoot[] {
new MtpRoot(1, 300, "Device", "Storage", 3000, 0, "")
});
- database.stopAddingRootDocuments(0);
- database.stopAddingRootDocuments(1);
+ mDatabase.stopAddingRootDocuments(0);
+ mDatabase.stopAddingRootDocuments(1);
{
- final Cursor cursor = database.queryRootDocuments(columns);
+ final Cursor cursor = mDatabase.queryRootDocuments(columns);
assertEquals(2, cursor.getCount());
cursor.moveToNext();
assertEquals("documentId", 1, cursor.getInt(0));
@@ -488,7 +496,7 @@
}
{
- final Cursor cursor = database.queryRoots(rootColumns);
+ final Cursor cursor = mDatabase.queryRoots(rootColumns);
assertEquals(2, cursor.getCount());
cursor.moveToNext();
assertEquals("rootId", 1, cursor.getInt(0));
@@ -501,34 +509,33 @@
}
public void testRestoreIdForDifferentParents() throws Exception {
- final MtpDatabase database = new MtpDatabase(getContext());
final String[] columns = new String[] {
DocumentsContract.Document.COLUMN_DOCUMENT_ID,
MtpDatabaseConstants.COLUMN_OBJECT_HANDLE
};
- database.startAddingChildDocuments("parentId1");
- database.startAddingChildDocuments("parentId2");
- database.putChildDocuments(0, "parentId1", new MtpObjectInfo[] {
+ mDatabase.startAddingChildDocuments("parentId1");
+ mDatabase.startAddingChildDocuments("parentId2");
+ mDatabase.putChildDocuments(0, "parentId1", new MtpObjectInfo[] {
createDocument(100, "note.txt", MtpConstants.FORMAT_TEXT, 1024),
});
- database.putChildDocuments(0, "parentId2", new MtpObjectInfo[] {
+ mDatabase.putChildDocuments(0, "parentId2", new MtpObjectInfo[] {
createDocument(101, "note.txt", MtpConstants.FORMAT_TEXT, 1024),
});
- database.clearMapping();
+ mDatabase.clearMapping();
- database.startAddingChildDocuments("parentId1");
- database.startAddingChildDocuments("parentId2");
- database.putChildDocuments(0, "parentId1", new MtpObjectInfo[] {
+ mDatabase.startAddingChildDocuments("parentId1");
+ mDatabase.startAddingChildDocuments("parentId2");
+ mDatabase.putChildDocuments(0, "parentId1", new MtpObjectInfo[] {
createDocument(200, "note.txt", MtpConstants.FORMAT_TEXT, 1024),
});
- database.putChildDocuments(0, "parentId2", new MtpObjectInfo[] {
+ mDatabase.putChildDocuments(0, "parentId2", new MtpObjectInfo[] {
createDocument(201, "note.txt", MtpConstants.FORMAT_TEXT, 1024),
});
- database.stopAddingChildDocuments("parentId1");
+ mDatabase.stopAddingChildDocuments("parentId1");
{
- final Cursor cursor = database.queryChildDocuments(columns, "parentId1");
+ final Cursor cursor = mDatabase.queryChildDocuments(columns, "parentId1");
assertEquals(1, cursor.getCount());
cursor.moveToNext();
assertEquals("documentId", 1, cursor.getInt(0));
@@ -536,7 +543,7 @@
cursor.close();
}
{
- final Cursor cursor = database.queryChildDocuments(columns, "parentId2");
+ final Cursor cursor = mDatabase.queryChildDocuments(columns, "parentId2");
assertEquals(1, cursor.getCount());
cursor.moveToNext();
assertEquals("documentId", 2, cursor.getInt(0));
@@ -546,7 +553,6 @@
}
public void testClearMtpIdentifierBeforeResolveRootDocuments() {
- final MtpDatabase database = new MtpDatabase(getContext());
final String[] columns = new String[] {
DocumentsContract.Document.COLUMN_DOCUMENT_ID,
MtpDatabaseConstants.COLUMN_STORAGE_ID,
@@ -557,26 +563,26 @@
Root.COLUMN_AVAILABLE_BYTES
};
- database.startAddingRootDocuments(0);
- database.putRootDocuments(0, resources, new MtpRoot[] {
+ mDatabase.startAddingRootDocuments(0);
+ mDatabase.putRootDocuments(0, resources, new MtpRoot[] {
new MtpRoot(0, 100, "Device", "Storage", 0, 0, ""),
});
- database.clearMapping();
+ mDatabase.clearMapping();
- database.startAddingRootDocuments(0);
- database.putRootDocuments(0, resources, new MtpRoot[] {
+ mDatabase.startAddingRootDocuments(0);
+ mDatabase.putRootDocuments(0, resources, new MtpRoot[] {
new MtpRoot(0, 200, "Device", "Storage", 2000, 0, ""),
});
- database.clearMapping();
+ mDatabase.clearMapping();
- database.startAddingRootDocuments(0);
- database.putRootDocuments(0, resources, new MtpRoot[] {
+ mDatabase.startAddingRootDocuments(0);
+ mDatabase.putRootDocuments(0, resources, new MtpRoot[] {
new MtpRoot(0, 300, "Device", "Storage", 3000, 0, ""),
});
- database.stopAddingRootDocuments(0);
+ mDatabase.stopAddingRootDocuments(0);
{
- final Cursor cursor = database.queryRootDocuments(columns);
+ final Cursor cursor = mDatabase.queryRootDocuments(columns);
assertEquals(1, cursor.getCount());
cursor.moveToNext();
assertEquals("documentId", 1, cursor.getInt(0));
@@ -585,7 +591,7 @@
cursor.close();
}
{
- final Cursor cursor = database.queryRoots(rootColumns);
+ final Cursor cursor = mDatabase.queryRoots(rootColumns);
assertEquals(1, cursor.getCount());
cursor.moveToNext();
assertEquals("rootId", 1, cursor.getInt(0));
@@ -595,7 +601,6 @@
}
public void testPutSameNameRootsAfterClearing() throws Exception {
- final MtpDatabase database = new MtpDatabase(getContext());
final String[] columns = new String[] {
DocumentsContract.Document.COLUMN_DOCUMENT_ID,
MtpDatabaseConstants.COLUMN_STORAGE_ID,
@@ -606,21 +611,21 @@
Root.COLUMN_AVAILABLE_BYTES
};
- database.startAddingRootDocuments(0);
- database.putRootDocuments(0, resources, new MtpRoot[] {
+ mDatabase.startAddingRootDocuments(0);
+ mDatabase.putRootDocuments(0, resources, new MtpRoot[] {
new MtpRoot(0, 100, "Device", "Storage", 0, 0, ""),
});
- database.clearMapping();
+ mDatabase.clearMapping();
- database.startAddingRootDocuments(0);
- database.putRootDocuments(0, resources, new MtpRoot[] {
+ mDatabase.startAddingRootDocuments(0);
+ mDatabase.putRootDocuments(0, resources, new MtpRoot[] {
new MtpRoot(0, 200, "Device", "Storage", 2000, 0, ""),
new MtpRoot(0, 201, "Device", "Storage", 2001, 0, ""),
});
- database.stopAddingRootDocuments(0);
+ mDatabase.stopAddingRootDocuments(0);
{
- final Cursor cursor = database.queryRootDocuments(columns);
+ final Cursor cursor = mDatabase.queryRootDocuments(columns);
assertEquals(2, cursor.getCount());
cursor.moveToNext();
assertEquals("documentId", 2, cursor.getInt(0));
@@ -633,7 +638,7 @@
cursor.close();
}
{
- final Cursor cursor = database.queryRoots(rootColumns);
+ final Cursor cursor = mDatabase.queryRoots(rootColumns);
assertEquals(2, cursor.getCount());
cursor.moveToNext();
assertEquals("rootId", 2, cursor.getInt(0));
@@ -646,27 +651,26 @@
}
public void testReplaceExistingRoots() {
- // The client code should be able to replace exisitng rows with new information.
- final MtpDatabase database = new MtpDatabase(getContext());
+ // The client code should be able to replace existing rows with new information.
// Add one.
- database.startAddingRootDocuments(0);
- database.putRootDocuments(0, resources, new MtpRoot[] {
+ mDatabase.startAddingRootDocuments(0);
+ mDatabase.putRootDocuments(0, resources, new MtpRoot[] {
new MtpRoot(0, 100, "Device", "Storage A", 0, 0, ""),
});
- database.stopAddingRootDocuments(0);
+ mDatabase.stopAddingRootDocuments(0);
// Replace it.
- database.startAddingRootDocuments(0);
- database.putRootDocuments(0, resources, new MtpRoot[] {
+ mDatabase.startAddingRootDocuments(0);
+ mDatabase.putRootDocuments(0, resources, new MtpRoot[] {
new MtpRoot(0, 100, "Device", "Storage B", 1000, 1000, ""),
});
- database.stopAddingRootDocuments(0);
+ mDatabase.stopAddingRootDocuments(0);
{
final String[] columns = new String[] {
DocumentsContract.Document.COLUMN_DOCUMENT_ID,
MtpDatabaseConstants.COLUMN_STORAGE_ID,
DocumentsContract.Document.COLUMN_DISPLAY_NAME
};
- final Cursor cursor = database.queryRootDocuments(columns);
+ final Cursor cursor = mDatabase.queryRootDocuments(columns);
assertEquals(1, cursor.getCount());
cursor.moveToNext();
assertEquals("documentId", 1, cursor.getInt(0));
@@ -679,7 +683,7 @@
Root.COLUMN_ROOT_ID,
Root.COLUMN_AVAILABLE_BYTES
};
- final Cursor cursor = database.queryRoots(columns);
+ final Cursor cursor = mDatabase.queryRoots(columns);
assertEquals(1, cursor.getCount());
cursor.moveToNext();
assertEquals("rootId", 1, cursor.getInt(0));
@@ -690,20 +694,19 @@
public void _testFailToReplaceExisitingUnmappedRoots() {
// The client code should not be able to replace rows before resolving 'unmapped' rows.
- final MtpDatabase database = new MtpDatabase(getContext());
// Add one.
- database.startAddingRootDocuments(0);
- database.putRootDocuments(0, resources, new MtpRoot[] {
+ mDatabase.startAddingRootDocuments(0);
+ mDatabase.putRootDocuments(0, resources, new MtpRoot[] {
new MtpRoot(0, 100, "Device", "Storage A", 0, 0, ""),
});
- database.clearMapping();
+ mDatabase.clearMapping();
// Add one.
- database.putRootDocuments(0, resources, new MtpRoot[] {
+ mDatabase.putRootDocuments(0, resources, new MtpRoot[] {
new MtpRoot(0, 100, "Device", "Storage B", 1000, 1000, ""),
});
// Add one more before resolving unmapped documents.
try {
- database.putRootDocuments(0, resources, new MtpRoot[] {
+ mDatabase.putRootDocuments(0, resources, new MtpRoot[] {
new MtpRoot(0, 100, "Device", "Storage B", 1000, 1000, ""),
});
fail();
diff --git a/packages/MtpDocumentsProvider/tests/src/com/android/mtp/MtpDocumentsProviderTest.java b/packages/MtpDocumentsProvider/tests/src/com/android/mtp/MtpDocumentsProviderTest.java
index bc7f28c..70923c0 100644
--- a/packages/MtpDocumentsProvider/tests/src/com/android/mtp/MtpDocumentsProviderTest.java
+++ b/packages/MtpDocumentsProvider/tests/src/com/android/mtp/MtpDocumentsProviderTest.java
@@ -43,7 +43,7 @@
mResolver = new TestContentResolver();
mMtpManager = new TestMtpManager(getContext());
mProvider = new MtpDocumentsProvider();
- mDatabase = new MtpDatabase(getContext());
+ mDatabase = new MtpDatabase(getContext(), true);
mProvider.onCreateForTesting(mResources, mMtpManager, mResolver, mDatabase);
}
diff --git a/packages/MtpDocumentsProvider/tests/src/com/android/mtp/PipeManagerTest.java b/packages/MtpDocumentsProvider/tests/src/com/android/mtp/PipeManagerTest.java
index 53018cc..7c947f5 100644
--- a/packages/MtpDocumentsProvider/tests/src/com/android/mtp/PipeManagerTest.java
+++ b/packages/MtpDocumentsProvider/tests/src/com/android/mtp/PipeManagerTest.java
@@ -19,15 +19,14 @@
import android.mtp.MtpObjectInfo;
import android.os.ParcelFileDescriptor;
import android.test.AndroidTestCase;
-import android.test.suitebuilder.annotation.SmallTest;
+import android.test.suitebuilder.annotation.MediumTest;
import java.io.IOException;
-import java.util.Date;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
-@SmallTest
+@MediumTest
public class PipeManagerTest extends AndroidTestCase {
private static final byte[] HELLO_BYTES = new byte[] { 'h', 'e', 'l', 'l', 'o' };
diff --git a/packages/MtpDocumentsProvider/tests/src/com/android/mtp/TestResultInstrumentation.java b/packages/MtpDocumentsProvider/tests/src/com/android/mtp/TestResultInstrumentation.java
index a243375..0fb0f34 100644
--- a/packages/MtpDocumentsProvider/tests/src/com/android/mtp/TestResultInstrumentation.java
+++ b/packages/MtpDocumentsProvider/tests/src/com/android/mtp/TestResultInstrumentation.java
@@ -12,8 +12,20 @@
@Override
public void onCreate(Bundle arguments) {
+ if (arguments == null) {
+ arguments = new Bundle();
+ }
+ final boolean includeRealDeviceTest =
+ Boolean.parseBoolean(arguments.getString("realDeviceTest", "false"));
+ if (!includeRealDeviceTest) {
+ arguments.putString("notAnnotation", "com.android.mtp.RealDeviceTest");
+ }
super.onCreate(arguments);
- addTestListener(this);
+ if (includeRealDeviceTest) {
+ // Show the test result by using activity because we need to disconnect USB cable
+ // from adb host while testing with real MTP device.
+ addTestListener(this);
+ }
}
@Override
diff --git a/packages/PrintSpooler/res/layout/printer_dropdown_prompt.xml b/packages/PrintSpooler/res/layout/printer_dropdown_prompt.xml
new file mode 100644
index 0000000..11fef2d
--- /dev/null
+++ b/packages/PrintSpooler/res/layout/printer_dropdown_prompt.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+
+<TextView xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:textAppearance="?android:attr/textAppearanceMedium"
+ android:textIsSelectable="false"
+ android:textColor="?android:attr/textColorPrimary"
+ android:paddingStart="20dip"
+ android:paddingEnd="8dip"
+ android:minHeight="56dip"
+ android:orientation="horizontal"
+ android:text="@string/destination_default_text"
+ android:gravity="start|center_vertical" />
diff --git a/packages/PrintSpooler/res/values/strings.xml b/packages/PrintSpooler/res/values/strings.xml
index 70abdf4..6d81788 100644
--- a/packages/PrintSpooler/res/values/strings.xml
+++ b/packages/PrintSpooler/res/values/strings.xml
@@ -49,6 +49,9 @@
<!-- Label of the page selection widget. [CHAR LIMIT=20] -->
<string name="label_pages">Pages</string>
+ <!-- Label of the destination widget. [CHAR LIMIT=20] -->
+ <string name="destination_default_text">Select a printer</string>
+
<!-- Template for the all pages option in the page selection widget. [CHAR LIMIT=20] -->
<string name="template_all_pages">All <xliff:g id="page_count" example="100">%1$s</xliff:g></string>
diff --git a/packages/PrintSpooler/src/com/android/printspooler/ui/PrintActivity.java b/packages/PrintSpooler/src/com/android/printspooler/ui/PrintActivity.java
index f409fd4..e758835 100644
--- a/packages/PrintSpooler/src/com/android/printspooler/ui/PrintActivity.java
+++ b/packages/PrintSpooler/src/com/android/printspooler/ui/PrintActivity.java
@@ -64,6 +64,7 @@
import android.util.ArrayMap;
import android.util.Log;
import android.view.KeyEvent;
+import android.view.MotionEvent;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.View.OnFocusChangeListener;
@@ -125,6 +126,8 @@
private static final String FRAGMENT_TAG = "FRAGMENT_TAG";
+ private static final String HAS_PRINTED_PREF = "has_printed";
+
private static final int ORIENTATION_PORTRAIT = 0;
private static final int ORIENTATION_LANDSCAPE = 1;
@@ -187,6 +190,7 @@
private Spinner mDestinationSpinner;
private DestinationAdapter mDestinationSpinnerAdapter;
+ private boolean mShowDestinationPrompt;
private Spinner mMediaSizeSpinner;
private ArrayAdapter<SpinnerItem<MediaSize>> mMediaSizeSpinnerAdapter;
@@ -1093,6 +1097,7 @@
updateOptionsUi();
addCurrentPrinterToHistory();
+ setUserPrinted();
PageRange[] selectedPages = computeSelectedPages();
if (!Arrays.equals(mSelectedPages, selectedPages)) {
@@ -1195,6 +1200,29 @@
// Print button
mPrintButton = (ImageView) findViewById(R.id.print_button);
mPrintButton.setOnClickListener(clickListener);
+
+ // Special prompt instead of destination spinner for the first time the user printed
+ if (!hasUserEverPrinted()) {
+ mShowDestinationPrompt = true;
+
+ mSummaryCopies.setEnabled(false);
+ mSummaryPaperSize.setEnabled(false);
+
+ mDestinationSpinner.setOnTouchListener(new View.OnTouchListener() {
+ @Override
+ public boolean onTouch(View v, MotionEvent event) {
+ mShowDestinationPrompt = false;
+ mSummaryCopies.setEnabled(true);
+ mSummaryPaperSize.setEnabled(true);
+ updateOptionsUi();
+
+ mDestinationSpinner.setOnTouchListener(null);
+ mDestinationSpinnerAdapter.notifyDataSetChanged();
+
+ return false;
+ }
+ });
+ }
}
/**
@@ -1332,6 +1360,22 @@
&& printer.getStatus() != PrinterInfo.STATUS_UNAVAILABLE;
}
+ /**
+ * Disable all options UI elements, beside the {@link #mDestinationSpinner}
+ */
+ private void disableOptionsUi() {
+ mCopiesEditText.setEnabled(false);
+ mCopiesEditText.setFocusable(false);
+ mMediaSizeSpinner.setEnabled(false);
+ mColorModeSpinner.setEnabled(false);
+ mDuplexModeSpinner.setEnabled(false);
+ mOrientationSpinner.setEnabled(false);
+ mRangeOptionsSpinner.setEnabled(false);
+ mPageRangeEditText.setEnabled(false);
+ mPrintButton.setVisibility(View.GONE);
+ mMoreOptionsButton.setEnabled(false);
+ }
+
void updateOptionsUi() {
// Always update the summary.
updateSummary();
@@ -1346,32 +1390,14 @@
if (mState != STATE_PRINTER_UNAVAILABLE) {
mDestinationSpinner.setEnabled(false);
}
- mCopiesEditText.setEnabled(false);
- mCopiesEditText.setFocusable(false);
- mMediaSizeSpinner.setEnabled(false);
- mColorModeSpinner.setEnabled(false);
- mDuplexModeSpinner.setEnabled(false);
- mOrientationSpinner.setEnabled(false);
- mRangeOptionsSpinner.setEnabled(false);
- mPageRangeEditText.setEnabled(false);
- mPrintButton.setVisibility(View.GONE);
- mMoreOptionsButton.setEnabled(false);
+ disableOptionsUi();
return;
}
// If no current printer, or it has no capabilities, or it is not
// available, we disable all print options except the destination.
if (mCurrentPrinter == null || !canPrint(mCurrentPrinter)) {
- mCopiesEditText.setEnabled(false);
- mCopiesEditText.setFocusable(false);
- mMediaSizeSpinner.setEnabled(false);
- mColorModeSpinner.setEnabled(false);
- mDuplexModeSpinner.setEnabled(false);
- mOrientationSpinner.setEnabled(false);
- mRangeOptionsSpinner.setEnabled(false);
- mPageRangeEditText.setEnabled(false);
- mPrintButton.setVisibility(View.GONE);
- mMoreOptionsButton.setEnabled(false);
+ disableOptionsUi();
return;
}
@@ -1679,6 +1705,10 @@
mCopiesEditText.setText(MIN_COPIES_STRING);
mCopiesEditText.requestFocus();
}
+
+ if (mShowDestinationPrompt) {
+ disableOptionsUi();
+ }
}
private void updateSummary() {
@@ -1980,6 +2010,32 @@
}
}
+
+ /**
+ * Check if the user has ever printed a document
+ *
+ * @return true iff the user has ever printed a document
+ */
+ private boolean hasUserEverPrinted() {
+ SharedPreferences preferences = getSharedPreferences(HAS_PRINTED_PREF, MODE_PRIVATE);
+
+ return preferences.getBoolean(HAS_PRINTED_PREF, false);
+ }
+
+ /**
+ * Remember that the user printed a document
+ */
+ private void setUserPrinted() {
+ SharedPreferences preferences = getSharedPreferences(HAS_PRINTED_PREF, MODE_PRIVATE);
+
+ if (!preferences.getBoolean(HAS_PRINTED_PREF, false)) {
+ SharedPreferences.Editor edit = preferences.edit();
+
+ edit.putBoolean(HAS_PRINTED_PREF, true);
+ edit.apply();
+ }
+ }
+
private final class DestinationAdapter extends BaseAdapter
implements PrinterRegistry.OnPrintersChangeListener {
private final List<PrinterHolder> mPrinterHolders = new ArrayList<>();
@@ -1988,6 +2044,11 @@
private boolean mHistoricalPrintersLoaded;
+ /**
+ * Has the {@link #mDestinationSpinner} ever used a view from printer_dropdown_prompt
+ */
+ private boolean hadPromptView;
+
public DestinationAdapter() {
mHistoricalPrintersLoaded = mPrinterRegistry.areHistoricalPrintersLoaded();
if (mHistoricalPrintersLoaded) {
@@ -2098,9 +2159,20 @@
@Override
public View getView(int position, View convertView, ViewGroup parent) {
- if (convertView == null) {
- convertView = getLayoutInflater().inflate(
- R.layout.printer_dropdown_item, parent, false);
+ if (mShowDestinationPrompt) {
+ if (convertView == null) {
+ convertView = getLayoutInflater().inflate(
+ R.layout.printer_dropdown_prompt, parent, false);
+ hadPromptView = true;
+ }
+
+ return convertView;
+ } else {
+ // We don't know if we got an recyled printer_dropdown_prompt, hence do not use it
+ if (hadPromptView || convertView == null) {
+ convertView = getLayoutInflater().inflate(
+ R.layout.printer_dropdown_item, parent, false);
+ }
}
CharSequence title = null;
diff --git a/services/core/java/com/android/server/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
index 13d3ee1..0ec4b18 100644
--- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
@@ -3354,11 +3354,10 @@
// preserve the old window until the new one is drawn. This prevents having a gap
// between the removal and addition, in which no window is visible. We also want the
// entrance of the new window to be properly animated.
- mWindowManager.setReplacingWindow(topActivity.appToken, true /* animate */);
+ mWindowManager.setReplacingWindow(topActivity.appToken);
}
- final ActivityStack stack =
- moveTaskToStackUncheckedLocked(task, stackId, toTop, forceFocus,
- "moveTaskToStack:" + reason);
+ final ActivityStack stack = moveTaskToStackUncheckedLocked(
+ task, stackId, toTop, forceFocus, "moveTaskToStack:" + reason);
// Make sure the task has the appropriate bounds/size for the stack it is in.
if (stackId == FULLSCREEN_WORKSPACE_STACK_ID && task.mBounds != null) {
diff --git a/services/core/java/com/android/server/wm/AppWindowToken.java b/services/core/java/com/android/server/wm/AppWindowToken.java
index 425ff9b..3f4eaac 100644
--- a/services/core/java/com/android/server/wm/AppWindowToken.java
+++ b/services/core/java/com/android/server/wm/AppWindowToken.java
@@ -128,8 +128,6 @@
boolean mWillReplaceWindow;
// If true, the replaced window was already requested to be removed.
boolean mReplacingRemoveRequested;
- // Whether the replacement of the window should trigger app transition animation.
- boolean mAnimateReplacingWindow;
// If not null, the window that will be used to replace the old one. This is being set when
// the window is added and unset when this window reports its first draw.
WindowState mReplacingWindow;
diff --git a/services/core/java/com/android/server/wm/InputMonitor.java b/services/core/java/com/android/server/wm/InputMonitor.java
index 3c3123f..1f351cb 100644
--- a/services/core/java/com/android/server/wm/InputMonitor.java
+++ b/services/core/java/com/android/server/wm/InputMonitor.java
@@ -171,10 +171,10 @@
private void addInputWindowHandleLw(final InputWindowHandle inputWindowHandle,
final WindowState child, int flags, final int type, final boolean isVisible,
- final boolean hasFocus, final boolean hasWallpaper, DisplayContent displayContent) {
+ final boolean hasFocus, final boolean hasWallpaper) {
// Add a window to our list of input windows.
inputWindowHandle.name = child.toString();
- flags = child.getTouchableRegion(inputWindowHandle.touchableRegion, flags, this);
+ flags = child.getTouchableRegion(inputWindowHandle.touchableRegion, flags);
inputWindowHandle.layoutParamsFlags = flags;
inputWindowHandle.layoutParamsType = type;
inputWindowHandle.dispatchingTimeoutNanos = child.getInputDispatchingTimeoutNanos();
@@ -308,8 +308,8 @@
mService.mDragState.sendDragStartedIfNeededLw(child);
}
- addInputWindowHandleLw(inputWindowHandle, child, flags, type, isVisible, hasFocus,
- hasWallpaper, displayContent);
+ addInputWindowHandleLw(
+ inputWindowHandle, child, flags, type, isVisible, hasFocus, hasWallpaper);
}
}
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 4293f0a..957cb0d 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -2077,7 +2077,7 @@
}
private void prepareWindowReplacementTransition(AppWindowToken atoken) {
- if (atoken == null || !atoken.mWillReplaceWindow || !atoken.mAnimateReplacingWindow) {
+ if (atoken == null || !atoken.mWillReplaceWindow) {
return;
}
atoken.allDrawn = false;
@@ -8556,8 +8556,7 @@
} else if (wtoken != null) {
winAnimator.mAnimLayer =
w.mLayer + wtoken.mAppAnimator.animLayerAdjustment;
- if (wtoken.mWillReplaceWindow && wtoken.mAnimateReplacingWindow &&
- wtoken.mReplacingWindow != w) {
+ if (wtoken.mWillReplaceWindow && wtoken.mReplacingWindow != w) {
// We know that we will be animating a relaunching window in the near future,
// which will receive a z-order increase. We want the replaced window to
// immediately receive the same treatment, e.g. to be above the dock divider.
@@ -10094,9 +10093,8 @@
* Hint to a token that its activity will relaunch, which will trigger removal and addition of
* a window.
* @param token Application token for which the activity will be relaunched.
- * @param animate Whether to animate the addition of the new window.
*/
- public void setReplacingWindow(IBinder token, boolean animate) {
+ public void setReplacingWindow(IBinder token) {
synchronized (mWindowMap) {
AppWindowToken appWindowToken = findAppWindowToken(token);
if (appWindowToken == null) {
@@ -10107,7 +10105,13 @@
+ " as replacing window.");
appWindowToken.mWillReplaceWindow = true;
appWindowToken.mHasReplacedWindow = false;
- appWindowToken.mAnimateReplacingWindow = animate;
+
+ // Set-up dummy animation so we can start treating windows associated with this token
+ // like they are in transition before the new app window is ready for us to run the
+ // real transition animation.
+ if (DEBUG_APP_TRANSITIONS) Slog.v(TAG,
+ "setReplacingWindow() Setting dummy animation on: " + appWindowToken);
+ appWindowToken.mAppAnimator.setDummyAnimation();
}
}
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index 673c21f..6a19e5a 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -16,11 +16,57 @@
package com.android.server.wm;
+import com.android.server.input.InputWindowHandle;
+
+import android.app.ActivityManager;
+import android.app.AppOpsManager;
+import android.content.Context;
+import android.content.res.Configuration;
+import android.graphics.Matrix;
+import android.graphics.PixelFormat;
+import android.graphics.Point;
+import android.graphics.Rect;
+import android.graphics.Region;
+import android.os.IBinder;
+import android.os.PowerManager;
+import android.os.RemoteCallbackList;
+import android.os.RemoteException;
+import android.os.SystemClock;
+import android.os.Trace;
+import android.os.UserHandle;
+import android.os.WorkSource;
+import android.util.DisplayMetrics;
+import android.util.Slog;
+import android.util.TimeUtils;
+import android.view.Display;
+import android.view.DisplayInfo;
+import android.view.Gravity;
+import android.view.IApplicationToken;
+import android.view.IWindow;
+import android.view.IWindowFocusObserver;
+import android.view.IWindowId;
+import android.view.InputChannel;
+import android.view.InputEvent;
+import android.view.InputEventReceiver;
+import android.view.View;
+import android.view.ViewTreeObserver;
+import android.view.WindowManager;
+import android.view.WindowManagerPolicy;
+
+import java.io.PrintWriter;
+import java.util.ArrayList;
+
import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER;
+import static android.view.ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_CONTENT;
+import static android.view.ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_FRAME;
+import static android.view.ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_REGION;
+import static android.view.ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_VISIBLE;
import static android.view.WindowManager.LayoutParams.FIRST_SUB_WINDOW;
import static android.view.WindowManager.LayoutParams.FLAG_ALLOW_LOCK_WHILE_SCREEN_ON;
import static android.view.WindowManager.LayoutParams.FLAG_DIM_BEHIND;
import static android.view.WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD;
+import static android.view.WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
+import static android.view.WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL;
import static android.view.WindowManager.LayoutParams.FLAG_SCALED;
import static android.view.WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED;
import static android.view.WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON;
@@ -36,8 +82,6 @@
import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD_DIALOG;
import static android.view.WindowManager.LayoutParams.TYPE_WALLPAPER;
import static com.android.server.wm.WindowManagerService.DEBUG_ADD_REMOVE;
-import static com.android.server.wm.WindowManagerService.DEBUG_ANIM;
-import static com.android.server.wm.WindowManagerService.DEBUG_APP_TRANSITIONS;
import static com.android.server.wm.WindowManagerService.DEBUG_CONFIGURATION;
import static com.android.server.wm.WindowManagerService.DEBUG_FOCUS_LIGHT;
import static com.android.server.wm.WindowManagerService.DEBUG_LAYOUT;
@@ -46,47 +90,6 @@
import static com.android.server.wm.WindowManagerService.DEBUG_RESIZE;
import static com.android.server.wm.WindowManagerService.DEBUG_VISIBILITY;
-import android.app.ActivityManager;
-import android.app.AppOpsManager;
-import android.graphics.Point;
-import android.os.PowerManager;
-import android.os.RemoteCallbackList;
-import android.os.SystemClock;
-import android.os.Trace;
-import android.os.WorkSource;
-import android.util.DisplayMetrics;
-import android.util.TimeUtils;
-import android.view.Display;
-import android.view.IWindowFocusObserver;
-import android.view.IWindowId;
-
-import com.android.server.input.InputWindowHandle;
-
-import android.content.Context;
-import android.content.res.Configuration;
-import android.graphics.Matrix;
-import android.graphics.PixelFormat;
-import android.graphics.Rect;
-import android.graphics.Region;
-import android.os.IBinder;
-import android.os.RemoteException;
-import android.os.UserHandle;
-import android.util.Slog;
-import android.view.DisplayInfo;
-import android.view.Gravity;
-import android.view.IApplicationToken;
-import android.view.IWindow;
-import android.view.InputChannel;
-import android.view.InputEvent;
-import android.view.InputEventReceiver;
-import android.view.View;
-import android.view.ViewTreeObserver;
-import android.view.WindowManager;
-import android.view.WindowManagerPolicy;
-
-import java.io.PrintWriter;
-import java.util.ArrayList;
-
class WindowList extends ArrayList<WindowState> {
}
@@ -1388,7 +1391,6 @@
&& token.mHasReplacedWindow) {
if (DEBUG_ADD_REMOVE) Slog.d(TAG, "Removing replacing window: " + this);
token.mWillReplaceWindow = false;
- token.mAnimateReplacingWindow = false;
token.mReplacingRemoveRequested = false;
token.mReplacingWindow = null;
token.mHasReplacedWindow = false;
@@ -1411,12 +1413,11 @@
return mAppToken != null && mAppToken.mTask != null && mAppToken.mTask.inDockedWorkspace();
}
- int getTouchableRegion(Region region, int flags, InputMonitor inputMonitor) {
- final boolean modal = (flags & (WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
- | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE)) == 0;
+ int getTouchableRegion(Region region, int flags) {
+ final boolean modal = (flags & (FLAG_NOT_TOUCH_MODAL | FLAG_NOT_FOCUSABLE)) == 0;
if (modal && mAppToken != null) {
// Limit the outer touch to the activity stack region.
- flags |= WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL;
+ flags |= FLAG_NOT_TOUCH_MODAL;
// If this is a modal window we need to dismiss it if it's not full screen and the
// touch happens outside of the frame that displays the content. This means we
// need to intercept touches outside of that window. The dim layer user
@@ -1437,6 +1438,7 @@
mTmpRect.inset(-delta, -delta);
}
region.set(mTmpRect);
+ cropRegionToStackBoundsIfNeeded(region);
} else {
// Not modal or full screen modal
getTouchableRegion(region);
@@ -1772,26 +1774,41 @@
frame.right - inset.right, frame.bottom - inset.bottom);
}
- public void getTouchableRegion(Region outRegion) {
+ void getTouchableRegion(Region outRegion) {
final Rect frame = mFrame;
switch (mTouchableInsets) {
default:
- case ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_FRAME:
+ case TOUCHABLE_INSETS_FRAME:
outRegion.set(frame);
break;
- case ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_CONTENT:
+ case TOUCHABLE_INSETS_CONTENT:
applyInsets(outRegion, frame, mGivenContentInsets);
break;
- case ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_VISIBLE:
+ case TOUCHABLE_INSETS_VISIBLE:
applyInsets(outRegion, frame, mGivenVisibleInsets);
break;
- case ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_REGION: {
+ case TOUCHABLE_INSETS_REGION: {
final Region givenTouchableRegion = mGivenTouchableRegion;
outRegion.set(givenTouchableRegion);
outRegion.translate(frame.left, frame.top);
break;
}
}
+ cropRegionToStackBoundsIfNeeded(outRegion);
+ }
+
+ void cropRegionToStackBoundsIfNeeded(Region region) {
+ if (mAppToken == null || !mAppToken.mCropWindowsToStack) {
+ return;
+ }
+
+ final TaskStack stack = getStack();
+ if (stack == null) {
+ return;
+ }
+
+ stack.getDimBounds(mTmpRect);
+ region.op(mTmpRect, Region.Op.INTERSECT);
}
WindowList getWindowList() {