@Override
  public void prepareViewToLeavePool(TaskView tv, Task task, boolean isNewView) {
    // Rebind the task and request that this task's data be filled into the TaskView
    tv.onTaskBound(task);

    // Mark the launch task as fullscreen
    if (Constants.DebugFlags.App.EnableScreenshotAppTransition && mAwaitingFirstLayout) {
      if (task.isLaunchTarget) {
        tv.setIsFullScreen(true);
      }
    }

    // Load the task data
    RecentsTaskLoader.getInstance().loadTaskData(task);

    // Sanity check, the task view should always be clipping against the stack at this point,
    // but just in case, re-enable it here
    tv.setClipViewInStack(true);

    // If the doze trigger has already fired, then update the state for this task view
    if (mUIDozeTrigger.hasTriggered()) {
      tv.setNoUserInteractionState();
    }

    // If we've finished the start animation, then ensure we always enable the focus animations
    if (mStartEnterAnimationCompleted) {
      tv.enableFocusAnimations();
    }

    // Find the index where this task should be placed in the stack
    int insertIndex = -1;
    int taskIndex = mStack.indexOfTask(task);
    if (taskIndex != -1) {
      int childCount = getChildCount();
      for (int i = 0; i < childCount; i++) {
        Task tvTask = ((TaskView) getChildAt(i)).getTask();
        if (taskIndex < mStack.indexOfTask(tvTask)) {
          insertIndex = i;
          break;
        }
      }
    }

    // Add/attach the view to the hierarchy
    if (isNewView) {
      addView(tv, insertIndex);

      // Set the callbacks and listeners for this new view
      tv.setTouchEnabled(true);
      tv.setCallbacks(this);
    } else {
      attachViewToParent(tv, insertIndex, tv.getLayoutParams());
    }
  }
 @Override
 public void onInitializeAccessibilityEvent(AccessibilityEvent event) {
   super.onInitializeAccessibilityEvent(event);
   int childCount = getChildCount();
   if (childCount > 0) {
     TaskView backMostTask = (TaskView) getChildAt(0);
     TaskView frontMostTask = (TaskView) getChildAt(childCount - 1);
     event.setFromIndex(mStack.indexOfTask(backMostTask.getTask()));
     event.setToIndex(mStack.indexOfTask(frontMostTask.getTask()));
     event.setContentDescription(frontMostTask.getTask().activityLabel);
   }
   event.setItemCount(mStack.getTaskCount());
   event.setScrollY(mStackScroller.mScroller.getCurrY());
   event.setMaxScrollY(mStackScroller.progressToScrollRange(mLayoutAlgorithm.mMaxScrollP));
 }
 @Override
 public void onTaskViewDismissed(TaskView tv) {
   Task task = tv.getTask();
   int taskIndex = mStack.indexOfTask(task);
   boolean taskWasFocused = tv.isFocusedTask();
   // Announce for accessibility
   tv.announceForAccessibility(
       getContext()
           .getString(R.string.accessibility_recents_item_dismissed, tv.getTask().activityLabel));
   // Remove the task from the view
   mStack.removeTask(task);
   // If the dismissed task was focused, then we should focus the next task in front
   if (taskWasFocused) {
     ArrayList<Task> tasks = mStack.getTasks();
     int nextTaskIndex = Math.min(tasks.size() - 1, taskIndex);
     if (nextTaskIndex >= 0) {
       Task nextTask = tasks.get(nextTaskIndex);
       TaskView nextTv = getChildViewForTask(nextTask);
       nextTv.setFocusedTask();
     }
   }
 }
  /** Synchronizes the views with the model */
  boolean synchronizeStackViewsWithModel() {
    if (mStackViewsDirty) {
      RecentsTaskLoader loader = RecentsTaskLoader.getInstance();
      SystemServicesProxy ssp = loader.getSystemServicesProxy();

      // Get all the task transforms
      ArrayList<Task> tasks = mStack.getTasks();
      float stackScroll = mStackScroller.getStackScroll();
      int[] visibleRange = mTmpVisibleRange;
      boolean isValidVisibleRange =
          updateStackTransforms(mCurrentTaskTransforms, tasks, stackScroll, visibleRange, false);
      if (mDebugOverlay != null) {
        mDebugOverlay.setText("vis[" + visibleRange[1] + "-" + visibleRange[0] + "]");
      }

      // Return all the invisible children to the pool
      mTmpTaskViewMap.clear();
      int childCount = getChildCount();
      for (int i = childCount - 1; i >= 0; i--) {
        TaskView tv = (TaskView) getChildAt(i);
        Task task = tv.getTask();
        int taskIndex = mStack.indexOfTask(task);
        if (visibleRange[1] <= taskIndex && taskIndex <= visibleRange[0]) {
          mTmpTaskViewMap.put(task, tv);
        } else {
          mViewPool.returnViewToPool(tv);
        }
      }

      // Pick up all the newly visible children and update all the existing children
      for (int i = visibleRange[0]; isValidVisibleRange && i >= visibleRange[1]; i--) {
        Task task = tasks.get(i);
        TaskViewTransform transform = mCurrentTaskTransforms.get(i);
        TaskView tv = mTmpTaskViewMap.get(task);
        int taskIndex = mStack.indexOfTask(task);

        if (tv == null) {
          tv = mViewPool.pickUpViewFromPool(task, task);

          if (mStackViewsAnimationDuration > 0) {
            // For items in the list, put them in start animating them from the
            // approriate ends of the list where they are expected to appear
            if (Float.compare(transform.p, 0f) <= 0) {
              mLayoutAlgorithm.getStackTransform(0f, 0f, mTmpTransform, null);
            } else {
              mLayoutAlgorithm.getStackTransform(1f, 0f, mTmpTransform, null);
            }
            tv.updateViewPropertiesToTaskTransform(mTmpTransform, 0);
          }
        }

        // Animate the task into place
        tv.updateViewPropertiesToTaskTransform(
            mCurrentTaskTransforms.get(taskIndex),
            mStackViewsAnimationDuration,
            mRequestUpdateClippingListener);

        // Request accessibility focus on the next view if we removed the task
        // that previously held accessibility focus
        childCount = getChildCount();
        if (childCount > 0 && ssp.isTouchExplorationEnabled()) {
          TaskView atv = (TaskView) getChildAt(childCount - 1);
          int indexOfTask = mStack.indexOfTask(atv.getTask());
          if (mPrevAccessibilityFocusedIndex != indexOfTask) {
            tv.requestAccessibilityFocus();
            mPrevAccessibilityFocusedIndex = indexOfTask;
          }
        }
      }

      // Reset the request-synchronize params
      mStackViewsAnimationDuration = 0;
      mStackViewsDirty = false;
      mStackViewsClipDirty = true;
      return true;
    }
    return false;
  }
 @Override
 public void onTaskViewFocusChanged(TaskView tv, boolean focused) {
   if (focused) {
     mFocusedTaskIndex = mStack.indexOfTask(tv.getTask());
   }
 }
