• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 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 
17 package com.android.tv.twopanelsettings.slices.compat;
18 
19 import static android.app.slice.SliceItem.FORMAT_ACTION;
20 import static android.app.slice.SliceItem.FORMAT_IMAGE;
21 import static android.app.slice.SliceItem.FORMAT_REMOTE_INPUT;
22 import static java.lang.annotation.RetentionPolicy.SOURCE;
23 
24 import android.app.PendingIntent;
25 import android.content.ActivityNotFoundException;
26 import android.content.Context;
27 import android.content.Intent;
28 import android.graphics.Bitmap;
29 import android.net.Uri;
30 import android.os.Parcelable;
31 import androidx.annotation.IntDef;
32 import androidx.annotation.NonNull;
33 import androidx.annotation.Nullable;
34 import androidx.core.util.Preconditions;
35 import com.android.tv.twopanelsettings.slices.compat.core.SliceActionImpl;
36 import com.android.tv.twopanelsettings.slices.compat.core.SliceHints;
37 import java.io.BufferedInputStream;
38 import java.io.IOException;
39 import java.io.InputStream;
40 import java.lang.annotation.Retention;
41 import java.nio.charset.Charset;
42 
43 /**
44  * Utilities for dealing with slices.
45  *
46  * <p>Slice framework has been deprecated, it will not receive any updates moving forward. If you
47  * are looking for a framework that handles communication across apps, consider using {@link
48  * android.app.appsearch.AppSearchManager}.
49  */
50 // @Deprecated // Supported for TV
51 public class SliceUtils {
52 
SliceUtils()53   private SliceUtils() {}
54 
55   /**
56    * Parse a slice that has been previously serialized.
57    *
58    * <p>Parses a slice that was serialized with {@link #serializeSlice}.
59    *
60    * <p>Note: Slices returned by this cannot be passed to {@link SliceConvert#unwrap(Slice)}.
61    *
62    * @param input The input stream to read from.
63    * @param encoding The encoding to read as.
64    * @param listener Listener used to handle actions when reconstructing the slice.
65    * @throws SliceParseException if the InputStream cannot be parsed.
66    */
parseSlice( @onNull final Context context, @NonNull InputStream input, @NonNull String encoding, @NonNull final SliceActionListener listener)67   public static @NonNull Slice parseSlice(
68       @NonNull final Context context,
69       @NonNull InputStream input,
70       @NonNull String encoding,
71       @NonNull final SliceActionListener listener)
72       throws IOException, SliceParseException {
73     BufferedInputStream bufferedInputStream = new BufferedInputStream(input);
74     Slice s = SliceXml.parseSlice(context, bufferedInputStream, encoding, listener);
75     s.mHints = ArrayUtils.appendElement(String.class, s.mHints, SliceHints.HINT_CACHED);
76     return s;
77   }
78 
79   /** */
80   // @RestrictTo(RestrictTo.Scope.LIBRARY)
parseImageMode(@onNull SliceItem iconItem)81   public static int parseImageMode(@NonNull SliceItem iconItem) {
82     return SliceActionImpl.parseImageMode(iconItem);
83   }
84 
fireAction( @ullable Context context, @NonNull Parcelable action, @Nullable Intent fillIn)85   public static void fireAction(
86       @Nullable Context context, @NonNull Parcelable action, @Nullable Intent fillIn)
87       throws PendingIntent.CanceledException {
88     if (action instanceof PendingIntent) {
89       ((PendingIntent) action).send(context, 0, fillIn, null, null);
90       return;
91     }
92 
93     Preconditions.checkNotNull(context);
94     Intent filledIn = new Intent((Intent) action);
95     if (fillIn != null) {
96       filledIn.fillIn(fillIn, 0);
97     }
98 
99     if (context.getPackageManager().resolveActivity(filledIn, 0) == null) {
100       context.sendBroadcast(filledIn);
101       return;
102     }
103 
104     try {
105       context.startActivity(filledIn);
106     } catch (ActivityNotFoundException e) {
107       throw new PendingIntent.CanceledException(e);
108     }
109   }
110 
doesStreamStartWith(String parcelName, BufferedInputStream inputStream)111   private static boolean doesStreamStartWith(String parcelName, BufferedInputStream inputStream) {
112     byte[] data = parcelName.getBytes(Charset.forName("UTF-16"));
113     byte[] buf = new byte[data.length];
114     try {
115       // Read out the int size of the string.
116       if (inputStream.read(buf, 0, 4) < 0) {
117         return false;
118       }
119       if (inputStream.read(buf, 0, buf.length) < 0) {
120         return false;
121       }
122       return parcelName.equals(new String(buf, "UTF-16"));
123     } catch (IOException e) {
124       return false;
125     }
126   }
127 
128   /** Holds options for how to handle SliceItems that cannot be serialized. */
129   public static class SerializeOptions {
130     /**
131      * Constant indicating that the an {@link IllegalArgumentException} should be thrown when this
132      * format is encountered.
133      */
134     public static final int MODE_THROW = 0;
135 
136     /** Constant indicating that the SliceItem should be removed when this format is encountered. */
137     public static final int MODE_REMOVE = 1;
138 
139     /**
140      * Constant indicating that the SliceItem should be serialized as much as possible.
141      *
142      * <p>For images this means they will be attempted to be serialized. For actions, the action
143      * will be removed but the content of the action will be serialized. The action may be triggered
144      * later on a de-serialized slice by binding the slice again and activating a pending-intent at
145      * the same location as the serialized action.
146      */
147     public static final int MODE_CONVERT = 2;
148 
149     @IntDef({MODE_THROW, MODE_REMOVE, MODE_CONVERT})
150     @Retention(SOURCE)
151     @interface FormatMode {}
152 
153     private int mActionMode = MODE_THROW;
154     private int mImageMode = MODE_THROW;
155     private int mMaxWidth = 1000;
156     private int mMaxHeight = 1000;
157 
158     private Bitmap.CompressFormat mFormat = Bitmap.CompressFormat.PNG;
159     private int mQuality = 100;
160 
161     /** */
162     // @RestrictTo(RestrictTo.Scope.LIBRARY)
checkThrow(String format)163     public void checkThrow(String format) {
164       switch (format) {
165         case FORMAT_ACTION:
166         case FORMAT_REMOTE_INPUT:
167           if (mActionMode != MODE_THROW) {
168             return;
169           }
170           break;
171         case FORMAT_IMAGE:
172           if (mImageMode != MODE_THROW) {
173             return;
174           }
175           break;
176         default:
177           return;
178       }
179       throw new IllegalArgumentException(format + " cannot be serialized");
180     }
181 
182     /** */
183     // @RestrictTo(RestrictTo.Scope.LIBRARY)
getActionMode()184     public @FormatMode int getActionMode() {
185       return mActionMode;
186     }
187 
188     /** */
189     // @RestrictTo(RestrictTo.Scope.LIBRARY)
getImageMode()190     public @FormatMode int getImageMode() {
191       return mImageMode;
192     }
193 
194     /** */
195     // @RestrictTo(RestrictTo.Scope.LIBRARY)
getMaxWidth()196     public int getMaxWidth() {
197       return mMaxWidth;
198     }
199 
200     /** */
201     // @RestrictTo(RestrictTo.Scope.LIBRARY)
getMaxHeight()202     public int getMaxHeight() {
203       return mMaxHeight;
204     }
205 
206     /** */
207     // @RestrictTo(RestrictTo.Scope.LIBRARY)
getFormat()208     public Bitmap.CompressFormat getFormat() {
209       return mFormat;
210     }
211 
212     /** */
213     // @RestrictTo(RestrictTo.Scope.LIBRARY)
getQuality()214     public int getQuality() {
215       return mQuality;
216     }
217 
218     /**
219      * Sets how {@link android.app.slice.SliceItem#FORMAT_ACTION} items should be handled.
220      *
221      * <p>The default mode is {@link #MODE_THROW}.
222      *
223      * @param mode The desired mode.
224      */
setActionMode(@ormatMode int mode)225     public SerializeOptions setActionMode(@FormatMode int mode) {
226       mActionMode = mode;
227       return this;
228     }
229 
230     /**
231      * Sets how {@link android.app.slice.SliceItem#FORMAT_IMAGE} items should be handled.
232      *
233      * <p>The default mode is {@link #MODE_THROW}.
234      *
235      * @param mode The desired mode.
236      */
setImageMode(@ormatMode int mode)237     public SerializeOptions setImageMode(@FormatMode int mode) {
238       mImageMode = mode;
239       return this;
240     }
241 
242     /**
243      * Set the maximum width of an image to use when serializing.
244      *
245      * <p>Will only be used if the {@link #setImageMode(int)} is set to {@link #MODE_CONVERT}. Any
246      * images larger than the maximum size will be scaled down to fit within that size. The default
247      * value is 1000.
248      */
setMaxImageWidth(int width)249     public SerializeOptions setMaxImageWidth(int width) {
250       mMaxWidth = width;
251       return this;
252     }
253 
254     /**
255      * Set the maximum height of an image to use when serializing.
256      *
257      * <p>Will only be used if the {@link #setImageMode(int)} is set to {@link #MODE_CONVERT}. Any
258      * images larger than the maximum size will be scaled down to fit within that size. The default
259      * value is 1000.
260      */
setMaxImageHeight(int height)261     public SerializeOptions setMaxImageHeight(int height) {
262       mMaxHeight = height;
263       return this;
264     }
265 
266     /**
267      * Sets the options to use when converting icons to be serialized. Only used if the image mode
268      * is set to {@link #MODE_CONVERT}.
269      *
270      * @param format The format to encode images with, default is {@link
271      *     android.graphics.Bitmap.CompressFormat#PNG}.
272      * @param quality The quality to use when encoding images.
273      */
setImageConversionFormat(Bitmap.CompressFormat format, int quality)274     public SerializeOptions setImageConversionFormat(Bitmap.CompressFormat format, int quality) {
275       mFormat = format;
276       mQuality = quality;
277       return this;
278     }
279   }
280 
281   /**
282    * A listener used to receive events on slices parsed with {@link #parseSlice(Context,
283    * InputStream, String, SliceActionListener)}.
284    */
285   public interface SliceActionListener {
286     /**
287      * Called when an action is triggered on a slice parsed with {@link #parseSlice(Context,
288      * InputStream, String, SliceActionListener)}.
289      *
290      * @param actionUri The uri of the action selected.
291      * @param context The context passed to {@link SliceItem#fireAction(Context, Intent)}
292      * @param intent The intent passed to {@link SliceItem#fireAction(Context, Intent)}
293      */
onSliceAction(Uri actionUri, Context context, Intent intent)294     void onSliceAction(Uri actionUri, Context context, Intent intent);
295   }
296 
297   /**
298    * Exception thrown during {@link #parseSlice(Context, InputStream, String, SliceActionListener)}.
299    */
300   public static class SliceParseException extends Exception {
301     /** */
302     // @RestrictTo(RestrictTo.Scope.LIBRARY)
SliceParseException(String s, Throwable e)303     public SliceParseException(String s, Throwable e) {
304       super(s, e);
305     }
306 
307     /** */
308     // @RestrictTo(RestrictTo.Scope.LIBRARY)
SliceParseException(String s)309     public SliceParseException(String s) {
310       super(s);
311     }
312   }
313 }
314