Example #1
0
  /** This is called with the full size of the window since we are handling our own insets. */
  @Override
  protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
    Log.d(TAG, "onLayout: ");
    // Get the search bar bounds so that we lay it out
    if (mSearchBar != null) {
      Rect searchBarSpaceBounds = new Rect();
      mConfig.getSearchBarBounds(
          getMeasuredWidth(), getMeasuredHeight(), mConfig.systemInsets.top, searchBarSpaceBounds);
      mSearchBar.layout(
          searchBarSpaceBounds.left,
          searchBarSpaceBounds.top,
          searchBarSpaceBounds.right,
          searchBarSpaceBounds.bottom);
    }

    // Layout each TaskStackView with the full width and height of the window since the
    // transition view is a child of that stack view
    List<TaskStackView> stackViews = getTaskStackViews();
    int stackCount = stackViews.size();
    for (int i = 0; i < stackCount; i++) {
      TaskStackView stackView = stackViews.get(i);
      if (stackView.getVisibility() != GONE) {
        stackView.layout(
            left, top, left + stackView.getMeasuredWidth(), top + stackView.getMeasuredHeight());
      }
    }
  }
  /** Launches the task that Recents was launched from, if possible */
  public boolean launchPreviousTask() {
    // Get the first stack view
    int childCount = getChildCount();
    for (int i = 0; i < childCount; i++) {
      View child = getChildAt(i);
      if (child != mSearchBar) {
        TaskStackView stackView = (TaskStackView) child;
        TaskStack stack = stackView.mStack;
        ArrayList<Task> tasks = stack.getTasks();

        // Find the launch task in the stack
        if (!tasks.isEmpty()) {
          int taskCount = tasks.size();
          for (int j = 0; j < taskCount; j++) {
            if (tasks.get(j).isLaunchTarget) {
              Task task = tasks.get(j);
              TaskView tv = stackView.getChildViewForTask(task);
              onTaskViewClicked(stackView, tv, stack, task, false);
              return true;
            }
          }
        }
      }
    }
    return false;
  }
  /** Set/get the bsp root node */
  public void setTaskStacks(ArrayList<TaskStack> stacks) {
    // Remove all TaskStackViews (but leave the search bar)
    int childCount = getChildCount();
    for (int i = childCount - 1; i >= 0; i--) {
      View v = getChildAt(i);
      if (v != mSearchBar) {
        removeViewAt(i);
      }
    }

    // Create and add all the stacks for this partition of space.
    mStacks = stacks;
    int numStacks = mStacks.size();
    for (int i = 0; i < numStacks; i++) {
      TaskStack stack = mStacks.get(i);
      TaskStackView stackView = new TaskStackView(getContext(), stack);
      stackView.setCallbacks(this);
      // Enable debug mode drawing
      if (mConfig.debugModeEnabled) {
        stackView.setDebugOverlay(mDebugOverlay);
      }
      addView(stackView);
    }

    // Reset the launched state
    mAlreadyLaunchingTask = false;
  }
  /** This is called with the full size of the window since we are handling our own insets. */
  @Override
  protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    int width = MeasureSpec.getSize(widthMeasureSpec);
    int height = MeasureSpec.getSize(heightMeasureSpec);

    // Get the search bar bounds and measure the search bar layout
    if (mSearchBar != null) {
      Rect searchBarSpaceBounds = new Rect();
      mConfig.getSearchBarBounds(width, height, mConfig.systemInsets.top, searchBarSpaceBounds);
      mSearchBar.measure(
          MeasureSpec.makeMeasureSpec(searchBarSpaceBounds.width(), MeasureSpec.EXACTLY),
          MeasureSpec.makeMeasureSpec(searchBarSpaceBounds.height(), MeasureSpec.EXACTLY));
    }

    Rect taskStackBounds = new Rect();
    mConfig.getTaskStackBounds(
        width, height, mConfig.systemInsets.top, mConfig.systemInsets.right, taskStackBounds);

    // Measure each TaskStackView with the full width and height of the window since the
    // transition view is a child of that stack view
    int childCount = getChildCount();
    for (int i = 0; i < childCount; i++) {
      View child = getChildAt(i);
      if (child != mSearchBar && child.getVisibility() != GONE) {
        TaskStackView tsv = (TaskStackView) child;
        // Set the insets to be the top/left inset + search bounds
        tsv.setStackInsetRect(taskStackBounds);
        tsv.measure(widthMeasureSpec, heightMeasureSpec);
      }
    }

    setMeasuredDimension(width, height);
  }
