1 /*
2  * Copyright (C) 2017 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.android.wallpaper.model;
17 
18 import android.app.Activity;
19 import android.content.Context;
20 import android.net.Uri;
21 import android.os.Parcel;
22 import android.os.Parcelable;
23 import android.util.Log;
24 
25 import androidx.exifinterface.media.ExifInterface;
26 
27 import com.android.wallpaper.R;
28 import com.android.wallpaper.asset.Asset;
29 import com.android.wallpaper.asset.ContentUriAsset;
30 
31 import java.text.ParseException;
32 import java.text.SimpleDateFormat;
33 import java.util.ArrayList;
34 import java.util.Arrays;
35 import java.util.Date;
36 import java.util.List;
37 
38 /**
39  * Represents a wallpaper image from the system's image picker.
40  */
41 public class ImageWallpaperInfo extends WallpaperInfo {
42     public static final Parcelable.Creator<ImageWallpaperInfo> CREATOR =
43             new Parcelable.Creator<ImageWallpaperInfo>() {
44                 @Override
45                 public ImageWallpaperInfo createFromParcel(Parcel in) {
46                     return new ImageWallpaperInfo(in);
47                 }
48 
49                 @Override
50                 public ImageWallpaperInfo[] newArray(int size) {
51                     return new ImageWallpaperInfo[size];
52                 }
53             };
54     private static final String TAG = "ImageWallpaperInfo";
55     // Desired EXIF tags in descending order of priority.
56     private static final String[] EXIF_TAGS = {
57             ExifInterface.TAG_IMAGE_DESCRIPTION,
58             ExifInterface.TAG_ARTIST,
59             ExifInterface.TAG_DATETIME_ORIGINAL,
60             ExifInterface.TAG_MODEL,
61     };
62     private Uri mUri;
63     private ContentUriAsset mAsset;
64     private boolean mIsAssetUncached;
65 
ImageWallpaperInfo(Uri uri)66     public ImageWallpaperInfo(Uri uri) {
67         mUri = uri;
68     }
69 
ImageWallpaperInfo(Uri uri, boolean uncachedAsset)70     public ImageWallpaperInfo(Uri uri, boolean uncachedAsset) {
71         mUri = uri;
72         mIsAssetUncached = uncachedAsset;
73     }
74 
ImageWallpaperInfo(Parcel in)75     protected ImageWallpaperInfo(Parcel in) {
76         mUri = Uri.parse(in.readString());
77     }
78 
79     @Override
getUri()80     public Uri getUri() {
81         return mUri;
82     }
83 
84     /**
85      * Formats a localized date string based on the provided datetime string in EXIF datetime format.
86      *
87      * @param exifDateTime Datetime string in EXIF datetime format.
88      * @return Localized date string, or the original datetime string if it could not be parsed.
89      */
formatDate(String exifDateTime)90     private static String formatDate(String exifDateTime) {
91         try {
92             Date parsedDate = new SimpleDateFormat("yyyy:MM:dd HH:mm:ss").parse(exifDateTime);
93             return SimpleDateFormat.getDateInstance().format(parsedDate);
94         } catch (ParseException e) {
95             Log.w(TAG, "Unable to parse image datetime", e);
96             return exifDateTime;
97         }
98     }
99 
getGenericAttributions(Context context)100     private static List<String> getGenericAttributions(Context context) {
101         return Arrays.asList(
102                 context.getResources().getString(R.string.my_photos_generic_wallpaper_title));
103     }
104 
105     @Override
getTitle(Context context)106     public String getTitle(Context context) {
107         return null;
108     }
109 
110     @Override
getAttributions(Context context)111     public List<String> getAttributions(Context context) {
112         ContentUriAsset asset = (ContentUriAsset) getAsset(context);
113 
114         if (!asset.isJpeg()) {
115             // Return generic attributions if image is not stored in the JPEG file format.
116             return getGenericAttributions(context);
117         }
118 
119         List<String> attributes = new ArrayList<>();
120 
121         for (String tag : EXIF_TAGS) {
122             String attribute = asset.readExifTag(tag);
123 
124             if (attribute == null) {
125                 continue;
126             }
127 
128             if (tag == ExifInterface.TAG_DATETIME_ORIGINAL) {
129                 attribute = formatDate(attribute);
130             }
131 
132             attributes.add(attribute);
133         }
134 
135         if (!attributes.isEmpty()) {
136             return attributes;
137         }
138 
139         // Return generic attributions if image did not contain any desired EXIF tags.
140         return getGenericAttributions(context);
141     }
142 
143     @Override
getAsset(Context context)144     public Asset getAsset(Context context) {
145         if (mIsAssetUncached) {
146             mAsset = new ContentUriAsset(
147                     context,
148                     mUri,
149                     /* uncached */ true);
150         } else {
151             if (mAsset == null) {
152                 mAsset = new ContentUriAsset(context, mUri);
153             }
154         }
155 
156         return mAsset;
157     }
158 
159     @Override
getThumbAsset(Context context)160     public Asset getThumbAsset(Context context) {
161         return getAsset(context);
162     }
163 
164     @Override
getCollectionId(Context context)165     public String getCollectionId(Context context) {
166         return context.getString(R.string.image_wallpaper_collection_id);
167     }
168 
169     @Override
showPreview(Activity srcActivity, InlinePreviewIntentFactory factory, int requestCode)170     public void showPreview(Activity srcActivity, InlinePreviewIntentFactory factory,
171                             int requestCode) {
172         srcActivity.startActivityForResult(factory.newIntent(srcActivity, this), requestCode);
173     }
174 
175     @Override
writeToParcel(Parcel parcel, int i)176     public void writeToParcel(Parcel parcel, int i) {
177         parcel.writeString(mUri.toString());
178     }
179 }
180