Dead code removal

Code to access the ActivityInstanceInfo api through reflection is no
longer needed, because we have the lifecycle tracking.

There were some resources for the snackbar that used to be shown during
the launching of the preset activities however that has been removed.

Finally there was a bunch of code in TestBase, that was trying to
predict the outcome of intent flags that never did anything.

Test: Build module
Change-Id: I73e0f172b605cff2433c8c386ef9b1672130f349
This commit is contained in:
Liam Clark
2019-01-11 14:02:26 -08:00
parent ba9b6ca212
commit b6daa9e91a
4 changed files with 4 additions and 368 deletions

View File

@@ -75,8 +75,6 @@ public class IntentDialogFragment extends DialogFragment {
mFlags = args.getStringArrayList(ARGUMENT_FLAGS);
mActivityName = args.getString(ARGUMENT_ACTIVITY_NAME);
mTaskId = args.getInt(TASK_ID);
BaseActivityViewModel viewModel = (new ViewModelProvider(getActivity(),
new ViewModelProvider.NewInstanceFactory())).get(BaseActivityViewModel.class);
}
@Nullable

View File

@@ -1,98 +0,0 @@
/*
* Copyright (C) 2018 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.
*/
package com.example.android.intentplayground;
import android.app.ActivityManager.RecentTaskInfo;
import android.content.ComponentName;
import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.util.Log;
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
public final class TaskInfo {
private final static String TAG = "TaskInfo";
/**
* Access the ActivityInstanceInfo without depending on it at compile time.
*
* @param info the recent task mInfo to try and access the activity information of.
* @return The activity instance mInfo in our own mirrored value object or an empty list.
*/
public static List<ActivityInstanceInfoMirror> getActivities(RecentTaskInfo info) {
try {
List<Object> activities = (List<Object>) info.getClass().getField("activities").get(info);
List<ActivityInstanceInfoMirror> activityInfoMirrors = new ArrayList<>();
for (Object activityInstanceInfo : activities) {
Class<?> activityInfoClass = activityInstanceInfo.getClass();
System.out.println(Arrays.toString(activityInfoClass.getDeclaredFields()));
ActivityInfo activityInfo = (ActivityInfo) activityInfoClass.getMethod("getInfo")
.invoke(activityInstanceInfo);
Intent intent = (Intent) activityInfoClass.getMethod("getIntent")
.invoke(activityInstanceInfo);
ComponentName name = (ComponentName) activityInfoClass.getMethod("getName")
.invoke(activityInstanceInfo);
Integer hashId = (Integer) activityInfoClass.getMethod("getHashId")
.invoke(activityInstanceInfo);
activityInfoMirrors.add(new ActivityInstanceInfoMirror(activityInfo, intent, name, hashId));
}
return activityInfoMirrors;
} catch (IllegalAccessException | NoSuchFieldException | InvocationTargetException | NoSuchMethodException e) {
Log.e(TAG, "ActivityInstanceInfo not available on the current api level", e);
return new ArrayList<>();
}
}
//TODO (b/119894108): Replace the mirror with actual class once the framework-api is merged.
public static class ActivityInstanceInfoMirror {
private final ActivityInfo mInfo;
private final Intent mIntent;
private final ComponentName mComponentName;
private final int mHashId;
public ActivityInstanceInfoMirror(ActivityInfo info, Intent intent,
ComponentName componentName, int hashId) {
this.mInfo = info;
this.mIntent = intent;
this.mComponentName = componentName;
this.mHashId = hashId;
}
public ActivityInfo getInfo() {
return mInfo;
}
public Intent getIntent() {
return mIntent;
}
public ComponentName getName() {
return mComponentName;
}
public int getHashId() {
return mHashId;
}
}
}

View File