Example #5
0
  /** Launches the task that Recents was launched from, if possible */
  public boolean launchPreviousTask() {
    Log.d(TAG, "launchPreviousTask: ");
    // Get the first stack view
    List<TaskStackView> stackViews = getTaskStackViews();
    int stackCount = stackViews.size();
    for (int i = 0; i < stackCount; i++) {
      TaskStackView stackView = stackViews.get(i);
      TaskStack stack = stackView.getStack();
      ArrayList<Task> tasks = stack.getTasks();

      // Find the launch task in the stack
      if (!tasks.isEmpty()) {
        int taskCount = tasks.size();
        for (int j = 0; j < taskCount; j++) {
          if (tasks.get(j).isLaunchTarget) {
            Task task = tasks.get(j);
            TaskView tv = stackView.getChildViewForTask(task);
            onTaskViewClicked(stackView, tv, stack, task, false);
            return true;
          }
        }
      }
    }
    return false;
  }
  /** Returns the transition rect for the given task id. */
  TaskViewTransform getThumbnailTransitionTransform(
      TaskStack stack, TaskStackView stackView, int runningTaskId, Task runningTaskOut) {
    // Find the running task in the TaskStack
    Task task = null;
    ArrayList<Task> tasks = stack.getTasks();
    if (runningTaskId != -1) {
      // Otherwise, try and find the task with the
      int taskCount = tasks.size();
      for (int i = taskCount - 1; i >= 0; i--) {
        Task t = tasks.get(i);
        if (t.key.id == runningTaskId) {
          task = t;
          runningTaskOut.copyFrom(t);
          break;
        }
      }
    }
    if (task == null) {
      // If no task is specified or we can not find the task just use the front most one
      task = tasks.get(tasks.size() - 1);
      runningTaskOut.copyFrom(task);
    }

    // Get the transform for the running task
    stackView.getScroller().setStackScrollToInitialState();
    mTmpTransform =
        stackView
            .getStackAlgorithm()
            .getStackTransform(task, stackView.getScroller().getStackScroll(), mTmpTransform, null);
    return mTmpTransform;
  }
Example #7
0
 /** Final callback after Recents is finally hidden. */
 public void onRecentsHidden() {
   // Notify each task stack view
   List<TaskStackView> stackViews = getTaskStackViews();
   int stackCount = stackViews.size();
   for (int i = 0; i < stackCount; i++) {
     TaskStackView stackView = stackViews.get(i);
     stackView.onRecentsHidden();
   }
 }
Example #8
0
 /** Notifies each task view of the user interaction. */
 public void onUserInteraction() {
   Log.d(TAG, "onUserInteraction: ");
   // Get the first stack view
   List<TaskStackView> stackViews = getTaskStackViews();
   int stackCount = stackViews.size();
   for (int i = 0; i < stackCount; i++) {
     TaskStackView stackView = stackViews.get(i);
     stackView.onUserInteraction();
   }
 }
 /** Requests all task stacks to start their enter-recents animation */
 public void startEnterRecentsAnimation(ViewAnimation.TaskViewEnterContext ctx) {
   int childCount = getChildCount();
   for (int i = 0; i < childCount; i++) {
     View child = getChildAt(i);
     if (child != mSearchBar) {
       TaskStackView stackView = (TaskStackView) child;
       stackView.startEnterRecentsAnimation(ctx);
     }
   }
 }
