1 /*
2  * Copyright 2024 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 
17 package androidx.pdf.data;
18 
19 import android.net.Uri;
20 import android.os.Bundle;
21 import android.os.ParcelFileDescriptor;
22 
23 import androidx.annotation.RestrictTo;
24 import androidx.pdf.data.Openable.Open;
25 import androidx.pdf.util.Preconditions;
26 
27 import org.jspecify.annotations.NonNull;
28 import org.jspecify.annotations.Nullable;
29 
30 import java.io.IOException;
31 import java.io.InputStream;
32 import java.util.Objects;
33 
34 /**
35  * File data that can be displayed in a Viewer. This class contains meta-data specific to Projector
36  * (e.g. display type), and an {@link Openable} that can be used to access the data.
37  * Instances are parcelable (in the form of a {@link Bundle}).
38  */
39 @RestrictTo(RestrictTo.Scope.LIBRARY)
40 public class DisplayData {
41 
42     private static final String TAG = DisplayData.class.getSimpleName();
43 
44     private static final String KEY_NAME = "n";
45     private static final String KEY_URI = "uri";
46     private static final String KEY_PARCELABLE_OPENABLE = "po";
47 
48     /**
49      * This is used for identifying this data, and as the base Uri in the case of HTML. In order to
50      * actually access the data, {@link #mOpenable} should be used instead.
51      */
52     private final Uri mUri;
53 
54     private final String mName;
55 
56     private final Openable mOpenable;
57 
DisplayData( @onNull Uri uri, @NonNull String name, @NonNull Openable openable)58     public DisplayData(
59             @NonNull Uri uri,
60             @NonNull String name,
61             @NonNull Openable openable) {
62         this.mName = Preconditions.checkNotNull(name);
63         this.mUri = Preconditions.checkNotNull(uri);
64         this.mOpenable = Preconditions.checkNotNull(openable);
65     }
66 
getUri()67     public @NonNull Uri getUri() {
68         return mUri;
69     }
70 
getName()71     public @NonNull String getName() {
72         return mName;
73     }
74 
getOpenable()75     public @NonNull Openable getOpenable() {
76         return mOpenable;
77     }
78 
79     /** Converts an Opener into ParcelFileDescriptor */
openFd(@onNull Opener opener)80     public @Nullable ParcelFileDescriptor openFd(@NonNull Opener opener) {
81         // TODO: StrictMode: close() not explicitly called on PFD.
82         try {
83             return open(opener).getFd();
84         } catch (IOException e) {
85             return null;
86         }
87     }
88 
89     /** Converts Opener to InputStream */
openInputStream(@onNull Opener opener)90     public @Nullable InputStream openInputStream(@NonNull Opener opener) throws IOException {
91         return open(opener).getInputStream();
92     }
93 
94     /**
95      *
96      */
length()97     public long length() {
98         return mOpenable.length();
99     }
100 
101     /**
102      *
103      */
open(Opener opener)104     private @NonNull Open open(Opener opener) throws IOException {
105         return mOpenable.openWith(opener);
106     }
107 
108     /**
109      *
110      */
asBundle()111     public @NonNull Bundle asBundle() {
112         Bundle bundle = new Bundle();
113         bundle.putString(KEY_NAME, mName);
114         bundle.putParcelable(KEY_URI, mUri);
115         bundle.putParcelable(KEY_PARCELABLE_OPENABLE, mOpenable);
116 
117         return bundle;
118     }
119 
120     /**
121      *
122      */
123     @SuppressWarnings("deprecation")
fromBundle(@onNull Bundle bundle)124     public static @NonNull DisplayData fromBundle(@NonNull Bundle bundle) {
125         bundle.setClassLoader(DisplayData.class.getClassLoader());
126         Uri uri = bundle.getParcelable(KEY_URI);
127         String name = bundle.getString(KEY_NAME);
128         Openable openable = bundle.getParcelable(KEY_PARCELABLE_OPENABLE);
129 
130         return new DisplayData(uri, name, openable);
131     }
132 
133     @Override
toString()134     public @NonNull String toString() {
135         return String.format(
136                 "Display Data [%s] +%s, uri: %s",
137                 mName, mOpenable.getClass().getSimpleName(), mUri);
138     }
139 
140     @Override
equals(Object obj)141     public boolean equals(Object obj) {
142         if (this == obj) {
143             return true;
144         }
145 
146         if (!(obj instanceof DisplayData)) {
147             return false;
148         }
149 
150         DisplayData other = (DisplayData) obj;
151         return mUri.equals(other.mUri)
152                 && mName.equals(other.mName)
153                 && mOpenable.equals(other.mOpenable);
154     }
155 
156     @Override
hashCode()157     public int hashCode() {
158         return Objects.hash(mUri, mName, mOpenable);
159     }
160 }
161