@@ -16,31 +16,17 @@
package com.example.android.intentplayground;
import android.app.Activity;
import android.app.ActivityManager;
import static java.util.Collections.singletonList;
import android.app.TaskStackBuilder;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.util.Log;
import com.example.android.intentplayground.TaskInfo.ActivityInstanceInfoMirror;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.LinkedList;
import java.util.List;
import java.util.ListIterator;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;
import static com.example.android.intentplayground.FlagUtils.getActivityFlags;
import static com.example.android.intentplayground.FlagUtils.hasActivityFlag;
import static com.example.android.intentplayground.FlagUtils.hasIntentFlag;
import static java.util.Collections.singletonList;
/**
* TestBase holds methods to query, test and compare task hierarchies.
@@ -49,7 +35,6 @@ public class TestBase {
static final String TAG = "TestBase";
private List<TaskStackBuilder> mBuilders;
private Context mContext;
private PackageInfo mPackageInfo;
TestBase(Context context, Node hierarchy) {
mBuilders = new LinkedList<>();
@@ -96,7 +81,8 @@ public class TestBase {
ArrayList<Intent> nextIntents = new ArrayList<>(Arrays.asList(
tsb.getIntents()));
Intent launch = nextIntents.remove(0)
.putParcelableArrayListExtra(BaseActivity.EXTRA_LAUNCH_FORWARD, nextIntents);
.putParcelableArrayListExtra(BaseActivity.EXTRA_LAUNCH_FORWARD,
nextIntents);
if (BuildConfig.DEBUG) {
Log.d(TAG, "Launching " + launch.getComponent().toString());
}
@@ -106,205 +92,7 @@ public class TestBase {
}
}
/**
* This method examines the flags on the given intent, as well as the <activity> flags of
* the intended component, and computes what the hierarchy of activities should look like
* after the launch of the intended component (WORK IN PROGRESS).
* @param intent The intent that will be passed to startActivity().
* @return A Node object that models the expected hierarchy.
*/
Node computeExpected(Intent intent) {
// Determine the effect of selected mIntent flags on expected hierarchy
Node currentTasks = describeTaskHierarchy(mContext);
Node startActivity = new Node(intent.getComponent()).setIntent(intent);
Node targetTask = findReusableTarget(currentTasks, intent)
.orElseGet(() -> createNewTaskIfNeeded(currentTasks, intent)
.orElse(findCurrentTask(currentTasks).get()));
clearIfNeeded(targetTask, intent);
if (needsStartActivity(currentTasks, intent)) {
targetTask.addFirstChild(startActivity);
}
return currentTasks;
}
/**
* Finds the taskAffinity of the target component
* @param actName The component for which to find the corresponding affinity
* @return the task affinity, or null if there is none associated with the component
*/
Optional<String> affinityOf(ComponentName actName) {
String affinity = null;
for (ActivityInfo activityInfo : mPackageInfo.activities) {
if (activityInfo.name.equals(actName.getClassName())) {
affinity = activityInfo.taskAffinity;
}
}
return Optional.ofNullable(affinity);
}
/**
* Describes the current set of tasks open in the application as
* a tree of Nodes. Returns a root node, whose children are task nodes.
* The children of those task nodes are activities, in order of most recently used.
* @param context The context of an activity in the application.
* @return A Node that models the current task hierarchy of the application.
*/
public static Node describeTaskHierarchy(Context context) {
ActivityManager am = context.getSystemService(ActivityManager.class);
Node root = Node.newRootNode();
int currentTaskId = ((Activity) context).getTaskId();
List<ActivityManager.RecentTaskInfo> tasks = am.getAppTasks().stream()
.map(ActivityManager.AppTask::getTaskInfo).collect(Collectors.toList());
for (ActivityManager.AppTask task : am.getAppTasks()) {
ActivityManager.RecentTaskInfo rti = task.getTaskInfo();
List<ActivityInstanceInfoMirror> activities = TaskInfo.getActivities(rti);
if (!activities.isEmpty()) {
Intent baseIntent = activities.get(0).getIntent();
Node taskRoot = new Node(rti.persistentId).setIntent(baseIntent);
if (taskRoot.mTaskId == currentTaskId) taskRoot.setCurrent(true);
activities.forEach(activity -> {
taskRoot.addChild(new Node(activity.getName().clone())
.setIntent(activity.getIntent()));
});
root.addChild(taskRoot);
}
}
return root;
}
private Optional<Node> findReusableTarget(Node tasks, Intent intent) {
ComponentName target = intent.getComponent();
boolean hasNewTask = hasIntentFlag(intent, IntentFlag.NEW_TASK);
boolean hasMultipleTask = hasIntentFlag(intent, IntentFlag.MULTIPLE_TASK);
boolean isSingleInstance = hasActivityFlag(mContext, target,
ActivityFlag.LAUNCH_MODE_SINGLE_INSTANCE);
boolean isSingleTask = hasActivityFlag(mContext, target,
ActivityFlag.LAUNCH_MODE_SINGLE_TASK);
boolean isIntoExisting = hasActivityFlag(mContext, target,
ActivityFlag.DOCUMENT_LAUNCH_MODE_INTO_EXISTING);
if (isSingleInstance || isSingleTask) {
Log.d(TAG, "found resuable target singleInstance/singleTask");
return findTaskOfActivity(tasks, target);
} else if (isIntoExisting || (hasNewTask && !hasMultipleTask)) {
Optional<Node> rootTask = findTaskWithRoot(tasks, target);
if (rootTask.isPresent()) {
Log.d(TAG, "found resuable target, same root task");
return rootTask;
}
else if (!isDocument(intent)) {
Log.d(TAG, "found resuable target, same affinity task");
return findTaskWithAffinity(tasks, affinityOf(target).
orElse(target.getPackageName()));
}
}
Log.d(TAG, "did not find resuable target");
return Optional.empty();
}
private Optional<Node> createNewTaskIfNeeded(Node tasks, Intent intent) {
// Everything in this method runs assuming there is no reuseable target for the intent
ComponentName target = intent.getComponent();
boolean hasNewTask = hasIntentFlag(intent, IntentFlag.NEW_TASK);
boolean hasMultipleTask = hasIntentFlag(intent, IntentFlag.MULTIPLE_TASK);
boolean hasNewDocument = hasIntentFlag(intent, IntentFlag.NEW_DOCUMENT);
Set<ActivityFlag> flags = getActivityFlags(mContext, target);
boolean isSingleInstance = flags.contains(ActivityFlag.LAUNCH_MODE_SINGLE_INSTANCE);
boolean isSingleTask = flags.contains(ActivityFlag.LAUNCH_MODE_SINGLE_TASK);
boolean isDocument = flags.contains(ActivityFlag.DOCUMENT_LAUNCH_MODE_ALWAYS)
|| flags.contains(ActivityFlag.DOCUMENT_LAUNCH_MODE_INTO_EXISTING);
if (hasNewTask || hasNewDocument || isDocument || isSingleInstance || isSingleTask) {
if (hasMultipleTask) {
// remove task with same root if present
findTaskWithRoot(tasks, target).ifPresent(task -> {
tasks.mChildren.remove(task);
});
}
Node newNode = new Node(Node.NEW_TASK_ID).setIntent(intent);
newNode.setNew(true);
tasks.addFirstChild(newNode);
Log.d(TAG, "create new task");
return Optional.of(newNode);
}
Log.d(TAG, "did not create new task");
return Optional.empty();
}
private static Optional<Node> findCurrentTask(Node stack) {
return stack.mChildren.stream().filter(Node::isCurrent).findFirst();
}
private static Optional<Node> findTaskOfActivity(Node stack, ComponentName target) {
for (Node task : stack.mChildren) {
for (Node activity : task.mChildren) {
if (activity.mName.equals(target)) return Optional.of(task);
}
}
return Optional.empty();
}
public static Optional<Node> findTaskWithRoot(Node stack, ComponentName target) {
return stack.mChildren.stream().filter(task -> task.mChildren.get(task.mChildren.size() - 1)
.mName.equals(target))
.findFirst();
}
public static List<Node> findTasksWithRoot(Node stack, ComponentName target) {
return stack.mChildren.stream().filter(task -> task.mChildren.get(task.mChildren.size() - 1)
.mName.equals(target))
.collect(Collectors.toList());
}
private Optional<Node> findTaskWithAffinity(Node stack, String affinity) {
// Need to iterate through tasks from least to most recent
ListIterator<Node> iterator = stack.mChildren.listIterator(stack.mChildren.size());
while (iterator.hasPrevious()) {
Node task = iterator.previous();
if (!hasActivityFlag(mContext, task.mChildren.get(0).mName,
ActivityFlag.LAUNCH_MODE_SINGLE_INSTANCE)) { // Exclude singleInstance
if (affinityOf(task.mChildren.get(0).mName)
.filter(a -> a.equals(affinity)).isPresent()) { // find matching affinity
return Optional.of(task);
}
}
}
return Optional.empty();
}
private boolean needsStartActivity(Node tasks, Intent intent) {
ComponentName sourceActivity = findCurrentTask(tasks).orElse(tasks.mChildren.get(0))
.mChildren.get(0).mName;
ComponentName target = intent.getComponent();
boolean hasSingleTop = hasIntentFlag(intent, IntentFlag.SINGLE_TOP);
boolean isSingleInstance = hasActivityFlag(mContext, target,
ActivityFlag.LAUNCH_MODE_SINGLE_INSTANCE);
boolean isSingleTask = hasActivityFlag(mContext, target,
ActivityFlag.LAUNCH_MODE_SINGLE_TASK);
boolean isSingleTop = hasActivityFlag(mContext, target,
ActivityFlag.LAUNCH_MODE_SINGLE_TOP);
if (sourceActivity.equals(target) &&
(isSingleInstance || isSingleTask || isSingleTop || hasSingleTop )) {
return false;
}
return true;
}
private boolean isDocument(Intent intent) {
Set<ActivityFlag> flags = getActivityFlags(mContext, intent.getComponent());
return flags.contains(ActivityFlag.DOCUMENT_LAUNCH_MODE_ALWAYS) ||
flags.contains(ActivityFlag.DOCUMENT_LAUNCH_MODE_INTO_EXISTING) ||
hasIntentFlag(intent, IntentFlag.NEW_DOCUMENT);
}
void setActivities(Node hierarchy) {
// load the package info for this app (used later to get <activity> flags)
PackageManager pm = mContext.getPackageManager();
try {
mPackageInfo = pm.getPackageInfo(mContext.getPackageName(),
PackageManager.GET_ACTIVITIES);
} catch (PackageManager.NameNotFoundException e) {
// Should not happen
throw new RuntimeException(e);
}
// Build list of TaskStackBuilders from task hierarchy modeled by Node
if (hierarchy.mChildren.isEmpty()) return;
mBuilders.clear();
@@ -327,27 +115,6 @@ public class TestBase {
new ArrayList<>(singletonList(launcherIntent)));
}
private void clearIfNeeded(Node task, Intent intent) {
ComponentName target = intent.getComponent();
boolean hasClearTop = hasIntentFlag(intent, IntentFlag.CLEAR_TOP);
boolean shouldClearTask = hasIntentFlag(intent, IntentFlag.CLEAR_TASK)
&& hasIntentFlag(intent, IntentFlag.NEW_TASK);
boolean isDocument = isDocument(intent);
boolean isSingleInstance = hasActivityFlag(mContext, target,
ActivityFlag.LAUNCH_MODE_SINGLE_INSTANCE);
boolean isSingleTask = hasActivityFlag(mContext, target,
ActivityFlag.LAUNCH_MODE_SINGLE_TASK);
if (hasClearTop) {
int targetIndex = 0;
for (int i = 0; i < task.mChildren.size(); i++) {
if (task.mChildren.get(i).mName.equals(target)) targetIndex = i;
}
task.mChildren = task.mChildren.subList(targetIndex, task.mChildren.size());
} else if (shouldClearTask || isDocument || isSingleInstance || isSingleTask) {
task.clearChildren();
}
}
public Context getContext() { return mContext; }
/**