Home | History | Annotate | Download | only in accessibility
      1 /*
      2  * Copyright (C) 2015 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package com.android.launcher3.accessibility;
     18 
     19 import android.content.Context;
     20 import android.graphics.Rect;
     21 import android.support.v4.view.accessibility.AccessibilityNodeInfoCompat;
     22 import android.text.TextUtils;
     23 import android.view.View;
     24 
     25 import com.android.launcher3.AppInfo;
     26 import com.android.launcher3.CellLayout;
     27 import com.android.launcher3.FolderInfo;
     28 import com.android.launcher3.ItemInfo;
     29 import com.android.launcher3.Launcher;
     30 import com.android.launcher3.accessibility.LauncherAccessibilityDelegate.DragType;
     31 import com.android.launcher3.R;
     32 import com.android.launcher3.ShortcutInfo;
     33 import com.android.launcher3.dragndrop.DragLayer;
     34 
     35 /**
     36  * Implementation of {@link DragAndDropAccessibilityDelegate} to support DnD on workspace.
     37  */
     38 public class WorkspaceAccessibilityHelper extends DragAndDropAccessibilityDelegate {
     39 
     40     private final Rect mTempRect = new Rect();
     41     private final int[] mTempCords = new int[2];
     42 
     43     public WorkspaceAccessibilityHelper(CellLayout layout) {
     44         super(layout);
     45     }
     46 
     47     /**
     48      * Find the virtual view id corresponding to the top left corner of any drop region by which
     49      * the passed id is contained. For an icon, this is simply
     50      */
     51     @Override
     52     protected int intersectsValidDropTarget(int id) {
     53         int mCountX = mView.getCountX();
     54         int mCountY = mView.getCountY();
     55 
     56         int x = id % mCountX;
     57         int y = id / mCountX;
     58         LauncherAccessibilityDelegate.DragInfo dragInfo = mDelegate.getDragInfo();
     59 
     60         if (dragInfo.dragType == DragType.WIDGET && mView.isHotseat()) {
     61             return INVALID_POSITION;
     62         }
     63 
     64         if (dragInfo.dragType == DragType.WIDGET) {
     65             // For a widget, every cell must be vacant. In addition, we will return any valid
     66             // drop target by which the passed id is contained.
     67             boolean fits = false;
     68 
     69             // These represent the amount that we can back off if we hit a problem. They
     70             // get consumed as we move up and to the right, trying new regions.
     71             int spanX = dragInfo.info.spanX;
     72             int spanY = dragInfo.info.spanY;
     73 
     74             for (int m = 0; m < spanX; m++) {
     75                 for (int n = 0; n < spanY; n++) {
     76 
     77                     fits = true;
     78                     int x0 = x - m;
     79                     int y0 = y - n;
     80 
     81                     if (x0 < 0 || y0 < 0) continue;
     82 
     83                     for (int i = x0; i < x0 + spanX; i++) {
     84                         if (!fits) break;
     85                         for (int j = y0; j < y0 + spanY; j++) {
     86                             if (i >= mCountX || j >= mCountY || mView.isOccupied(i, j)) {
     87                                 fits = false;
     88                                 break;
     89                             }
     90                         }
     91                     }
     92                     if (fits) {
     93                         return x0 + mCountX * y0;
     94                     }
     95                 }
     96             }
     97             return INVALID_POSITION;
     98         } else {
     99             // For an icon, we simply check the view directly below
    100             View child = mView.getChildAt(x, y);
    101             if (child == null || child == dragInfo.item) {
    102                 // Empty cell. Good for an icon or folder.
    103                 return id;
    104             } else if (dragInfo.dragType != DragType.FOLDER) {
    105                 // For icons, we can consider cells that have another icon or a folder.
    106                 ItemInfo info = (ItemInfo) child.getTag();
    107                 if (info instanceof AppInfo || info instanceof FolderInfo ||
    108                         info instanceof ShortcutInfo) {
    109                     return id;
    110                 }
    111             }
    112             return INVALID_POSITION;
    113         }
    114     }
    115 
    116     @Override
    117     protected String getConfirmationForIconDrop(int id) {
    118         int x = id % mView.getCountX();
    119         int y = id / mView.getCountX();
    120         LauncherAccessibilityDelegate.DragInfo dragInfo = mDelegate.getDragInfo();
    121 
    122         View child = mView.getChildAt(x, y);
    123         if (child == null || child == dragInfo.item) {
    124             return mContext.getString(R.string.item_moved);
    125         } else {
    126             ItemInfo info = (ItemInfo) child.getTag();
    127             if (info instanceof AppInfo || info instanceof ShortcutInfo) {
    128                 return mContext.getString(R.string.folder_created);
    129 
    130             } else if (info instanceof FolderInfo) {
    131                 return mContext.getString(R.string.added_to_folder);
    132             }
    133         }
    134         return "";
    135     }
    136 
    137     @Override
    138     protected void onPopulateNodeForVirtualView(int id, AccessibilityNodeInfoCompat node) {
    139         super.onPopulateNodeForVirtualView(id, node);
    140 
    141 
    142         // ExploreByTouchHelper does not currently handle view scale.
    143         // Update BoundsInScreen to appropriate value.
    144         DragLayer dragLayer = Launcher.getLauncher(mView.getContext()).getDragLayer();
    145         mTempCords[0] = mTempCords[1] = 0;
    146         float scale = dragLayer.getDescendantCoordRelativeToSelf(mView, mTempCords);
    147 
    148         node.getBoundsInParent(mTempRect);
    149         mTempRect.left = mTempCords[0] + (int) (mTempRect.left * scale);
    150         mTempRect.right = mTempCords[0] + (int) (mTempRect.right * scale);
    151         mTempRect.top = mTempCords[1] + (int) (mTempRect.top * scale);
    152         mTempRect.bottom = mTempCords[1] + (int) (mTempRect.bottom * scale);
    153         node.setBoundsInScreen(mTempRect);
    154     }
    155 
    156     @Override
    157     protected String getLocationDescriptionForIconDrop(int id) {
    158         int x = id % mView.getCountX();
    159         int y = id / mView.getCountX();
    160         LauncherAccessibilityDelegate.DragInfo dragInfo = mDelegate.getDragInfo();
    161 
    162         View child = mView.getChildAt(x, y);
    163         if (child == null || child == dragInfo.item) {
    164             if (mView.isHotseat()) {
    165                 return mContext.getString(R.string.move_to_hotseat_position, id + 1);
    166             } else {
    167                 return mContext.getString(R.string.move_to_empty_cell, y + 1, x + 1);
    168             }
    169         } else {
    170             return getDescriptionForDropOver(child, mContext);
    171         }
    172     }
    173 
    174     public static String getDescriptionForDropOver(View overChild, Context context) {
    175         ItemInfo info = (ItemInfo) overChild.getTag();
    176         if (info instanceof ShortcutInfo) {
    177             return context.getString(R.string.create_folder_with, info.title);
    178         } else if (info instanceof FolderInfo) {
    179             if (TextUtils.isEmpty(info.title)) {
    180                 // Find the first item in the folder.
    181                 FolderInfo folder = (FolderInfo) info;
    182                 ShortcutInfo firstItem = null;
    183                 for (ShortcutInfo shortcut : folder.contents) {
    184                     if (firstItem == null || firstItem.rank > shortcut.rank) {
    185                         firstItem = shortcut;
    186                     }
    187                 }
    188 
    189                 if (firstItem != null) {
    190                     return context.getString(R.string.add_to_folder_with_app, firstItem.title);
    191                 }
    192             }
    193             return context.getString(R.string.add_to_folder, info.title);
    194         }
    195         return "";
    196     }
    197 }
    198