/* * Copyright 2016 Flipkart Internet Pvt. Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.flipkart.android.proteus.builder; import android.support.annotation.NonNull; import android.support.annotation.Nullable; import android.util.Log; import android.view.View; import android.view.ViewGroup; import com.flipkart.android.proteus.DataContext; import com.flipkart.android.proteus.parser.LayoutHandler; import com.flipkart.android.proteus.toolbox.BitmapLoader; import com.flipkart.android.proteus.toolbox.IdGenerator; import com.flipkart.android.proteus.toolbox.ProteusConstants; import com.flipkart.android.proteus.toolbox.Styles; import com.flipkart.android.proteus.toolbox.Utils; import com.flipkart.android.proteus.view.ProteusView; import com.flipkart.android.proteus.view.manager.ProteusViewManager; import com.flipkart.android.proteus.view.manager.ProteusViewManagerImpl; import com.google.gson.JsonElement; import com.google.gson.JsonObject; import java.util.HashMap; import java.util.Map; /** * A layout builder which can parse json to construct an android view out of it. It uses the * registered handlers to convert the json string to a view and then assign attributes. */ public class SimpleLayoutBuilder implements LayoutBuilder { private static final String TAG = "SimpleLayoutBuilder"; @Nullable protected LayoutBuilderCallback listener; private HashMap<String, LayoutHandler> layoutHandlers = new HashMap<>(); @Nullable private BitmapLoader bitmapLoader; private boolean isSynchronousRendering = false; private IdGenerator idGenerator; protected SimpleLayoutBuilder(@NonNull IdGenerator idGenerator) { this.idGenerator = idGenerator; } @Override public void registerHandler(String type, LayoutHandler handler) { handler.prepareAttributeHandlers(); layoutHandlers.put(type, handler); } @Override public void unregisterHandler(String type) { layoutHandlers.remove(type); } @Override @Nullable public LayoutHandler getHandler(String type) { return layoutHandlers.get(type); } @Override @Nullable public ProteusView build(ViewGroup parent, JsonObject layout, JsonObject data, int index, Styles styles) { String type = Utils.getPropertyAsString(layout, ProteusConstants.TYPE); if (type == null) { throw new IllegalArgumentException("'type' missing in layout: " + layout.toString()); } LayoutHandler handler = layoutHandlers.get(type); if (handler == null) { return onUnknownViewEncountered(type, parent, layout, data, index, styles); } /** * View creation. */ final ProteusView view; onBeforeCreateView(handler, parent, layout, data, index, styles); view = createView(handler, parent, layout, data, index, styles); onAfterCreateView(handler, view, parent, layout, data, index, styles); ProteusViewManager viewManager = createViewManager(handler, parent, layout, data, index, styles); viewManager.setView((View) view); view.setViewManager(viewManager); /** * Parsing each attribute and setting it on the view. */ JsonElement value; String attribute; for (Map.Entry<String, JsonElement> entry : layout.entrySet()) { if (ProteusConstants.TYPE.equals(entry.getKey())) { continue; } value = entry.getValue(); attribute = entry.getKey(); boolean handled = handleAttribute(handler, view, attribute, value); if (!handled) { onUnknownAttributeEncountered(attribute, value, view); } } return view; } protected void onBeforeCreateView(LayoutHandler handler, ViewGroup parent, JsonObject layout, JsonObject data, int index, Styles styles) { handler.onBeforeCreateView(parent, layout, data, styles, index); } protected ProteusView createView(LayoutHandler handler, ViewGroup parent, JsonObject layout, JsonObject data, int index, Styles styles) { return handler.createView(parent, layout, data, styles, index); } protected void onAfterCreateView(LayoutHandler handler, ProteusView view, ViewGroup parent, JsonObject layout, JsonObject data, int index, Styles styles) { //noinspection unchecked handler.onAfterCreateView((View) view, parent, layout, data, styles, index); } protected ProteusViewManager createViewManager(LayoutHandler handler, View parent, JsonObject layout, JsonObject data, int index, Styles styles) { if (ProteusConstants.isLoggingEnabled()) { Log.d(TAG, "ProteusView created with " + Utils.getLayoutIdentifier(layout)); } ProteusViewManagerImpl viewManager = new ProteusViewManagerImpl(); DataContext dataContext = new DataContext(); dataContext.setData(data); dataContext.setIndex(index); viewManager.setLayout(layout); viewManager.setDataContext(dataContext); viewManager.setStyles(styles); viewManager.setLayoutBuilder(this); viewManager.setLayoutHandler(handler); return viewManager; } public boolean handleAttribute(LayoutHandler handler, ProteusView view, String attribute, JsonElement value) { if (ProteusConstants.isLoggingEnabled()) { Log.d(TAG, "Handle '" + attribute + "' : " + value.toString() + " for view with " + Utils.getLayoutIdentifier(view.getViewManager().getLayout())); } //noinspection unchecked return handler.handleAttribute((View) view, attribute, value); } protected void onUnknownAttributeEncountered(String attribute, JsonElement value, ProteusView view) { if (listener != null) { listener.onUnknownAttribute(attribute, value, view); } } @Nullable protected ProteusView onUnknownViewEncountered(String type, ViewGroup parent, JsonObject layout, JsonObject data, int index, Styles styles) { if (ProteusConstants.isLoggingEnabled()) { Log.d(TAG, "No LayoutHandler for: " + type); } if (listener != null) { return listener.onUnknownViewType(type, parent, layout, data, index, styles); } return null; } @Override public int getUniqueViewId(String id) { return idGenerator.getUnique(id); } @Nullable @Override public IdGenerator getIdGenerator() { return idGenerator; } @Nullable @Override public LayoutBuilderCallback getListener() { return listener; } @Override public void setListener(@Nullable LayoutBuilderCallback listener) { this.listener = listener; } @Override public BitmapLoader getNetworkDrawableHelper() { return bitmapLoader; } @Override public void setBitmapLoader(@Nullable BitmapLoader bitmapLoader) { this.bitmapLoader = bitmapLoader; } @Override public boolean isSynchronousRendering() { return isSynchronousRendering; } @Override public void setSynchronousRendering(boolean isSynchronousRendering) { this.isSynchronousRendering = isSynchronousRendering; } }