Example #10
0
 /** ** RecentsPackageMonitor.PackageCallbacks Implementation *** */
 @Override
 public void onPackagesChanged(RecentsPackageMonitor monitor, String packageName, int userId) {
   // Propagate this event down to each task stack view
   List<TaskStackView> stackViews = getTaskStackViews();
   int stackCount = stackViews.size();
   for (int i = 0; i < stackCount; i++) {
     TaskStackView stackView = stackViews.get(i);
     stackView.onPackagesChanged(monitor, packageName, userId);
   }
 }
 /** Notifies each task view of the user interaction. */
 public void onUserInteraction() {
   // Get the first stack view
   int childCount = getChildCount();
   for (int i = 0; i < childCount; i++) {
     View child = getChildAt(i);
     if (child != mSearchBar) {
       TaskStackView stackView = (TaskStackView) child;
       stackView.onUserInteraction();
     }
   }
 }
 /** ** RecentsPackageMonitor.PackageCallbacks Implementation *** */
 @Override
 public void onComponentRemoved(HashSet<ComponentName> cns) {
   // Propagate this event down to each task stack view
   int childCount = getChildCount();
   for (int i = 0; i < childCount; i++) {
     View child = getChildAt(i);
     if (child != mSearchBar) {
       TaskStackView stackView = (TaskStackView) child;
       stackView.onComponentRemoved(cns);
     }
   }
 }
 /** Focuses the next task in the first stack view */
 public void focusNextTask(boolean forward) {
   // Get the first stack view
   int childCount = getChildCount();
   for (int i = 0; i < childCount; i++) {
     View child = getChildAt(i);
     if (child != mSearchBar) {
       TaskStackView stackView = (TaskStackView) child;
       stackView.focusNextTask(forward);
       break;
     }
   }
 }
 /** Dismisses the focused task. */
 public void dismissFocusedTask() {
   // Get the first stack view
   int childCount = getChildCount();
   for (int i = 0; i < childCount; i++) {
     View child = getChildAt(i);
     if (child != mSearchBar) {
       TaskStackView stackView = (TaskStackView) child;
       stackView.dismissFocusedTask();
       break;
     }
   }
 }
  /** Requests all task stacks to start their exit-recents animation */
  public void startExitToHomeAnimation(ViewAnimation.TaskViewExitContext ctx) {
    int childCount = getChildCount();
    for (int i = 0; i < childCount; i++) {
      View child = getChildAt(i);
      if (child != mSearchBar) {
        TaskStackView stackView = (TaskStackView) child;
        stackView.startExitToHomeAnimation(ctx);
      }
    }

    // Notify of the exit animation
    mCb.onExitToHomeAnimationTriggered();
  }
Example #16
0
  /** Requests all task stacks to start their enter-recents animation */
  public void startEnterRecentsAnimation(ViewAnimation.TaskViewEnterContext ctx) {
    Log.d(TAG, "startEnterRecentsAnimation: ");
    // We have to increment/decrement the post animation trigger in case there are no children
    // to ensure that it runs
    ctx.postAnimationTrigger.increment();

    List<TaskStackView> stackViews = getTaskStackViews();
    int stackCount = stackViews.size();
    for (int i = 0; i < stackCount; i++) {
      TaskStackView stackView = stackViews.get(i);
      stackView.startEnterRecentsAnimation(ctx);
    }
    ctx.postAnimationTrigger.decrement();
  }
  /**
   * Caches the header thumbnail used for a window animation asynchronously into {@link
   * #mThumbnailTransitionBitmapCache}.
   */
  void preCacheThumbnailTransitionBitmapAsync(
      ActivityManager.RunningTaskInfo topTask,
      TaskStack stack,
      TaskStackView stackView,
      boolean isTopTaskHome) {
    preloadIcon(topTask);

    // Update the destination rect
    mDummyStackView.updateMinMaxScrollForStack(stack, mTriggeredFromAltTab, isTopTaskHome);
    final Task toTask = new Task();
    final TaskViewTransform toTransform =
        getThumbnailTransitionTransform(stack, stackView, topTask.id, toTask);
    new AsyncTask<Void, Void, Bitmap>() {
      @Override
      protected Bitmap doInBackground(Void... params) {
        return drawThumbnailTransitionBitmap(toTask, toTransform);
      }

      @Override
      protected void onPostExecute(Bitmap bitmap) {
        mThumbnailTransitionBitmapCache = bitmap;
        mThumbnailTransitionBitmapCacheKey = toTask;
      }
    }.execute();
  }
