package treehou.se.habit.ui.sitemaps; import android.os.Build; import android.os.Bundle; import android.util.Log; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.LinearLayout; import android.widget.Toast; import com.google.gson.Gson; import com.trello.rxlifecycle.components.support.RxFragment; import java.util.ArrayList; import java.util.List; import javax.inject.Inject; import butterknife.BindView; import butterknife.ButterKnife; import butterknife.Unbinder; import io.realm.Realm; import rx.Subscription; import se.treehou.ng.ohcommunicator.connector.GsonHelper; import se.treehou.ng.ohcommunicator.connector.models.OHLinkedPage; import se.treehou.ng.ohcommunicator.connector.models.OHServer; import se.treehou.ng.ohcommunicator.connector.models.OHWidget; import se.treehou.ng.ohcommunicator.services.IServerHandler; import treehou.se.habit.HabitApplication; import treehou.se.habit.R; import treehou.se.habit.core.db.model.ServerDB; import treehou.se.habit.module.ServerLoaderFactory; import treehou.se.habit.ui.widgets.WidgetFactory; import treehou.se.habit.util.ConnectionFactory; import treehou.se.habit.util.RxUtil; public class PageFragment extends RxFragment { private static final String TAG = "PageFragment"; // Arguments private static final String ARG_PAGE = "ARG_PAGE"; private static final String ARG_SERVER = "ARG_SERVER"; private static final String STATE_PAGE = "STATE_PAGE"; @BindView(R.id.lou_widgets) LinearLayout louFragments; @Inject ConnectionFactory connectionFactory; @Inject ServerLoaderFactory serverLoaderFactory; private ServerDB server; private OHLinkedPage page; private WidgetFactory widgetFactory; private List<OHWidget> widgets = new ArrayList<>(); private List<WidgetFactory.IWidgetHolder> widgetHolders = new ArrayList<>(); private Unbinder unbinder; private boolean initialized = false; /** * Creates a new instane of the page. * * @param server the server to connect to * @param page the page to visualise * * @return Fragment visualazing a page */ public static PageFragment newInstance(ServerDB server, OHLinkedPage page) { Gson gson = GsonHelper.createGsonBuilder(); Bundle args = new Bundle(); args.putString(ARG_PAGE, gson.toJson(page)); args.putLong(ARG_SERVER, server.getId()); PageFragment fragment = new PageFragment(); fragment.setArguments(args); return fragment; } public PageFragment() {} @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); ((HabitApplication) getActivity().getApplication()).component().inject(this); Realm realm = Realm.getDefaultInstance(); Bundle args = getArguments(); Gson gson = GsonHelper.createGsonBuilder(); long serverId = args.getLong(ARG_SERVER); String jPage = args.getString(ARG_PAGE); server = ServerDB.load(realm, serverId); page = gson.fromJson(jPage, OHLinkedPage.class); initialized = false; if(savedInstanceState != null && savedInstanceState.containsKey(STATE_PAGE)) { jPage = savedInstanceState.getString(STATE_PAGE); OHLinkedPage savedPage = gson.fromJson(jPage, OHLinkedPage.class); if(savedPage.getId().equals(page.getId())) { page = savedPage; initialized = true; } } } @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { View view = inflater.inflate(R.layout.fragment_widget, container, false); unbinder = ButterKnife.bind(this, view); updatePage(page); if(!initialized && server != null) { requestPageUpdate(); } initialized = true; return view; } /** * Request page from server. */ private void requestPageUpdate(){ final IServerHandler serverHandler = connectionFactory.createServerHandler(server.toGeneric(), getActivity()); serverHandler.requestPageRx(page) .compose(this.bindToLifecycle()) .compose(RxUtil.newToMainSchedulers()) .subscribe(ohLinkedPage -> { Log.d(TAG, "Received update " + ohLinkedPage.getWidgets().size() + " widgets from " + page.getLink()); updatePage(ohLinkedPage); }, throwable -> { Log.e(TAG, "Error when requesting page ", throwable); Toast.makeText(getActivity(), R.string.lost_server_connection, Toast.LENGTH_LONG).show(); getActivity().getSupportFragmentManager().popBackStack(); }); } /** * Create longpoller listening for updates of page. * * @return */ private Subscription createLongPoller() { final long serverId = server.getId(); Realm realm = Realm.getDefaultInstance(); OHServer server = serverLoaderFactory.loadServer(realm, serverId); realm.close(); final IServerHandler serverHandler = connectionFactory.createServerHandler(server, getActivity()); return serverHandler.requestPageUpdatesRx(server, page) .compose(this.bindToLifecycle()) .compose(RxUtil.newToMainSchedulers()) .subscribe(this::updatePage); } @Override public void onResume() { super.onResume(); // Start listening for server updates if (supportsLongPolling()) { createLongPoller(); } } /** * Check if android device supports long polling. * @return true if long polling is supported, else false. */ private boolean supportsLongPolling(){ return android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT && server != null; } @Override public void onDestroyView() { super.onDestroyView(); unbinder.unbind(); } /** * Update page. * * Recreate all widgets needed. * * @param page */ private synchronized void updatePage(final OHLinkedPage page){ Log.d(TAG, "Updating page " + page.getTitle() + " widgets " + widgets.size() + " : " + page.getWidgets().size()); this.page = page; widgetFactory = new WidgetFactory(getActivity(), server, page); final List<OHWidget> pageWidgets = page.getWidgets(); boolean invalidate = pageWidgets.size() != widgets.size(); if(!invalidate){ for(int i=0; i < widgets.size(); i++) { OHWidget currentWidget = widgets.get(i); OHWidget newWidget = pageWidgets.get(i); if(currentWidget.needUpdate(newWidget)){ Log.d(TAG, "Widget " + currentWidget.getType() + " " + currentWidget.getLabel() + " needs update"); invalidate = true; break; } } } final boolean invalidateWidgets = invalidate; if(invalidateWidgets) { invalidateWidgets(pageWidgets); } else { updateWidgets(pageWidgets); } } /** * Invalidate all widgets in page. * @param pageWidgets the widgets to update. */ private void invalidateWidgets(List<OHWidget> pageWidgets){ widgetHolders.clear(); louFragments.removeAllViews(); for (OHWidget widget : pageWidgets) { try { WidgetFactory.IWidgetHolder result = widgetFactory.createWidget(widget, null); widgetHolders.add(result); louFragments.addView(result.getView()); } catch (Exception e) { Log.w(TAG, "Create widget failed", e); } } widgets.clear(); widgets.addAll(pageWidgets); } /** * Update widgets in page. * @param pageWidgets the data to update widgets with. */ private void updateWidgets(List<OHWidget> pageWidgets){ for (int i=0; i < widgetHolders.size(); i++) { try { WidgetFactory.IWidgetHolder holder = widgetHolders.get(i); Log.d(TAG, "updating widget " + holder.getClass().getSimpleName()); OHWidget newWidget = pageWidgets.get(i); holder.update(newWidget); } catch (Exception e) { Log.w(TAG, "Updating widget failed", e); } } } @Override public void onSaveInstanceState(Bundle outState) { outState.putSerializable(STATE_PAGE, GsonHelper.createGsonBuilder().toJson(page)); super.onSaveInstanceState(outState); } }