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
17package com.android.layoutlib.bridge.remote.server;
18
19import com.android.ide.common.rendering.api.DrawableParams;
20import com.android.ide.common.rendering.api.RenderParams;
21import com.android.ide.common.rendering.api.Result;
22import com.android.ide.common.rendering.api.SessionParams;
23import com.android.layout.remote.api.RemoteBridge;
24import com.android.layout.remote.api.RemoteDrawableParams;
25import com.android.layout.remote.api.RemoteLayoutLog;
26import com.android.layout.remote.api.RemoteRenderParams;
27import com.android.layout.remote.api.RemoteRenderSession;
28import com.android.layout.remote.api.RemoteSessionParams;
29import com.android.layoutlib.bridge.Bridge;
30import com.android.layoutlib.bridge.remote.server.adapters.RemoteAssetRepositoryAdapter;
31import com.android.layoutlib.bridge.remote.server.adapters.RemoteILayoutPullParserAdapter;
32import com.android.layoutlib.bridge.remote.server.adapters.RemoteLayoutLogAdapter;
33import com.android.layoutlib.bridge.remote.server.adapters.RemoteLayoutlibCallbackAdapter;
34import com.android.layoutlib.bridge.remote.server.adapters.RemoteRenderResourcesAdapter;
35import com.android.layoutlib.bridge.remote.server.adapters.RemoteRenderSessionAdapter;
36import com.android.tools.layoutlib.annotations.NotNull;
37
38import java.io.File;
39import java.rmi.RemoteException;
40import java.util.HashMap;
41import java.util.HashSet;
42import java.util.Map;
43import java.util.Set;
44
45/**
46 * Remote {@link Bridge} implementation. This class is the remote entry point for the server. Its
47 * responsibility is to receive the remote calls and apply the required transformations to convert
48 * it into a regular call to the {@link Bridge} class.
49 */
50public class RemoteBridgeImpl implements RemoteBridge {
51    private Bridge mBridge = new Bridge();
52
53    /**
54     * The project keys are used as key for some caches. They are usually expected to remain in
55     * memory so WeakReferences are used in the caches.
56     * Because in the remote bridge we do not have a real pointer to the object, we keep the strings
57     * in memory until they are cleared.
58     */
59    private Map<String, String> mCachedProjectKeys = new HashMap<>();
60
61    @Override
62    public int getApiLevel() {
63        return mBridge.getApiLevel();
64    }
65
66    @Override
67    public int getRevision() {
68        return mBridge.getRevision();
69    }
70
71    @Override
72    public boolean supports(int feature) {
73        return mBridge.supports(feature);
74    }
75
76    @Override
77    public boolean init(Map<String, String> platformProperties, File fontLocation,
78            Map<String, Map<String, Integer>> enumValueMap, RemoteLayoutLog log) {
79        return mBridge.init(platformProperties, fontLocation, enumValueMap,
80                log != null ? new RemoteLayoutLogAdapter(log) : null);
81    }
82
83    @Override
84    public boolean dispose() {
85        return mBridge.dispose();
86    }
87
88    private static void setupRenderParams(@NotNull RenderParams params,
89            @NotNull RemoteRenderParams remoteParams) throws RemoteException {
90        params.setAssetRepository(new RemoteAssetRepositoryAdapter(remoteParams.getAssets()));
91        params.setActivityName(remoteParams.getActivityName());
92        params.setAppIcon(remoteParams.getAppIcon());
93        params.setAppLabel(remoteParams.getAppLabel());
94        params.setTimeout(remoteParams.getTimeout());
95        params.setLocale(remoteParams.getLocale());
96        if (remoteParams.isForceNoDecor()) {
97            params.setForceNoDecor();
98        }
99        params.setRtlSupport(remoteParams.isRtlSupported());
100        if (remoteParams.isBgColorOverridden()) {
101            params.setOverrideBgColor(remoteParams.getOverrideBgColor());
102        }
103        params.setImageFactory(remoteParams.getImageFactory());
104        // TODO: Also unpack remote flags and pass them to RenderParams
105    }
106
107    @NotNull
108    @Override
109    public RemoteRenderSession createSession(@NotNull RemoteSessionParams remoteParams) {
110        try {
111            String projectKey = mCachedProjectKeys.putIfAbsent(remoteParams.getProjectKey(),
112                    remoteParams.getProjectKey());
113
114            // Unpack the remote params and convert it into the local SessionParams
115            SessionParams params = new SessionParams(
116                    new RemoteILayoutPullParserAdapter(remoteParams.getLayoutDescription()),
117                    remoteParams.getRenderingMode(), projectKey,
118                    remoteParams.getRemoteHardwareConfig().getHardwareConfig(),
119                    new RemoteRenderResourcesAdapter(remoteParams.getRemoteResources()),
120                    new RemoteLayoutlibCallbackAdapter(remoteParams.getRemoteLayoutlibCallback()),
121                    remoteParams.getMinSdkVersion(), remoteParams.getTargetSdkVersion(),
122                    new RemoteLayoutLogAdapter(remoteParams.getLog()));
123            setupRenderParams(params, remoteParams);
124            return RemoteRenderSessionAdapter.create(mBridge.createSession(params));
125
126        } catch (RemoteException e) {
127            throw new RuntimeException(e);
128        }
129    }
130
131    @NotNull
132    @Override
133    public Result renderDrawable(@NotNull RemoteDrawableParams remoteParams) {
134        try {
135            String projectKey = mCachedProjectKeys.putIfAbsent(remoteParams.getProjectKey(),
136                    remoteParams.getProjectKey());
137
138            DrawableParams params = new DrawableParams(
139                    remoteParams.getDrawable(),
140                    projectKey,
141                    remoteParams.getRemoteHardwareConfig().getHardwareConfig(),
142                    new RemoteRenderResourcesAdapter(remoteParams.getRemoteResources()),
143                    new RemoteLayoutlibCallbackAdapter(remoteParams.getRemoteLayoutlibCallback()),
144                    remoteParams.getMinSdkVersion(), remoteParams.getTargetSdkVersion(),
145                    new RemoteLayoutLogAdapter(remoteParams.getLog()));
146            setupRenderParams(params, remoteParams);
147            return mBridge.renderDrawable(params);
148        } catch (RemoteException e) {
149            throw new RuntimeException(e);
150        }
151    }
152
153    @Override
154    public void clearCaches(String projectKey) {
155        mCachedProjectKeys.remove(projectKey);
156        mBridge.clearCaches(projectKey);
157    }
158
159    @Override
160    public boolean isRtl(String locale) {
161        return mBridge.isRtl(locale);
162    }
163}
164