Example #18
0
  /** This is called with the full size of the window since we are handling our own insets. */
  @Override
  protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    Log.d(TAG, "onMeasure: ");
    int width = MeasureSpec.getSize(widthMeasureSpec);
    int height = MeasureSpec.getSize(heightMeasureSpec);

    // Get the search bar bounds and measure the search bar layout
    Rect searchBarSpaceBounds = new Rect();
    if (mSearchBar != null) {
      mConfig.getSearchBarBounds(width, height, mConfig.systemInsets.top, searchBarSpaceBounds);
      mSearchBar.measure(
          MeasureSpec.makeMeasureSpec(searchBarSpaceBounds.width(), MeasureSpec.EXACTLY),
          MeasureSpec.makeMeasureSpec(searchBarSpaceBounds.height(), MeasureSpec.EXACTLY));
    }

    Rect taskStackBounds = new Rect();
    mConfig.getAvailableTaskStackBounds(
        width,
        height,
        mConfig.systemInsets.top,
        mConfig.systemInsets.right,
        searchBarSpaceBounds,
        taskStackBounds);

    // Measure each TaskStackView with the full width and height of the window since the
    // transition view is a child of that stack view
    List<TaskStackView> stackViews = getTaskStackViews();
    List<Rect> stackViewsBounds = mLayoutAlgorithm.computeStackRects(stackViews, taskStackBounds);
    int stackCount = stackViews.size();
    for (int i = 0; i < stackCount; i++) {
      TaskStackView stackView = stackViews.get(i);
      if (stackView.getVisibility() != GONE) {
        // We are going to measure the TaskStackView with the whole RecentsView dimensions,
        // but the actual stack is going to be inset to the bounds calculated by the layout
        // algorithm
        stackView.setStackInsetRect(stackViewsBounds.get(i));
        stackView.measure(widthMeasureSpec, heightMeasureSpec);
      }
    }

    setMeasuredDimension(width, height);
  }
