1 /*
2  * Copyright 2021 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.appsearch.platformstorage.converter;
18 
19 import android.os.Build;
20 
21 import androidx.annotation.DoNotInline;
22 import androidx.annotation.RequiresApi;
23 import androidx.annotation.RestrictTo;
24 import androidx.appsearch.app.GenericDocument;
25 import androidx.appsearch.app.GetByDocumentIdRequest;
26 import androidx.appsearch.app.PutDocumentsRequest;
27 import androidx.appsearch.app.RemoveByDocumentIdRequest;
28 import androidx.appsearch.app.ReportSystemUsageRequest;
29 import androidx.appsearch.app.ReportUsageRequest;
30 import androidx.core.util.Preconditions;
31 
32 import org.jspecify.annotations.NonNull;
33 
34 import java.util.List;
35 import java.util.Map;
36 
37 /**
38  * Translates between Platform and Jetpack versions of requests.
39  * @exportToFramework:hide
40  */
41 @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
42 @RequiresApi(Build.VERSION_CODES.S)
43 public final class RequestToPlatformConverter {
RequestToPlatformConverter()44     private RequestToPlatformConverter() {}
45 
46     /**
47      * Translates a jetpack {@link PutDocumentsRequest} into a platform
48      * {@link android.app.appsearch.PutDocumentsRequest}.
49      */
toPlatformPutDocumentsRequest( @onNull PutDocumentsRequest jetpackRequest)50     public static android.app.appsearch.@NonNull PutDocumentsRequest toPlatformPutDocumentsRequest(
51             @NonNull PutDocumentsRequest jetpackRequest) {
52         Preconditions.checkNotNull(jetpackRequest);
53         android.app.appsearch.PutDocumentsRequest.Builder platformBuilder =
54                 new android.app.appsearch.PutDocumentsRequest.Builder();
55         // Convert normal generic documents.
56         for (GenericDocument jetpackDocument : jetpackRequest.getGenericDocuments()) {
57             platformBuilder.addGenericDocuments(
58                     GenericDocumentToPlatformConverter.toPlatformGenericDocument(jetpackDocument));
59         }
60         // Convert taken action generic documents.
61         for (GenericDocument jetpackTakenActionGenericDocument :
62                 jetpackRequest.getTakenActionGenericDocuments()) {
63             if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.VANILLA_ICE_CREAM) {
64                 ApiHelperForV.addTakenActionGenericDocuments(
65                         platformBuilder,
66                         GenericDocumentToPlatformConverter.toPlatformGenericDocument(
67                                 jetpackTakenActionGenericDocument));
68             } else {
69                 // This version of platform-storage doesn't support the dedicated
70                 // addTakenActionGenericDocuments API, but we can still add them to the index via
71                 // the put API (just without logging).
72                 platformBuilder.addGenericDocuments(
73                         GenericDocumentToPlatformConverter.toPlatformGenericDocument(
74                                 jetpackTakenActionGenericDocument));
75             }
76         }
77         return platformBuilder.build();
78     }
79 
80     /**
81      * Translates a jetpack {@link GetByDocumentIdRequest} into a platform
82      * {@link android.app.appsearch.GetByDocumentIdRequest}.
83      */
84     public static android.app.appsearch.@NonNull GetByDocumentIdRequest
toPlatformGetByDocumentIdRequest(@onNull GetByDocumentIdRequest jetpackRequest)85             toPlatformGetByDocumentIdRequest(@NonNull GetByDocumentIdRequest jetpackRequest) {
86         Preconditions.checkNotNull(jetpackRequest);
87         android.app.appsearch.GetByDocumentIdRequest.Builder platformBuilder =
88                 new android.app.appsearch.GetByDocumentIdRequest.Builder(
89                         jetpackRequest.getNamespace())
90                         .addIds(jetpackRequest.getIds());
91         for (Map.Entry<String, List<String>> projection :
92                 jetpackRequest.getProjections().entrySet()) {
93             platformBuilder.addProjection(projection.getKey(), projection.getValue());
94         }
95         return platformBuilder.build();
96     }
97 
98     /**
99      * Translates a jetpack {@link RemoveByDocumentIdRequest} into a platform
100      * {@link android.app.appsearch.RemoveByDocumentIdRequest}.
101      */
102     public static android.app.appsearch.@NonNull RemoveByDocumentIdRequest
toPlatformRemoveByDocumentIdRequest( @onNull RemoveByDocumentIdRequest jetpackRequest)103             toPlatformRemoveByDocumentIdRequest(
104             @NonNull RemoveByDocumentIdRequest jetpackRequest) {
105         Preconditions.checkNotNull(jetpackRequest);
106         return new android.app.appsearch.RemoveByDocumentIdRequest.Builder(
107                 jetpackRequest.getNamespace())
108                 .addIds(jetpackRequest.getIds())
109                 .build();
110     }
111 
112     /**
113      * Translates a jetpack {@link androidx.appsearch.app.ReportUsageRequest} into a platform
114      * {@link android.app.appsearch.ReportUsageRequest}.
115      */
toPlatformReportUsageRequest( @onNull ReportUsageRequest jetpackRequest)116     public static android.app.appsearch.@NonNull ReportUsageRequest toPlatformReportUsageRequest(
117             @NonNull ReportUsageRequest jetpackRequest) {
118         Preconditions.checkNotNull(jetpackRequest);
119         return new android.app.appsearch.ReportUsageRequest.Builder(
120                 jetpackRequest.getNamespace(), jetpackRequest.getDocumentId())
121                 .setUsageTimestampMillis(jetpackRequest.getUsageTimestampMillis())
122                 .build();
123     }
124 
125     /**
126      * Translates a jetpack {@link androidx.appsearch.app.ReportSystemUsageRequest} into a platform
127      * {@link android.app.appsearch.ReportSystemUsageRequest}.
128      */
129     public static android.app.appsearch.@NonNull ReportSystemUsageRequest
toPlatformReportSystemUsageRequest(@onNull ReportSystemUsageRequest jetpackRequest)130             toPlatformReportSystemUsageRequest(@NonNull ReportSystemUsageRequest jetpackRequest) {
131         Preconditions.checkNotNull(jetpackRequest);
132         return new android.app.appsearch.ReportSystemUsageRequest.Builder(
133                 jetpackRequest.getPackageName(),
134                 jetpackRequest.getDatabaseName(),
135                 jetpackRequest.getNamespace(),
136                 jetpackRequest.getDocumentId())
137                 .setUsageTimestampMillis(jetpackRequest.getUsageTimestampMillis())
138                 .build();
139     }
140 
141     @RequiresApi(Build.VERSION_CODES.VANILLA_ICE_CREAM)
142     private static class ApiHelperForV {
ApiHelperForV()143         private ApiHelperForV() {}
144 
145         @DoNotInline
addTakenActionGenericDocuments( android.app.appsearch.PutDocumentsRequest.Builder platformBuilder, android.app.appsearch.GenericDocument platformTakenActionGenericDocument)146         static void addTakenActionGenericDocuments(
147                 android.app.appsearch.PutDocumentsRequest.Builder platformBuilder,
148                 android.app.appsearch.GenericDocument platformTakenActionGenericDocument) {
149             try {
150                 platformBuilder.addTakenActionGenericDocuments(platformTakenActionGenericDocument);
151             } catch (android.app.appsearch.exceptions.AppSearchException e) {
152                 // This method incorrectly declares that it throws AppSearchException, whereas in
153                 // fact there's nothing in its implementation that would do so. Suppress it here
154                 // instead of piping all the way through the stack.
155                 throw new RuntimeException(
156                         "Unexpected AppSearchException which should not be possible", e);
157             }
158         }
159     }
160 }
161