Home | History | Annotate | Download | only in intentplayground
      1 /*
      2  * Copyright (C) 2018 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 package com.example.android.intentplayground;
     17 
     18 import android.content.ComponentName;
     19 import android.content.Intent;
     20 import android.os.Parcel;
     21 import android.os.Parcelable;
     22 
     23 import java.util.Arrays;
     24 import java.util.Collections;
     25 import java.util.LinkedList;
     26 import java.util.List;
     27 
     28 /**
     29  * This class represents a node in the tree of tasks. It can either represent a task
     30  * or an activity.
     31  */
     32 public class Node implements Parcelable, Comparable<Node> {
     33     static final int NEW_TASK_ID = 0xa4d701d;
     34     public static final int ROOT_NODE_ID = 0xAABBCCDD;
     35     public int mTaskId;
     36     public List<Node> mChildren = new LinkedList<>();
     37     public ComponentName mName;
     38     private static final int CURRENT  = 0x1;
     39     private static final int MODIFIED  = 0x2;
     40     private static final int NEW  = 0x4;
     41     private boolean mIsTaskNode;
     42     private int mOptionFlags;
     43     private Intent mIntent;
     44 
     45     Node(ComponentName data) {
     46         mIsTaskNode = false;
     47         mName = data;
     48     }
     49 
     50     /**
     51      * Create a task Node.
     52      * @param taskId the id of the task.
     53      */
     54     Node(int taskId) {
     55         mIsTaskNode = true;
     56         mTaskId = taskId;
     57     }
     58 
     59     /**
     60      * Creates a Node with the same data as the parameter (copy constructor).
     61      * @param other Node to copy over.
     62      */
     63     Node(Node other) {
     64         if (other.mIsTaskNode) {
     65             mIsTaskNode = true;
     66             mTaskId = other.mTaskId;
     67         } else {
     68             mIsTaskNode = false;
     69             mName = other.mName.clone();
     70         }
     71         mOptionFlags = other.mOptionFlags;
     72         mIntent = other.mIntent;
     73         other.mChildren.forEach(child -> addChild(new Node(child)));
     74     }
     75 
     76     /**
     77      * Adds a child to this Node's children.
     78      * @param child The child node to add.
     79      * @return returns This Node object for method chaining.
     80      */
     81     Node addChild(Node child) {
     82         mChildren.add(child);
     83         return this;
     84     }
     85 
     86     /**
     87      * Adds a child to the beginning of the list of this Node's children.
     88      * @param child The child node to add.
     89      * @return This Node object for method chaining.
     90      */
     91     Node addFirstChild(Node child) {
     92         mChildren.add(0, child);
     93         return this;
     94     }
     95 
     96     /**
     97      * Clear children from this Node.
     98      * @return returns This Node object for method chaining.
     99      */
    100     Node clearChildren() {
    101         mChildren.clear();
    102         return this;
    103     }
    104 
    105     static Node newTaskNode() {
    106         return new Node(NEW_TASK_ID);
    107     }
    108 
    109     static Node newRootNode() {
    110         return new Node(ROOT_NODE_ID);
    111     }
    112 
    113     boolean isModified() {
    114         return (mOptionFlags & MODIFIED) != 0;
    115     }
    116 
    117     void setModified(boolean value) {
    118         if (value) {
    119             mOptionFlags |= MODIFIED;
    120         } else {
    121             mOptionFlags &= ~MODIFIED;
    122         }
    123     }
    124 
    125     boolean isNew() {
    126         return ((mOptionFlags & NEW) != 0) || (mIsTaskNode && (mTaskId == NEW_TASK_ID));
    127     }
    128     void setNew(boolean value) {
    129         if (value) {
    130             mOptionFlags |= NEW;
    131         } else {
    132             mOptionFlags &= ~NEW;
    133         }
    134     }
    135 
    136     boolean isCurrent() {
    137         return (mOptionFlags & CURRENT) != 0;
    138     }
    139 
    140     Node setCurrent(boolean value) {
    141         if (value) {
    142             mOptionFlags |= CURRENT;
    143         } else {
    144             mOptionFlags &= ~CURRENT;
    145         }
    146         return this;
    147     }
    148 
    149     public Node setIntent(Intent intent) {
    150         mIntent = new Intent(intent);
    151         return this;
    152     }
    153 
    154     public Intent getIntent() {
    155         return mIntent;
    156     }
    157 
    158     private Node(Parcel in) {
    159         mIsTaskNode = in.readInt() == 1;
    160         if (mIsTaskNode) {
    161             mTaskId = in.readInt();
    162         } else {
    163             mName = ComponentName.CREATOR.createFromParcel(in);
    164         }
    165         if (in.readInt() > 0) {
    166             in.readTypedList(mChildren, Node.CREATOR);
    167         } else {
    168             mChildren = new LinkedList<>();
    169         }
    170         mOptionFlags = in.readInt();
    171         if (in.readInt() > 0) {
    172             mIntent = Intent.CREATOR.createFromParcel(in);
    173         }
    174     }
    175 
    176     /**
    177      * Compare the tree represented by this Node to another to determine if
    178      * they are isomorphic.
    179      * @param other The Node to compare to this.
    180      */
    181     public boolean equals(Node other) {
    182         if (mIsTaskNode && other.mIsTaskNode) {
    183             // Check if taskIds are equal, or if one is a new task (which is essentially a wildcard)
    184             if ((mTaskId != other.mTaskId) && (mTaskId != NEW_TASK_ID)
    185                     && (other.mTaskId != NEW_TASK_ID)) {
    186                 return false;
    187             }
    188         } else if (!mIsTaskNode && !other.mIsTaskNode){
    189             if (!other.mName.equals(mName)) return false;
    190         } else return false;
    191         if (mChildren.size() == 0 && other.mChildren.size() == 0) {
    192             return true;
    193         } else if (mChildren.size() != other.mChildren.size()){
    194             return false;
    195         } else {
    196             Collections.sort(mChildren);
    197             Collections.sort(other.mChildren);
    198             for (int i = 0; i < mChildren.size(); i++) {
    199                 if (!mChildren.get(i).equals(other.mChildren.get(i))) {
    200                     return false;
    201                 }
    202             }
    203             return true;
    204         }
    205     }
    206 
    207     /**
    208      * Note: this class has a natural ordering that is inconsistent with equals().
    209      * compareTo() makes comparison based on the {@link ComponentName} that this class
    210      * holds, and does not consider its children.
    211      */
    212     public int compareTo(Node o) {
    213         return mIsTaskNode ? Integer.valueOf(mTaskId).compareTo(o.mTaskId)
    214                 : mName.compareTo(o.mName);
    215     }
    216 
    217     @Override
    218     public String toString() {
    219         StringBuilder output = new StringBuilder("Node ");
    220         if (isCurrent()) output.append("current ");
    221         if (isNew()) output.append("new ");
    222         if (isModified()) output.append("modified ");
    223         output.append("<<");
    224         if (mIsTaskNode) output.append("taskId=").append(mTaskId);
    225         else output.append(mName.toShortString());
    226         if (mIntent != null) {
    227             output.append("intent:(");
    228             FlagUtils.discoverFlags(mIntent).forEach(flag -> {
    229                 output.append(flag.replace(FlagUtils.INTENT_FLAG_PREFIX, "")).append(',');
    230             });
    231             output.append(")");
    232         }
    233         output.append(">> {");
    234         if (!mChildren.isEmpty()) output.append('\n');
    235         mChildren.forEach(child -> Arrays.asList(child.toString().split("\n")).forEach(line ->
    236                 output.append("\t\t").append(line).append("\n")));
    237         output.append("}\n");
    238         return output.toString();
    239     }
    240 
    241     @Override
    242     public void writeToParcel(Parcel dest, int flags) {
    243         dest.writeInt( mIsTaskNode ? 1 : 0);
    244         if (mIsTaskNode) {
    245             dest.writeInt(mTaskId);
    246         } else {
    247             mName.writeToParcel(dest, 0);
    248         }
    249         if (mChildren.size() == 0 || mChildren == null) {
    250             dest.writeInt(0);
    251         } else {
    252             dest.writeInt(1);
    253             dest.writeTypedList(mChildren);
    254         }
    255         dest.writeInt(mOptionFlags);
    256         dest.writeInt(mIntent == null ? 0 : 1);
    257         if (mIntent != null) mIntent.writeToParcel(dest, 0 /* flags */);
    258     }
    259 
    260     @Override
    261     public int describeContents() {
    262         return 0;
    263     }
    264 
    265     public static final Creator<Node> CREATOR = new Creator<Node>() {
    266         @Override
    267         public Node createFromParcel(Parcel in) {
    268             return new Node(in);
    269         }
    270 
    271         @Override
    272         public Node[] newArray(int size) {
    273             return new Node[size];
    274         }
    275     };
    276 }
    277