Example #19
0
 /** Launches a given task. */
 public boolean launchTask(Task task) {
   Log.d(TAG, "launchTask: ");
   // Get the first stack view
   List<TaskStackView> stackViews = getTaskStackViews();
   int stackCount = stackViews.size();
   for (int i = 0; i < stackCount; i++) {
     TaskStackView stackView = stackViews.get(i);
     TaskStack stack = stackView.getStack();
     // Iterate the stack views and try and find the given task.
     List<TaskView> taskViews = stackView.getTaskViews();
     int taskViewCount = taskViews.size();
     for (int j = 0; j < taskViewCount; j++) {
       TaskView tv = taskViews.get(j);
       if (tv.getTask() == task) {
         onTaskViewClicked(stackView, tv, stack, task, false);
         return true;
       }
     }
   }
   return false;
 }
 /** Launches the focused task from the first stack if possible */
 public boolean launchFocusedTask() {
   // Get the first stack view
   int childCount = getChildCount();
   for (int i = 0; i < childCount; i++) {
     View child = getChildAt(i);
     if (child != mSearchBar) {
       TaskStackView stackView = (TaskStackView) child;
       TaskStack stack = stackView.mStack;
       // Iterate the stack views and try and find the focused task
       int taskCount = stackView.getChildCount();
       for (int j = 0; j < taskCount; j++) {
         TaskView tv = (TaskView) stackView.getChildAt(j);
         Task task = tv.getTask();
         if (tv.isFocusedTask()) {
           onTaskViewClicked(stackView, tv, stack, task, false);
           return true;
         }
       }
     }
   }
   return false;
 }
  /** Prepares the header bar layout. */
  void reloadHeaderBarLayout() {
    Resources res = mContext.getResources();
    mWindowRect = mSystemServicesProxy.getWindowRect();
    mStatusBarHeight = res.getDimensionPixelSize(com.android.internal.R.dimen.status_bar_height);
    mNavBarHeight = res.getDimensionPixelSize(com.android.internal.R.dimen.navigation_bar_height);
    mNavBarWidth = res.getDimensionPixelSize(com.android.internal.R.dimen.navigation_bar_width);
    mConfig = RecentsConfiguration.reinitialize(mContext, mSystemServicesProxy);
    mConfig.updateOnConfigurationChange();
    Rect searchBarBounds = new Rect();
    // Try and pre-emptively bind the search widget on startup to ensure that we
    // have the right thumbnail bounds to animate to.
    // Note: We have to reload the widget id before we get the task stack bounds below
    if (mSystemServicesProxy.getOrBindSearchAppWidget(mContext, mAppWidgetHost) != null) {
      mConfig.getSearchBarBounds(
          mWindowRect.width(), mWindowRect.height(), mStatusBarHeight, searchBarBounds);
    }
    mConfig.getAvailableTaskStackBounds(
        mWindowRect.width(),
        mWindowRect.height(),
        mStatusBarHeight,
        (mConfig.hasTransposedNavBar ? mNavBarWidth : 0),
        searchBarBounds,
        mTaskStackBounds);
    if (mConfig.isLandscape && mConfig.hasTransposedNavBar) {
      mSystemInsets.set(0, mStatusBarHeight, mNavBarWidth, 0);
    } else {
      mSystemInsets.set(0, mStatusBarHeight, 0, mNavBarHeight);
    }

    // Inflate the header bar layout so that we can rebind and draw it for the transition
    TaskStack stack = new TaskStack();
    mDummyStackView = new TaskStackView(mContext, stack);
    TaskStackViewLayoutAlgorithm algo = mDummyStackView.getStackAlgorithm();
    Rect taskStackBounds = new Rect(mTaskStackBounds);
    taskStackBounds.bottom -= mSystemInsets.bottom;
    algo.computeRects(mWindowRect.width(), mWindowRect.height(), taskStackBounds);
    Rect taskViewSize = algo.getUntransformedTaskViewSize();
    int taskBarHeight = res.getDimensionPixelSize(R.dimen.recents_task_bar_height);
    synchronized (mHeaderBarLock) {
      mHeaderBar =
          (TaskViewHeader) mInflater.inflate(R.layout.recents_task_view_header, null, false);
      mHeaderBar.measure(
          View.MeasureSpec.makeMeasureSpec(taskViewSize.width(), View.MeasureSpec.EXACTLY),
          View.MeasureSpec.makeMeasureSpec(taskBarHeight, View.MeasureSpec.EXACTLY));
      mHeaderBar.layout(0, 0, taskViewSize.width(), taskBarHeight);
    }
  }
