blob: 28f6ed81b7c4e858b91d114de456f96d3fa57c55 [file] [log] [blame]
/*
* 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.content.ComponentName;
import android.content.Intent;
import android.os.Parcel;
import android.os.Parcelable;
import java.util.Arrays;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
/**
* This class represents a node in the tree of tasks. It can either represent a task
* or an activity.
*/
public class Node implements Parcelable, Comparable<Node> {
static final int NEW_TASK_ID = 0xa4d701d;
public static final int ROOT_NODE_ID = 0xAABBCCDD;
public int mTaskId;
public List<Node> mChildren = new LinkedList<>();
public ComponentName mName;
private static final int CURRENT = 0x1;
private static final int MODIFIED = 0x2;
private static final int NEW = 0x4;
private boolean mIsTaskNode;
private int mOptionFlags;
private Intent mIntent;
Node(ComponentName data) {
mIsTaskNode = false;
mName = data;
}
/**
* Create a task Node.
* @param taskId the id of the task.
*/
Node(int taskId) {
mIsTaskNode = true;
mTaskId = taskId;
}
/**
* Creates a Node with the same data as the parameter (copy constructor).
* @param other Node to copy over.
*/
Node(Node other) {
if (other.mIsTaskNode) {
mIsTaskNode = true;
mTaskId = other.mTaskId;
} else {
mIsTaskNode = false;
mName = other.mName.clone();
}
mOptionFlags = other.mOptionFlags;
mIntent = other.mIntent;
other.mChildren.forEach(child -> addChild(new Node(child)));
}
/**
* Adds a child to this Node's children.
* @param child The child node to add.
* @return returns This Node object for method chaining.
*/
Node addChild(Node child) {
mChildren.add(child);
return this;
}
/**
* Adds a child to the beginning of the list of this Node's children.
* @param child The child node to add.
* @return This Node object for method chaining.
*/
Node addFirstChild(Node child) {
mChildren.add(0, child);
return this;
}
/**
* Clear children from this Node.
* @return returns This Node object for method chaining.
*/
Node clearChildren() {
mChildren.clear();
return this;
}
static Node newTaskNode() {
return new Node(NEW_TASK_ID);
}
static Node newRootNode() {
return new Node(ROOT_NODE_ID);
}
boolean isModified() {
return (mOptionFlags & MODIFIED) != 0;
}
void setModified(boolean value) {
if (value) {
mOptionFlags |= MODIFIED;
} else {
mOptionFlags &= ~MODIFIED;
}
}
boolean isNew() {
return ((mOptionFlags & NEW) != 0) || (mIsTaskNode && (mTaskId == NEW_TASK_ID));
}
void setNew(boolean value) {
if (value) {
mOptionFlags |= NEW;
} else {
mOptionFlags &= ~NEW;
}
}
boolean isCurrent() {
return (mOptionFlags & CURRENT) != 0;
}
Node setCurrent(boolean value) {
if (value) {
mOptionFlags |= CURRENT;
} else {
mOptionFlags &= ~CURRENT;
}
return this;
}
public Node setIntent(Intent intent) {
mIntent = new Intent(intent);
return this;
}
public Intent getIntent() {
return mIntent;
}
private Node(Parcel in) {
mIsTaskNode = in.readInt() == 1;
if (mIsTaskNode) {
mTaskId = in.readInt();
} else {
mName = ComponentName.CREATOR.createFromParcel(in);
}
if (in.readInt() > 0) {
in.readTypedList(mChildren, Node.CREATOR);
} else {
mChildren = new LinkedList<>();
}
mOptionFlags = in.readInt();
if (in.readInt() > 0) {
mIntent = Intent.CREATOR.createFromParcel(in);
}
}
/**
* Compare the tree represented by this Node to another to determine if
* they are isomorphic.
* @param other The Node to compare to this.
*/
public boolean equals(Node other) {
if (mIsTaskNode && other.mIsTaskNode) {
// Check if taskIds are equal, or if one is a new task (which is essentially a wildcard)
if ((mTaskId != other.mTaskId) && (mTaskId != NEW_TASK_ID)
&& (other.mTaskId != NEW_TASK_ID)) {
return false;
}
} else if (!mIsTaskNode && !other.mIsTaskNode){
if (!other.mName.equals(mName)) return false;
} else return false;
if (mChildren.size() == 0 && other.mChildren.size() == 0) {
return true;
} else if (mChildren.size() != other.mChildren.size()){
return false;
} else {
Collections.sort(mChildren);
Collections.sort(other.mChildren);
for (int i = 0; i < mChildren.size(); i++) {
if (!mChildren.get(i).equals(other.mChildren.get(i))) {
return false;
}
}
return true;
}
}
/**
* Note: this class has a natural ordering that is inconsistent with equals().
* compareTo() makes comparison based on the {@link ComponentName} that this class
* holds, and does not consider its children.
*/
public int compareTo(Node o) {
return mIsTaskNode ? Integer.valueOf(mTaskId).compareTo(o.mTaskId)
: mName.compareTo(o.mName);
}
@Override
public String toString() {
StringBuilder output = new StringBuilder("Node ");
if (isCurrent()) output.append("current ");
if (isNew()) output.append("new ");
if (isModified()) output.append("modified ");
output.append("<<");
if (mIsTaskNode) output.append("taskId=").append(mTaskId);
else output.append(mName.toShortString());
if (mIntent != null) {
output.append("intent:(");
FlagUtils.discoverFlags(mIntent).forEach(flag -> {
output.append(flag.replace(FlagUtils.INTENT_FLAG_PREFIX, "")).append(',');
});
output.append(")");
}
output.append(">> {");
if (!mChildren.isEmpty()) output.append('\n');
mChildren.forEach(child -> Arrays.asList(child.toString().split("\n")).forEach(line ->
output.append("\t\t").append(line).append("\n")));
output.append("}\n");
return output.toString();
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeInt( mIsTaskNode ? 1 : 0);
if (mIsTaskNode) {
dest.writeInt(mTaskId);
} else {
mName.writeToParcel(dest, 0);
}
if (mChildren.size() == 0 || mChildren == null) {
dest.writeInt(0);
} else {
dest.writeInt(1);
dest.writeTypedList(mChildren);
}
dest.writeInt(mOptionFlags);
dest.writeInt(mIntent == null ? 0 : 1);
if (mIntent != null) mIntent.writeToParcel(dest, 0 /* flags */);
}
@Override
public int describeContents() {
return 0;
}
public static final Creator<Node> CREATOR = new Creator<Node>() {
@Override
public Node createFromParcel(Parcel in) {
return new Node(in);
}
@Override
public Node[] newArray(int size) {
return new Node[size];
}
};
}