blob: 4394dea2cc6c9526e8075c0ae90e0142ce963fd5 [file] [log] [blame]
// Copyright 2018 The Feed Authors.
//
// 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.google.android.libraries.feed.piet;
import android.content.Context;
import android.view.View;
import android.widget.FrameLayout;
import com.google.android.libraries.feed.common.logging.Logger;
import com.google.android.libraries.feed.piet.AdapterFactory.SingletonKeySupplier;
import com.google.android.libraries.feed.piet.DebugLogger.MessageType;
import com.google.search.now.ui.piet.ElementsProto.BindingValue;
import com.google.search.now.ui.piet.ElementsProto.CustomElement;
import com.google.search.now.ui.piet.ElementsProto.CustomElementData;
import com.google.search.now.ui.piet.ElementsProto.Element;
import com.google.search.now.ui.piet.ElementsProto.Visibility;
import com.google.search.now.ui.piet.ErrorsProto.ErrorCode;
/** Adapter that manages a custom view created by the host. */
class CustomElementAdapter extends ElementAdapter<FrameLayout, CustomElement> {
private static final String TAG = "CustomElementAdapter";
CustomElementData boundCustomElementData = CustomElementData.getDefaultInstance();
CustomElementAdapter(Context context, AdapterParameters parameters) {
super(context, parameters, new FrameLayout(context), KeySupplier.SINGLETON_KEY);
}
@Override
protected CustomElement getModelFromElement(Element baseElement) {
if (!baseElement.hasCustomElement()) {
throw new PietFatalException(
ErrorCode.ERR_MISSING_ELEMENT_CONTENTS,
String.format("Missing CustomElement; has %s", baseElement.getElementsCase()));
}
return baseElement.getCustomElement();
}
@Override
void onBindModel(CustomElement customElement, Element baseElement, FrameContext frameContext) {
switch (customElement.getContentCase()) {
case CUSTOM_ELEMENT_DATA:
boundCustomElementData = customElement.getCustomElementData();
break;
case CUSTOM_BINDING:
BindingValue binding =
frameContext.getCustomElementBindingValue(customElement.getCustomBinding());
if (!binding.hasCustomElementData()) {
if (customElement.getCustomBinding().getIsOptional()) {
setVisibilityOnView(Visibility.GONE);
return;
} else {
throw new PietFatalException(
ErrorCode.ERR_MISSING_OR_UNHANDLED_CONTENT,
frameContext.reportMessage(
MessageType.ERROR,
ErrorCode.ERR_MISSING_OR_UNHANDLED_CONTENT,
String.format(
"Custom element binding %s had no content", binding.getBindingId())));
}
}
boundCustomElementData = binding.getCustomElementData();
break;
default:
Logger.e(
TAG,
frameContext.reportMessage(
MessageType.ERROR,
ErrorCode.ERR_MISSING_OR_UNHANDLED_CONTENT,
"Missing payload in CustomElement"));
return;
}
View v =
getParameters()
.hostProviders
.getCustomElementProvider()
.createCustomElement(boundCustomElementData);
getBaseView().addView(v);
}
@Override
void onUnbindModel() {
FrameLayout baseView = getBaseView();
// There should be a maximum of one child that was bound, so using the CustomElementData that
// was saved during the last bind should be fine.
if (baseView != null && baseView.getChildCount() > 0) {
for (int i = 0; i < baseView.getChildCount(); i++) {
getParameters()
.hostProviders
.getCustomElementProvider()
.releaseCustomView(baseView.getChildAt(i), boundCustomElementData);
}
baseView.removeAllViews();
}
}
static class KeySupplier extends SingletonKeySupplier<CustomElementAdapter, CustomElement> {
@Override
public String getAdapterTag() {
return TAG;
}
@Override
public CustomElementAdapter getAdapter(Context context, AdapterParameters parameters) {
return new CustomElementAdapter(context, parameters);
}
}
}