Example #22
0
  /** Set/get the bsp root node */
  public void setTaskStacks(ArrayList<TaskStack> stacks) {
    Log.d(TAG, "setTaskStacks: ");
    int numStacks = stacks.size();

    // Remove all/extra stack views
    int numTaskStacksToKeep = 0; // Keep no tasks if we are recreating the layout
    if (mConfig.launchedReuseTaskStackViews) {
      numTaskStacksToKeep = Math.min(mTaskStackViews.size(), numStacks);
    }
    for (int i = mTaskStackViews.size() - 1; i >= numTaskStacksToKeep; i--) {
      removeView(mTaskStackViews.remove(i));
    }

    // Update the stack views that we are keeping
    for (int i = 0; i < numTaskStacksToKeep; i++) {
      TaskStackView tsv = mTaskStackViews.get(i);
      // If onRecentsHidden is not triggered, we need to the stack view again here
      tsv.reset();
      tsv.setStack(stacks.get(i));
    }

    // Add remaining/recreate stack views
    mStacks = stacks;
    for (int i = mTaskStackViews.size(); i < numStacks; i++) {
      TaskStack stack = stacks.get(i);
      TaskStackView stackView = new TaskStackView(getContext(), stack);
      stackView.setCallbacks(this);
      addView(stackView);
      mTaskStackViews.add(stackView);
    }

    // Enable debug mode drawing on all the stacks if necessary
    if (mConfig.debugModeEnabled) {
      for (int i = mTaskStackViews.size() - 1; i >= 0; i--) {
        TaskStackView stackView = mTaskStackViews.get(i);
        stackView.setDebugOverlay(mDebugOverlay);
      }
    }

    // Trigger a new layout
    requestLayout();
  }
  /** ** TaskStackView.TaskStackCallbacks Implementation *** */
  @Override
  public void onTaskViewClicked(
      final TaskStackView stackView,
      final TaskView tv,
      final TaskStack stack,
      final Task task,
      final boolean lockToTask) {
    // Notify any callbacks of the launching of a new task
    if (mCb != null) {
      mCb.onTaskViewClicked();
    }
    // Skip if we are already launching tasks
    if (mAlreadyLaunchingTask) {
      return;
    }
    mAlreadyLaunchingTask = true;

    // 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) {
      Bitmap b;
      if (tv != null) {
        // Disable any focused state before we draw the header
        if (tv.isFocusedTask()) {
          tv.unsetFocusedTask();
        }

        float scale = tv.getScaleX();
        int fromHeaderWidth = (int) (tv.mHeaderView.getMeasuredWidth() * scale);
        int fromHeaderHeight = (int) (tv.mHeaderView.getMeasuredHeight() * scale);
        b = Bitmap.createBitmap(fromHeaderWidth, fromHeaderHeight, Bitmap.Config.ARGB_8888);
        if (Constants.DebugFlags.App.EnableTransitionThumbnailDebugMode) {
          b.eraseColor(0xFFff0000);
        } else {
          Canvas c = new Canvas(b);
          c.scale(tv.getScaleX(), tv.getScaleY());
          tv.mHeaderView.draw(c);
          c.setBitmap(null);
        }
      } else {
        // Notify the system to skip the thumbnail layer by using an ALPHA_8 bitmap
        b = Bitmap.createBitmap(1, 1, Bitmap.Config.ALPHA_8);
      }
      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() {
                          ssp.lockCurrentTask();
                        }
                      },
                      350);
                  mTriggered = true;
                }
              }
            };
      }
      opts =
          ActivityOptions.makeThumbnailAspectScaleUpAnimation(
              sourceView,
              b,
              offsetX,
              offsetY,
              transform.rect.width(),
              transform.rect.height(),
              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) {
                  ssp.lockCurrentTask();
                }
              } else {
                // Dismiss the task and return the user to home if we fail to
                // launch the task
                onTaskViewDismissed(task);
                if (mCb != null) {
                  mCb.onTaskLaunchFailed();
                }
              }
            }
          }
        };

    // Launch the app right away if there is no task view, otherwise, animate the icon out first
    if (tv == null) {
      post(launchRunnable);
    } else {
      if (!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);
        postDelayed(launchRunnable, 17);
      }
    }
  }
  /** Starts the recents activity */
  void startRecentsActivity(ActivityManager.RunningTaskInfo topTask, boolean isTopTaskHome) {
    RecentsTaskLoader loader = RecentsTaskLoader.getInstance();
    RecentsConfiguration.reinitialize(mContext, mSystemServicesProxy);

    if (sInstanceLoadPlan == null) {
      // Create a new load plan if onPreloadRecents() was never triggered
      sInstanceLoadPlan = loader.createLoadPlan(mContext);
    }

    // Temporarily skip the transition (use a dummy fade) if multi stack is enabled.
    // For multi-stack we need to figure out where each of the tasks are going.
    if (mConfig.multiStackEnabled) {
      loader.preloadTasks(sInstanceLoadPlan, true);
      ArrayList<TaskStack> stacks = sInstanceLoadPlan.getAllTaskStacks();
      TaskStack stack = stacks.get(0);
      mDummyStackView.updateMinMaxScrollForStack(stack, mTriggeredFromAltTab, true);
      TaskStackViewLayoutAlgorithm.VisibilityReport stackVr =
          mDummyStackView.computeStackVisibilityReport();
      ActivityOptions opts = getUnknownTransitionActivityOptions();
      startAlternateRecentsActivity(
          topTask,
          opts,
          true /* fromHome */,
          false /* fromSearchHome */,
          false /* fromThumbnail */,
          stackVr);
      return;
    }

    if (!sInstanceLoadPlan.hasTasks()) {
      loader.preloadTasks(sInstanceLoadPlan, isTopTaskHome);
    }
    ArrayList<TaskStack> stacks = sInstanceLoadPlan.getAllTaskStacks();
    TaskStack stack = stacks.get(0);

    // Prepare the dummy stack for the transition
    mDummyStackView.updateMinMaxScrollForStack(stack, mTriggeredFromAltTab, isTopTaskHome);
    TaskStackViewLayoutAlgorithm.VisibilityReport stackVr =
        mDummyStackView.computeStackVisibilityReport();
    boolean hasRecentTasks = stack.getTaskCount() > 0;
    boolean useThumbnailTransition = (topTask != null) && !isTopTaskHome && hasRecentTasks;

    if (useThumbnailTransition) {

      // Try starting with a thumbnail transition
      ActivityOptions opts = getThumbnailTransitionActivityOptions(topTask, stack, mDummyStackView);
      if (opts != null) {
        startAlternateRecentsActivity(
            topTask,
            opts,
            false /* fromHome */,
            false /* fromSearchHome */,
            true /* fromThumbnail */,
            stackVr);
      } else {
        // Fall through below to the non-thumbnail transition
        useThumbnailTransition = false;
      }
    }

    if (!useThumbnailTransition) {
      // If there is no thumbnail transition, but is launching from home into recents, then
      // use a quick home transition and do the animation from home
      if (hasRecentTasks) {
        String homeActivityPackage = mSystemServicesProxy.getHomeActivityPackageName();
        String searchWidgetPackage =
            Prefs.getString(mContext, Prefs.Key.SEARCH_APP_WIDGET_PACKAGE, null);

        // Determine whether we are coming from a search owned home activity
        boolean fromSearchHome =
            (homeActivityPackage != null) && homeActivityPackage.equals(searchWidgetPackage);
        ActivityOptions opts = getHomeTransitionActivityOptions(fromSearchHome);
        startAlternateRecentsActivity(
            topTask, opts, true /* fromHome */, fromSearchHome, false /* fromThumbnail */, stackVr);
      } else {
        // Otherwise we do the normal fade from an unknown source
        ActivityOptions opts = getUnknownTransitionActivityOptions();
        startAlternateRecentsActivity(
            topTask,
            opts,
            true /* fromHome */,
            false /* fromSearchHome */,
            false /* fromThumbnail */,
            stackVr);
      }
    }
    mLastToggleTime = SystemClock.elapsedRealtime();
  }
Example #25
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();
      }
    }
  }