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.Parcel;
21 import android.os.ParcelFileDescriptor;
22 import android.os.Parcelable;
23 
24 import androidx.annotation.RestrictTo;
25 import androidx.pdf.util.Preconditions;
26 import androidx.pdf.util.Uris;
27 
28 import org.jspecify.annotations.NonNull;
29 import org.jspecify.annotations.Nullable;
30 
31 import java.io.File;
32 import java.io.FileInputStream;
33 import java.io.FileNotFoundException;
34 import java.io.IOException;
35 import java.io.InputStream;
36 
37 /**
38  * A file's data that is saved on disk (e.g. in cache).
39  */
40 @RestrictTo(RestrictTo.Scope.LIBRARY)
41 public class FileOpenable implements Openable, Parcelable {
42 
43     private static final String TAG = FileOpenable.class.getSimpleName();
44 
45     /**
46      * Turns this {@link Uri} into a {@link File} if possible
47      *
48      * @throws IllegalArgumentException If the Uri was not a 'file:' one.
49      */
getFile(Uri fileUri)50     private static File getFile(Uri fileUri) {
51         Preconditions.checkArgument(Uris.isFileUri(fileUri),
52                 "FileOpenable only valid for file Uris");
53         return new File(fileUri.getPath());
54     }
55 
56     private final @Nullable String mContentType;
57 
58     private final File mFile;
59 
60     /**
61      * Constructs an {@link Openable} from a file and a given content-type.
62      *
63      * @throws FileNotFoundException If the file does not exist.
64      */
FileOpenable(@onNull File file, @Nullable String mimeType)65     public FileOpenable(@NonNull File file, @Nullable String mimeType)
66             throws FileNotFoundException {
67         if (!file.exists()) {
68             throw new FileNotFoundException("file does not exist");
69         }
70         this.mFile = Preconditions.checkNotNull(file);
71         this.mContentType = mimeType;
72     }
73 
74     /**
75      * Constructs an {@link Openable} from a file Uri.
76      *
77      * @throws IllegalArgumentException If the Uri was not a 'file:' one.
78      * @throws FileNotFoundException    If the file does not exist.
79      */
FileOpenable(@onNull Uri uri)80     public FileOpenable(@NonNull Uri uri) throws FileNotFoundException {
81         this(getFile(uri), Uris.extractContentType(uri));
82     }
83 
84     @Override
openWith(@onNull Opener opener)85     public @NonNull Open openWith(@NonNull Opener opener) throws IOException {
86         return new Open() {
87 
88             @Override
89             public InputStream getInputStream() throws IOException {
90                 return new FileInputStream(mFile);
91             }
92 
93             @Override
94             public ParcelFileDescriptor getFd() throws IOException {
95                 return ParcelFileDescriptor.open(mFile, ParcelFileDescriptor.MODE_READ_ONLY);
96             }
97 
98             @Override
99             public long length() {
100                 return mFile.length();
101             }
102 
103             @Override
104             public String getContentType() {
105                 return mContentType;
106             }
107         };
108     }
109 
110     @Override
111     public @Nullable String getContentType() {
112         return mContentType;
113     }
114 
115     @Override
116     public long length() {
117         return mFile.length();
118     }
119 
120     public @NonNull String getFileName() {
121         return mFile.getName();
122     }
123 
124     public @NonNull Uri getFileUri() {
125         return Uri.fromFile(mFile);
126     }
127 
128 
129     @Override
130     public void writeToParcel(@NonNull Parcel dest, int flags) {
131         dest.writeString(mFile.getPath());
132         dest.writeString(mContentType);
133     }
134 
135     @Override
136     public int describeContents() {
137         return 0;
138     }
139 
140     public static final Creator<FileOpenable> CREATOR =
141             new Creator<FileOpenable>() {
142                 @Override
143                 public @Nullable FileOpenable createFromParcel(Parcel parcel) {
144                     try {
145                         return new FileOpenable(makeFile(parcel.readString()), parcel.readString());
146                     } catch (FileNotFoundException e) {
147                         return null;
148                     }
149                 }
150 
151                 private File makeFile(String filePath) {
152                     return new File(filePath);
153                 }
154 
155                 @Override
156                 public FileOpenable[] newArray(int size) {
157                     return new FileOpenable[size];
158                 }
159             };
160 }
161