Merge "Import revised translations. DO NOT MERGE" into ics-mr1
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 3f77ce1..72a915d 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -208,13 +208,13 @@
<skip />
<!-- The format string for default page scroll text [CHAR_LIMIT=none] -->
- <string name="default_scroll_format" translatable="false">Page %1$d of %2$d</string>
+ <string name="default_scroll_format">Page %1$d of %2$d</string>
<!-- The format string for Workspace page scroll text [CHAR_LIMIT=none] -->
- <string name="workspace_scroll_format" translatable="false">Workspace %1$d of %2$d</string>
+ <string name="workspace_scroll_format">Workspace %1$d of %2$d</string>
<!-- The format string for AppsCustomize Apps page scroll text [CHAR_LIMIT=none] -->
- <string name="apps_customize_apps_scroll_format" translatable="false">Apps page %1$d of %2$d</string>
+ <string name="apps_customize_apps_scroll_format">Apps page %1$d of %2$d</string>
<!-- The format string for AppsCustomize Apps page scroll text [CHAR_LIMIT=none] -->
- <string name="apps_customize_widgets_scroll_format" translatable="false">Widgets page %1$d of %2$d</string>
+ <string name="apps_customize_widgets_scroll_format">Widgets page %1$d of %2$d</string>
<!-- Clings -->
<!-- The title text for the workspace cling [CHAR_LIMIT=none] -->
@@ -237,4 +237,18 @@
<string name="cling_dismiss">OK</string>
<add-resource type="string" name="default_folder_name" />
+ <!-- Folder accessibility -->
+ <!-- The format string for when a folder is opened, speaks the dimensions -->
+ <string name="folder_opened">Folder opened, %1$d by %2$d</string>
+ <!-- Instruction that clicking outside will close folder -->
+ <string name="folder_tap_to_close">Tap to close folder</string>
+ <!-- Instruction that clicking outside will commit folder rename -->
+ <string name="folder_tap_to_rename">Tap to commit rename</string>
+ <!-- Indication that folder closed -->
+ <string name="folder_closed">Folder closed</string>
+ <!-- Folder renamed format -->
+ <string name="folder_renamed">Folder renamed to %1$s</string>
+ <!-- Folder name format -->
+ <string name="folder_name_format">Folder: %1$s</string>
+
</resources>
diff --git a/src/com/android/launcher2/DragLayer.java b/src/com/android/launcher2/DragLayer.java
index 28ef6a5..aa49ca1 100644
--- a/src/com/android/launcher2/DragLayer.java
+++ b/src/com/android/launcher2/DragLayer.java
@@ -32,6 +32,8 @@
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewParent;
+import android.view.accessibility.AccessibilityEvent;
+import android.view.accessibility.AccessibilityManager;
import android.view.animation.DecelerateInterpolator;
import android.view.animation.Interpolator;
import android.widget.FrameLayout;
@@ -65,6 +67,8 @@
private int[] mDropViewPos = new int[2];
private float mDropViewScale;
private float mDropViewAlpha;
+ private boolean mHoverPointClosesFolder = false;
+ private Rect mHitRect = new Rect();
/**
* Used to create a new DragLayer from XML.
@@ -89,6 +93,22 @@
return mDragController.dispatchKeyEvent(event) || super.dispatchKeyEvent(event);
}
+ private boolean isEventOverFolderTextRegion(Folder folder, MotionEvent ev) {
+ getDescendantRectRelativeToSelf(folder.getEditTextRegion(), mHitRect);
+ if (mHitRect.contains((int) ev.getX(), (int) ev.getY())) {
+ return true;
+ }
+ return false;
+ }
+
+ private boolean isEventOverFolder(Folder folder, MotionEvent ev) {
+ getDescendantRectRelativeToSelf(folder, mHitRect);
+ if (mHitRect.contains((int) ev.getX(), (int) ev.getY())) {
+ return true;
+ }
+ return false;
+ }
+
private boolean handleTouchDown(MotionEvent ev, boolean intercept) {
Rect hitRect = new Rect();
int x = (int) ev.getX();
@@ -110,15 +130,14 @@
Folder currentFolder = mLauncher.getWorkspace().getOpenFolder();
if (currentFolder != null && !mLauncher.isFolderClingVisible() && intercept) {
if (currentFolder.isEditingName()) {
- getDescendantRectRelativeToSelf(currentFolder.getEditTextRegion(), hitRect);
- if (!hitRect.contains(x, y)) {
+ if (!isEventOverFolderTextRegion(currentFolder, ev)) {
currentFolder.dismissEditingName();
return true;
}
}
getDescendantRectRelativeToSelf(currentFolder, hitRect);
- if (!hitRect.contains(x, y)) {
+ if (!isEventOverFolder(currentFolder, ev)) {
mLauncher.closeFolder();
return true;
}
@@ -138,6 +157,63 @@
}
@Override
+ public boolean onInterceptHoverEvent(MotionEvent ev) {
+ Folder currentFolder = mLauncher.getWorkspace().getOpenFolder();
+ if (currentFolder == null) {
+ return false;
+ } else {
+ if (AccessibilityManager.getInstance(mContext).isTouchExplorationEnabled()) {
+ final int action = ev.getAction();
+ boolean isOverFolder;
+ switch (action) {
+ case MotionEvent.ACTION_HOVER_ENTER:
+ isOverFolder = isEventOverFolder(currentFolder, ev);
+ if (!isOverFolder) {
+ sendTapOutsideFolderAccessibilityEvent(currentFolder.isEditingName());
+ mHoverPointClosesFolder = true;
+ return true;
+ } else if (isOverFolder) {
+ mHoverPointClosesFolder = false;
+ } else {
+ return true;
+ }
+ case MotionEvent.ACTION_HOVER_MOVE:
+ isOverFolder = isEventOverFolder(currentFolder, ev);
+ if (!isOverFolder && !mHoverPointClosesFolder) {
+ sendTapOutsideFolderAccessibilityEvent(currentFolder.isEditingName());
+ mHoverPointClosesFolder = true;
+ return true;
+ } else if (isOverFolder) {
+ mHoverPointClosesFolder = false;
+ } else {
+ return true;
+ }
+ }
+ }
+ }
+ return false;
+ }
+
+ private void sendTapOutsideFolderAccessibilityEvent(boolean isEditingName) {
+ if (AccessibilityManager.getInstance(mContext).isEnabled()) {
+ Folder currentFolder = mLauncher.getWorkspace().getOpenFolder();
+ int stringId = isEditingName ? R.string.folder_tap_to_rename : R.string.folder_tap_to_close;
+ AccessibilityEvent event = AccessibilityEvent.obtain(
+ AccessibilityEvent.TYPE_VIEW_FOCUSED);
+ onInitializeAccessibilityEvent(event);
+ event.getText().add(mContext.getString(stringId));
+ AccessibilityManager.getInstance(mContext).sendAccessibilityEvent(event);
+ }
+ }
+
+ @Override
+ public boolean onHoverEvent(MotionEvent ev) {
+ // If we've received this, we've already done the necessary handling
+ // in onInterceptHoverEvent. Return true to consume the event.
+ return false;
+ }
+
+ @Override
public boolean onTouchEvent(MotionEvent ev) {
boolean handled = false;
int action = ev.getAction();
diff --git a/src/com/android/launcher2/Folder.java b/src/com/android/launcher2/Folder.java
index 019ac70..33b5de1 100644
--- a/src/com/android/launcher2/Folder.java
+++ b/src/com/android/launcher2/Folder.java
@@ -37,6 +37,8 @@
import android.view.MenuItem;
import android.view.MotionEvent;
import android.view.View;
+import android.view.accessibility.AccessibilityEvent;
+import android.view.accessibility.AccessibilityManager;
import android.view.animation.AccelerateInterpolator;
import android.view.animation.DecelerateInterpolator;
import android.view.inputmethod.EditorInfo;
@@ -95,7 +97,6 @@
private Alarm mReorderAlarm = new Alarm();
private Alarm mOnExitAlarm = new Alarm();
private int mFolderNameHeight;
- private Rect mHitRect = new Rect();
private Rect mTempRect = new Rect();
private boolean mDragInProgress = false;
private boolean mDeleteFolderOnDropCompleted = false;
@@ -240,9 +241,14 @@
mFolderName.setHint(sHintText);
// Convert to a string here to ensure that no other state associated with the text field
// gets saved.
- mInfo.setTitle(mFolderName.getText().toString());
+ String newTitle = mFolderName.getText().toString();
+ mInfo.setTitle(newTitle);
LauncherModel.updateItemInDatabase(mLauncher, mInfo);
+ if (commit) {
+ sendCustomAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED,
+ String.format(mContext.getString(R.string.folder_renamed), newTitle));
+ }
// In order to clear the focus from the text field, we set the focus on ourself. This
// ensures that every time the field is clicked, focus is gained, giving reliable behavior.
requestFocus();
@@ -283,6 +289,12 @@
mFolderIcon = icon;
}
+ @Override
+ public boolean dispatchPopulateAccessibilityEvent(AccessibilityEvent event) {
+ // When the folder gets focus, we don't want to announce the list of items.
+ return true;
+ }
+
/**
* @return the FolderInfo object associated with this folder
*/
@@ -398,6 +410,9 @@
oa.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationStart(Animator animation) {
+ sendCustomAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED,
+ String.format(mContext.getString(R.string.folder_opened),
+ mContent.getCountX(), mContent.getCountY()));
mState = STATE_ANIMATING;
}
@Override
@@ -415,6 +430,15 @@
oa.start();
}
+ private void sendCustomAccessibilityEvent(int type, String text) {
+ if (AccessibilityManager.getInstance(mContext).isEnabled()) {
+ AccessibilityEvent event = AccessibilityEvent.obtain(type);
+ onInitializeAccessibilityEvent(event);
+ event.getText().add(text);
+ AccessibilityManager.getInstance(mContext).sendAccessibilityEvent(event);
+ }
+ }
+
private void setFocusOnFirstChild() {
View firstChild = mContent.getChildAt(0, 0);
if (firstChild != null) {
@@ -460,6 +484,8 @@
}
@Override
public void onAnimationStart(Animator animation) {
+ sendCustomAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED,
+ mContext.getString(R.string.folder_closed));
mState = STATE_ANIMATING;
}
});
diff --git a/src/com/android/launcher2/FolderIcon.java b/src/com/android/launcher2/FolderIcon.java
index 8945076..546f4b5 100644
--- a/src/com/android/launcher2/FolderIcon.java
+++ b/src/com/android/launcher2/FolderIcon.java
@@ -32,6 +32,7 @@
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
+import android.view.accessibility.AccessibilityEvent;
import android.view.animation.AccelerateInterpolator;
import android.view.animation.DecelerateInterpolator;
import android.widget.ImageView;
@@ -122,19 +123,18 @@
icon.mFolderName = (BubbleTextView) icon.findViewById(R.id.folder_icon_name);
icon.mFolderName.setText(folderInfo.title);
icon.mPreviewBackground = (ImageView) icon.findViewById(R.id.preview_background);
- icon.mPreviewBackground.setContentDescription(folderInfo.title);
icon.setTag(folderInfo);
icon.setOnClickListener(launcher);
icon.mInfo = folderInfo;
icon.mLauncher = launcher;
-
+ icon.setContentDescription(String.format(launcher.getString(R.string.folder_name_format),
+ folderInfo.title));
Folder folder = Folder.fromXml(launcher);
folder.setDragController(launcher.getDragController());
folder.setFolderIcon(icon);
folder.bind(folderInfo);
icon.mFolder = folder;
- Resources res = launcher.getResources();
icon.mFolderRingAnimator = new FolderRingAnimator(launcher, icon);
folderInfo.addListener(icon);
@@ -587,6 +587,7 @@
public void onTitleChanged(CharSequence title) {
mFolderName.setText(title.toString());
- mPreviewBackground.setContentDescription(title.toString());
+ setContentDescription(String.format(mContext.getString(R.string.folder_name_format),
+ title));
}
}
diff --git a/src/com/android/launcher2/LauncherApplication.java b/src/com/android/launcher2/LauncherApplication.java
index 755168c..6c8ba24 100644
--- a/src/com/android/launcher2/LauncherApplication.java
+++ b/src/com/android/launcher2/LauncherApplication.java
@@ -25,6 +25,8 @@
import android.content.res.Configuration;
import android.database.ContentObserver;
import android.os.Handler;
+import android.view.accessibility.AccessibilityEvent;
+import android.view.accessibility.AccessibilityManager;
import java.lang.ref.WeakReference;
@@ -40,7 +42,8 @@
super.onCreate();
// set sIsScreenXLarge and sScreenDensity *before* creating icon cache
- final int screenSize = getResources().getConfiguration().screenLayout & Configuration.SCREENLAYOUT_SIZE_MASK;
+ final int screenSize = getResources().getConfiguration().screenLayout &
+ Configuration.SCREENLAYOUT_SIZE_MASK;
sIsScreenLarge = screenSize == Configuration.SCREENLAYOUT_SIZE_LARGE ||
screenSize == Configuration.SCREENLAYOUT_SIZE_XLARGE;
sScreenDensity = getResources().getDisplayMetrics().density;
diff --git a/src/com/android/launcher2/Workspace.java b/src/com/android/launcher2/Workspace.java
index e4f1fe8..bf89078 100644
--- a/src/com/android/launcher2/Workspace.java
+++ b/src/com/android/launcher2/Workspace.java
@@ -1529,27 +1529,34 @@
return;
}
- if (mAnimator != null) {
- mAnimator.cancel();
- }
+ // Initialize animation arrays for the first time if necessary
+ initAnimationArrays();
+
+ // Cancel any running transition animations
+ if (mAnimator != null) mAnimator.cancel();
+ mAnimator = new AnimatorSet();
// Stop any scrolling, move to the current page right away
setCurrentPage((mNextPage != INVALID_PAGE) ? mNextPage : mCurrentPage);
- float finalScaleFactor = 1.0f;
- float finalBackgroundAlpha = (state == State.SPRING_LOADED) ? 1.0f : 0f;
- boolean normalState = false;
- State oldState = mState;
+ final State oldState = mState;
+ final boolean oldStateIsNormal = (oldState == State.NORMAL);
+ final boolean oldStateIsSpringLoaded = (oldState == State.SPRING_LOADED);
+ final boolean oldStateIsSmall = (oldState == State.SMALL);
mState = state;
+ final boolean stateIsNormal = (state == State.NORMAL);
+ final boolean stateIsSpringLoaded = (state == State.SPRING_LOADED);
+ final boolean stateIsSmall = (state == State.SMALL);
+ float finalScaleFactor = 1.0f;
+ float finalBackgroundAlpha = stateIsSpringLoaded ? 1.0f : 0f;
+ float translationX = 0;
+ float translationY = 0;
boolean zoomIn = true;
if (state != State.NORMAL) {
- finalScaleFactor = mSpringLoadedShrinkFactor - (state == State.SMALL ? 0.1f : 0);
- if (oldState == State.NORMAL && state == State.SMALL) {
+ finalScaleFactor = mSpringLoadedShrinkFactor - (stateIsSmall ? 0.1f : 0);
+ if (oldStateIsNormal && stateIsSmall) {
zoomIn = false;
- if (animated) {
- hideScrollingIndicator(true);
- }
setLayoutScale(finalScaleFactor);
updateChildrenLayersEnabled();
} else {
@@ -1558,34 +1565,34 @@
}
} else {
setLayoutScale(1.0f);
- normalState = true;
}
- float translationX = 0;
- float translationY = 0;
-
- mAnimator = new AnimatorSet();
-
- final int screenCount = getChildCount();
- initAnimationArrays();
-
final int duration = zoomIn ?
getResources().getInteger(R.integer.config_workspaceUnshrinkTime) :
getResources().getInteger(R.integer.config_appsCustomizeWorkspaceShrinkTime);
- for (int i = 0; i < screenCount; i++) {
- final CellLayout cl = (CellLayout)getChildAt(i);
- float finalAlphaValue = 0f;
+ for (int i = 0; i < getChildCount(); i++) {
+ final CellLayout cl = (CellLayout) getChildAt(i);
float rotation = 0f;
+ float initialAlpha = cl.getAlpha();
+ float finalAlphaMultiplierValue = 1f;
+ float finalAlpha = (!mFadeInAdjacentScreens || stateIsSpringLoaded ||
+ (i == mCurrentPage)) ? 1f : 0f;
- // Set the final alpha depending on whether we are fading side pages. On phone ui,
- // we don't do any of the rotation, or the fading alpha in portrait. See the
- // ctor and screenScrolled().
- if (mFadeInAdjacentScreens && normalState) {
- finalAlphaValue = (i == mCurrentPage) ? 1f : 0f;
- } else {
- finalAlphaValue = 1f;
+ // Determine the pages alpha during the state transition
+ if ((oldStateIsSmall && stateIsNormal) ||
+ (oldStateIsNormal && stateIsSmall)) {
+ // To/from workspace - only show the current page unless the transition is not
+ // animated and the animation end callback below doesn't run
+ if (i == mCurrentPage || !animated) {
+ finalAlpha = 1f;
+ finalAlphaMultiplierValue = 0f;
+ } else {
+ initialAlpha = 0f;
+ finalAlpha = 0f;
+ }
}
+ // Update the rotation of the screen (don't apply rotation on Phone UI)
if (LauncherApplication.isScreenLarge()) {
if (i < mCurrentPage) {
rotation = WORKSPACE_ROTATION;
@@ -1594,7 +1601,6 @@
}
}
- float finalAlphaMultiplierValue = 1f;
// If the screen is not xlarge, then don't rotate the CellLayouts
// NOTE: If we don't update the side pages alpha, then we should not hide the side
// pages. see unshrink().
@@ -1602,8 +1608,8 @@
translationX = getOffsetXForRotation(rotation, cl.getWidth(), cl.getHeight());
}
- mOldAlphas[i] = cl.getAlpha();
- mNewAlphas[i] = finalAlphaValue;
+ mOldAlphas[i] = initialAlpha;
+ mNewAlphas[i] = finalAlpha;
if (animated) {
mOldTranslationXs[i] = cl.getTranslationX();
mOldTranslationYs[i] = cl.getTranslationY();
@@ -1625,9 +1631,9 @@
cl.setTranslationY(translationY);
cl.setScaleX(finalScaleFactor);
cl.setScaleY(finalScaleFactor);
- cl.setBackgroundAlpha(0.0f);
+ cl.setBackgroundAlpha(finalBackgroundAlpha);
cl.setBackgroundAlphaMultiplier(finalAlphaMultiplierValue);
- cl.setAlpha(finalAlphaValue);
+ cl.setAlpha(finalAlpha);
cl.setRotationY(rotation);
mChangeStateAnimationListener.onAnimationEnd(null);
}
@@ -1641,6 +1647,21 @@
animWithInterpolator.setInterpolator(mZoomInInterpolator);
}
+ animWithInterpolator.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(android.animation.Animator animation) {
+ // The above code to determine initialAlpha and finalAlpha will ensure that only
+ // the current page is visible during (and subsequently, after) the transition
+ // animation. If fade adjacent pages is disabled, then re-enable the page
+ // visibility after the transition animation.
+ if (!mFadeInAdjacentScreens && stateIsNormal && oldStateIsSmall) {
+ for (int i = 0; i < getChildCount(); i++) {
+ final CellLayout cl = (CellLayout) getChildAt(i);
+ cl.setAlpha(1f);
+ }
+ }
+ }
+ });
animWithInterpolator.addUpdateListener(new LauncherAnimatorUpdateListener() {
public void onAnimationUpdate(float a, float b) {
mTransitionProgress = b;
@@ -1649,7 +1670,7 @@
return;
}
invalidate();
- for (int i = 0; i < screenCount; i++) {
+ for (int i = 0; i < getChildCount(); i++) {
final CellLayout cl = (CellLayout) getChildAt(i);
cl.fastInvalidate();
cl.setFastTranslationX(a * mOldTranslationXs[i] + b * mNewTranslationXs[i]);
@@ -1674,7 +1695,7 @@
// an optimization, but not required
return;
}
- for (int i = 0; i < screenCount; i++) {
+ for (int i = 0; i < getChildCount(); i++) {
final CellLayout cl = (CellLayout) getChildAt(i);
cl.setFastRotationY(a * mOldRotationYs[i] + b * mNewRotationYs[i]);
}
@@ -1689,7 +1710,7 @@
mAnimator.start();
}
- if (state == State.SPRING_LOADED) {
+ if (stateIsSpringLoaded) {
// Right now we're covered by Apps Customize
// Show the background gradient immediately, so the gradient will
// be showing once AppsCustomize disappears