Exemple #6
0
  /** ** TaskStackView.TaskStackCallbacks Implementation *** */
  @Override
  public void onTaskViewClicked(
      final TaskStackView stackView,
      final TaskView tv,
      final TaskStack stack,
      final Task task,
      final boolean lockToTask) {
    Log.d(TAG, "onTaskViewClicked: ");
    // Notify any callbacks of the launching of a new task
    if (mCb != null) {
      mCb.onTaskViewClicked();
    }

    // Upfront the processing of the thumbnail
    TaskViewTransform transform = new TaskViewTransform();
    View sourceView;
    int offsetX = 0;
    int offsetY = 0;
    float stackScroll = stackView.getScroller().getStackScroll();
    if (tv == null) {
      // If there is no actual task view, then use the stack view as the source view
      // and then offset to the expected transform rect, but bound this to just
      // outside the display rect (to ensure we don't animate from too far away)
      sourceView = stackView;
      transform =
          stackView.getStackAlgorithm().getStackTransform(task, stackScroll, transform, null);
      offsetX = transform.rect.left;
      offsetY = mConfig.displayRect.height();
    } else {
      sourceView = tv.mThumbnailView;
      transform =
          stackView.getStackAlgorithm().getStackTransform(task, stackScroll, transform, null);
    }

    // Compute the thumbnail to scale up from
    final SystemServicesProxy ssp = RecentsTaskLoader.getInstance().getSystemServicesProxy();
    ActivityOptions opts = null;
    if (task.thumbnail != null && task.thumbnail.getWidth() > 0 && task.thumbnail.getHeight() > 0) {
      ActivityOptions.OnAnimationStartedListener animStartedListener = null;
      if (lockToTask) {
        animStartedListener =
            new ActivityOptions.OnAnimationStartedListener() {
              boolean mTriggered = false;

              @Override
              public void onAnimationStarted() {
                if (!mTriggered) {
                  postDelayed(
                      new Runnable() {
                        @Override
                        public void run() {
                          mCb.onScreenPinningRequest();
                        }
                      },
                      350);
                  mTriggered = true;
                }
              }
            };
      }
      if (tv != null) {
        postDrawHeaderThumbnailTransitionRunnable(
            tv, offsetX, offsetY, transform, animStartedListener);
      }
      if (mConfig.multiStackEnabled) {
        opts =
            ActivityOptions.makeCustomAnimation(
                sourceView.getContext(),
                R.anim.recents_from_unknown_enter,
                R.anim.recents_from_unknown_exit,
                sourceView.getHandler(),
                animStartedListener);
      } else {
        opts =
            ActivityOptions.makeThumbnailAspectScaleUpAnimation(
                sourceView,
                Bitmap.createBitmap(1, 1, Bitmap.Config.ALPHA_8).createAshmemBitmap(),
                offsetX,
                offsetY,
                transform.rect.width(),
                transform.rect.height(),
                sourceView.getHandler(),
                animStartedListener);
      }
    }

    final ActivityOptions launchOpts = opts;
    final Runnable launchRunnable =
        new Runnable() {
          @Override
          public void run() {
            if (task.isActive) {
              // Bring an active task to the foreground
              ssp.moveTaskToFront(task.key.id, launchOpts);
            } else {
              if (ssp.startActivityFromRecents(
                  getContext(), task.key.id, task.activityLabel, launchOpts)) {
                if (launchOpts == null && lockToTask) {
                  mCb.onScreenPinningRequest();
                }
              } else {
                // Dismiss the task and return the user to home if we fail to
                // launch the task
                onTaskViewDismissed(task);
                if (mCb != null) {
                  mCb.onTaskLaunchFailed();
                }

                // Keep track of failed launches
                MetricsLogger.count(getContext(), "overview_task_launch_failed", 1);
              }
            }
          }
        };

    // Keep track of the index of the task launch
    int taskIndexFromFront = 0;
    int taskIndex = stack.indexOfTask(task);
    if (taskIndex > -1) {
      taskIndexFromFront = stack.getTaskCount() - taskIndex - 1;
    }
    MetricsLogger.histogram(getContext(), "overview_task_launch_index", taskIndexFromFront);

    // Launch the app right away if there is no task view, otherwise, animate the icon out first
    if (tv == null) {
      launchRunnable.run();
    } else {
      if (task.group != null && !task.group.isFrontMostTask(task)) {
        // For affiliated tasks that are behind other tasks, we must animate the front cards
        // out of view before starting the task transition
        stackView.startLaunchTaskAnimation(tv, launchRunnable, lockToTask);
      } else {
        // Otherwise, we can start the task transition immediately
        stackView.startLaunchTaskAnimation(tv, null, lockToTask);
        launchRunnable.run();
      }
    }
  }