Prepare MyGlass Activity

We will now prepare the Activity that host the Glass mechanism on which shared data is created and presented. Glass can be established either on Activity or Fragment. We will show here an example of using BasicGlassActivity . The provided reference apps shows also example of using BasicGlassFragment (see cg.android.samples.supermarket.place.MyReviewsGlass in the supermarket reference app)

This tutorial assume that you already familiar with the basic programming of Android app so we put the focus only on main issues related with Glass operation. We will now create new Activity in the project and later we will call it from our MainActivity:

 

Create the class

Create class MyGlass that extends cg.android.activity.BasicGlassActivity and make sure to override any abstract method from the parent class.

package com.example.sosa; import android.view.ViewGroup; import cg.android.activity.BasicGlassActivity; /** * Created by Gilad Tiram on 22/10/2014. * <p/> * This class demonstrate simple and didactic Activity that uses the 'Object Glass' pattern * for associating widgets in relation with some code that we assume is a representation * for a state of some object. This is very abstract example mainly intend to show and explain the easy of creating * state oriented sharing app. A real application will probably refer to some more constructed object and possibly * with choose to represent the state of this object by scanning a code or maybe by taking a picture of the object, * or maybe relate with location or other sensor data or even use user entry of some predefined code or open code * field as we do here. * <p/> * Whatever the approach for representing the state of an object is, The concept remain the same: * State oriented sharing is the operation of sharing data in the form of widgets by associating the data to * state of something: Object, website, physical condition, physiological condition etc' * <p/> * The API let you create state oriented sharing apps where the entire sharing algorithms, as well as collaboration * algorithms, as well as data storage, network operations, search graph, views, dialogs, added value services and many * more advanced features (e.g. Copmputational SaaS) are all given to you by API specifically and the by the broad * Content Glass system. * <p/> * It is very easy to write simple State Oriented Sharing Apps. Of course you will have to take care the styling of * your app and the general user experience. Content Glass on the other end will provide you the entire infrastructure * so you can focus on creating your solution. * <p/> */ public class MyGlass extends BasicGlassActivity { @Override public ViewGroup getWidgetsContentView() { return null; } @Override public boolean isGlassIdDynamic() { return false; } @Override public void createWidgetsContentView() { } }

 

Add Activity in Manifext

Add the activity entry inthe manifest file

<activity android:name=".MyGlass" android:label="My Glass"> </activity>

 

 

Overriding methods

Now lets do some changes in the overriding methods to make the glass suitable for out example. Note that we are going to refer to some missing code and resources that we will add soon.  

@Override public ViewGroup getWidgetsContentView() { return contentView; } @Override public boolean isGlassIdDynamic() { return true; } @Override public void createWidgetsContentView() { setContentView(R.layout.my_object_glass); contentView = (ViewGroup) findViewById(R.id.glass_layout); if (contentView != null) { MyOnClickListener listener = new MyOnClickListener(); Button btn = (Button) findViewById(R.id.buttonSetContext); btn.setOnClickListener(listener); } else { throw new CGRuntimeException("Please check your layout"); } }

 

set onCreate method

Add the onCreate code to your MyGlass class. The nCreate code include an initiali8zation of the Content Glass engine. In pattern of this application, the state of an object, that is used for associating with widget (shared content) is not pre determine, but is determine dynamically by user code. Accordingly we saperate Glass initialization and Glass starting. In this step we only create and initialize the internal glass engine. The glass will be started later according to code provided by user. Of course there are different patterns for example in the case of Website a glass is determine by URL meaning we can initialize and start the glass upon page loading. In this example however we are using dynamically started glass.

/** * Initialize the glass and prepare the resource file for showing by help fragment * * @param savedInstanceState bundle */ @Override protected void onCreate(Bundle savedInstanceState) { //set the title from the title provided by the calling intent setTitle("My Glass"); //it is important to call the super before initializing the glass. super.onCreate(savedInstanceState); //initialize the glass. Layer will be set later getCGApplication().initContentGlass(null, null); }

 

 

Creating the layout

Lets first create layout that will use us to show shared content and also for creating shared content and attach it to some state. This is very didactic layout that has a button that will be used to open a wizard for setting the state of the context associate with shared widget, and a text that will show the state used for shared content. Create file my_object_glass.xml and locate under "res\layout" dir. 

<?xml version="1.0" encoding="utf-8"?> <!-- This is the layout on which we will share widgets and show shared widgets --> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width="match_parent" android:layout_height="match_parent" android:id="@+id/supermarket_place_shared_reviews_glass" > <Button android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Set sharing context" android:id="@+id/buttonSetContext" android:background="@android:color/holo_green_dark" android:textSize="25sp" android:layout_alignParentBottom="true" android:layout_centerHorizontal="true" android:paddingLeft="10dp" android:paddingRight="10dp"/> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:id="@+id/contextInfo" android:textAppearance="?android:attr/textAppearanceLarge" android:text="Sharing context: ..." android:layout_alignParentTop="true" android:layout_alignParentStart="true" android:layout_alignParentEnd="true"/> <Space android:layout_width="fill_parent" android:layout_height="30dp" android:id="@+id/space1" android:layout_above="@+id/buttonSetContext" android:layout_alignParentStart="true"/> <ScrollView xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/scroll" android:layout_width="fill_parent" android:layout_height="fill_parent" android:layout_below="@+id/contextInfo" android:layout_above="@+id/buttonSetContext" android:layout_alignParentEnd="true" android:fadeScrollbars="false" android:fillViewport="false" > <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width="fill_parent" android:layout_height="wrap_content" android:id="@+id/glass_layout" android:background="#ffffff"> </LinearLayout> </ScrollView> </RelativeLayout>

 

Implementing Click listener 

Add this code to your MyGlass for opening the state wizard upon user click

/** * Handle button events. Toggle glass fragments according to clicks button */ private class MyOnClickListener implements View.OnClickListener { @Override public void onClick(View view) { int itemId = view.getId(); if (itemId == R.id.buttonSetContext) { openSateWizard(); } } }

 

Method for calling the state wizard

Now we will add a method that open state wizard. State wizard is a class that help the user to set object state Id that will later be associated with shared widgets. Widgets are presented whenever your app is set to be in the state that was associated with the widget upon create. For example if you create Widget and associate it with the code "1234" then in order for other user (or you) to see the shared widget, the user need to enter some code: "1234" - This is the meaning of State Oriented sharing! The shared data is associated with State Id of some object or a thing, and is invoked when application is set to that state. We will discuss in more details later.  

/** * In order to attach a widget to and object we open state wizard that let the user select * a code that will be used as the context for sharing data. */ private void openSateWizard() { //open state wizard. Note that we use STATE_RECOGNITION as the request code Intent intent = new Intent(this, TextStateIdWizard.class); startActivityForResult(intent, CGFinals.STATE_RECOGNITION); }

 

 Handling State Wizard response

When user return from the state wizard, the response include settings of the selected state. In our example this will be a simple textual code set by the user. Add the following method for handlng the response from state wizard.

/** * Perform set of operations required in order to prepare the current glass for a given * text code that will be used as glass Id. The glass Id is part of the state that define * a state of an object to associate shared widgets with. * * @param glassId String that represent the selected title for sharing a comment on currently selected newspaper * @param stateType info about the selected state type either page number or title of an article */ private void setGlassForState(String glassId, String stateType) { //present glassId on screen setContentInfoText(glassId, stateType); //start (or restart) the glass CGLayer layer = new CGLayer(CGFinals.DEFAULT, glassId, CGFinals.GLASS_TYPE_OBJECT, null, false); getCGApplication().startContentGlass(layer); //invalidate options menu in order to enable menu items getActivity().invalidateOptionsMenu(); //ask user for creating new comment askUserForCreate(); } /** * Set the selected context info * * @param barcode either page number or title of an article used as glass Id * @param stateType info about the selected state type either page number or title of an article */ private void setContentInfoText(String barcode, String stateType) { ((TextView) getActivity().findViewById(R.id.contextInfo)).setText("Sharing context: " + stateType + " " + StringUtil.defaultIfEmpty(barcode, "...")); }

 

Creating Shared Widget

Now we will add the code that create shared widget. Thiscode is invoked once the Glass is set into state. In our case the widget can be created once user has set a code that designate a state of some Object (in the world , this is abstract example as being saidalready) 

 

/** * Create text widget that wil be used as a comment to newspaper article. Articles are identified by their * title. */ private void createsSharedWidget() { try { getCGApplication().createAndShowNewWidget(AndroidCGFinals.BASIC_LIBRARY, WIDGET_TYPE, null, null); } catch (CGException e) { getCGApplication().popupError("Fail to create widget!", e); } } /** * Ask the user if want to create new comment for selected article. */ private void askUserForCreate() { AlertDialog.Builder builder = new AlertDialog.Builder(this); builder.setMessage("Create new widget?") .setCancelable(false) .setPositiveButton("Yes", new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int id) { dialog.dismiss(); createsSharedWidget(); } }) .setNegativeButton("No", new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int id) { dialog.dismiss(); } }); AlertDialog alert = builder.create(); alert.show(); }

 

 Handling menus

Add the following code for controlling the menus related with a Glass. We have two menus here: one is for general options allowed for the MyGlass Activity and the second menu is related with context options of Widget (the component that represent shared content). We will soon create the missing XML resources.

/** * @return the Id for setting options menu for this fragment */ @Override public int getOptionsMenuId() { return R.menu.my_object_glass_menu; } /** * Handle custom option menu events * * @param item the clicked menu * @return true if consumed, else false. */ public boolean onCustomOptionsItemSelected(MenuItem item) { int itemId = item.getItemId(); if (itemId == R.id.add_widget) { createsSharedWidget(); return true; } else if (itemId == R.id.set_sharing_context) { openSateWizard(); return true; } return false; } /** * Prepare the custom options menu for this glass * * @param menu menu * @return true */ @Override public boolean onPrepareCustomOptionsMenu(Menu menu) { boolean cgOn = getCGApplication().isContentGlassStarted(); boolean widgetsHidden = getCGApplication().isWidgetsHidden(); menu.getItem(1).setEnabled(cgOn); menu.getItem(2).setEnabled(cgOn); menu.getItem(3).setEnabled(!widgetsHidden && cgOn); menu.getItem(4).setEnabled(widgetsHidden && cgOn); return true; } /** * @return the menu Id for the context menu used by this glass */ @Override public int getContextMenuId() { return cg.android.client.R.menu.default_widget_context_menu; } /** * Invoked when item of widget's context menu is selected. * * @param item the clicked menu item * @return true if the operation for relating a product to barcode was selected. Else false */ @Override public boolean onCustomContextItemSelected(MenuItem item) { int itemId = item.getItemId(); return false; }

 

Define options menu

Create file named my_object_glass_menu.xml and locate it under res/menus dir. Copy the code below to this file.

 

<?xml version="1.0" encoding="utf-8"?> <menu xmlns:android="http://schemas.android.com/apk/res/android"> <item android:id="@+id/add_widget" android:title="@string/add_widget"/> <item android:id="@+id/set_sharing_context" android:title="@string/set_sharing_context"/> <item android:id="@+id/open_identities_pref" android:title="@string/set_identities"/> <item android:id="@+id/clear_all_widget" android:title="@string/cg_clear_all_widget"/> <item android:id="@+id/hide_all_widgets" android:title="@string/cg_hide_all_widgets"/> <item android:id="@+id/show_all_widgets" android:title="@string/cg_show_all_widgets"/> </menu>

 

String values

Create file strings.xml and locate under res\string dir. Add the following code. 

<?xml version="1.0" encoding="utf-8"?> <resources> <string name="app_name">sosa</string> <string name="add_widget">add_widget</string> <string name="set_sharing_context">Set Sharing Context</string> <string name="sharing_context_info">Sharing context: ...</string> </resources>

 

 

Summary

You should now have the most that is required for basic State Oriented Sharing app. In the next chapters we will dive deeper into this "State Id"  issue and we will also discuss the flow of Object oriented glass. At this point your class should like something like this:

package com.example.sosa; import android.app.AlertDialog; import android.content.DialogInterface; import android.content.Intent; import android.os.Bundle; import android.view.Menu; import android.view.MenuItem; import android.view.View; import android.view.ViewGroup; import android.widget.Button; import android.widget.TextView; import cg.CGException; import cg.CGFinals; import cg.android.AndroidCGFinals; import cg.android.activity.BasicGlassActivity; import cg.android.view.state.StateRecognitionWizard; import cg.android.view.state.TextStateIdWizard; import cg.glass.CGLayer; import rhz.dom.RHZDom; import rhz.util.StringUtil; /** * Created by Gilad Tiram on 22/10/2014. * <p/> * This class demonstrate simple and didactic Activity that uses the 'Object Glass' pattern * for associating widgets in relation with some code that we assume is a representation * for a state of some object. This is very abstract example mainly intend to show and explain the easy of creating * state oriented sharing app. A real application will probably refer to some more constructed object and possibly * with choose to represent the state of this object by scanning a code or maybe by taking a picture of the object, * or maybe relate with location or other sensor data or even use user entry of some predefined code or open code * field as we do here. * <p/> * Whatever the approach for representing the state of an object is, The concept remain the same: * State oriented sharing is the operation of sharing data in the form of widgets by associating the data to * state of something: Object, website, physical condition, physiological condition etc' * <p/> * The API let you create state oriented sharing apps where the entire sharing algorithms, as well as collaboration * algorithms, as well as data storage, network operations, search graph, views, dialogs, added value services and many * more advanced features (e.g. Copmputational SaaS) are all given to you by API specifically and the by the broad * Content Glass system. * <p/> * It is very easy to write simple State Oriented Sharing Apps. Of course you will have to take care the styling of * your app and the general user experience. Content Glass on the other end will provide you the entire infrastructure * so you can focus on creating your solution. * <p/> * * */ public class MyGlass extends BasicGlassActivity { /** * The type of the widget we are using to share some info */ private static final String WIDGET_TYPE = "simple-text-widget"; @Override public ViewGroup getWidgetsContentView() { return contentView; } @Override public boolean isGlassIdDynamic() { return true; } @Override public void createWidgetsContentView() { setContentView(R.layout.my_object_glass); contentView = (ViewGroup) findViewById(cg.android.client.R.id.glass_layout); if (contentView != null) { MyOnClickListener listener = new MyOnClickListener(); Button btn = (Button) findViewById(R.id.buttonSetContext); btn.setOnClickListener(listener); } contentView = (ViewGroup) findViewById(cg.android.client.R.id.glass_layout); } /** * Initialize the glass and prepare the resource file for showing by help fragment * * @param savedInstanceState bundle */ @Override protected void onCreate(Bundle savedInstanceState) { //set the title from the title provided by the calling intent setTitle("My Glass"); //it is important to call the super before initializing the glass. super.onCreate(savedInstanceState); //initialize the glass. Layer will be set later getCGApplication().initContentGlass(null, null); } /** * Handle button events. Toggle glass fragments according to clicks button */ private class MyOnClickListener implements View.OnClickListener { @Override public void onClick(View view) { int itemId = view.getId(); if (itemId == R.id.buttonSetContext) { openSateWizard(); } } } /** * In order to attach a widget to and article or a page we open state wizard that let the user select * a page or a related title that will be used as the context for sharing data. */ private void openSateWizard() { //open state wizard. Note that we use STATE_RECOGNITION as the request code Intent intent = new Intent(this, TextStateIdWizard.class); startActivityForResult(intent, CGFinals.STATE_RECOGNITION); } /** * A callback response from the state wizard. We check that the request code match CGFinals.STATE_RECOGNITION * and if match we then examine the result code. If success operation then get Glass Id value and call * {@link #setGlassForState(String, String)} with the received Id. * * @param requestCode request code * @param resultCode result code * @param data intent data of response from barcode scanner activity * @see cg.android.view.state.StateRecognitionWizard#parseResponse(int, int, android.content.Intent) */ @Override public void onActivityResult(int requestCode, int resultCode, Intent data) { //parse the response code and get the response data RHZDom response = StateRecognitionWizard.parseResponse(requestCode, resultCode, data); int status = response.getInt(CGFinals.STATUS); if (status == CGFinals.SUCCESS_INT) { //success setGlassForState(response.getString(CGFinals.GLASS_ID), response.getString(CGFinals.INFO)); } else if (status == CGFinals.ERROR_INT) { //error getCGApplication().logError("Fail to prepare state! Details: " + response.getString(CGFinals.MESSAGE), null); } //we ignore other states: canceled state or not-relevant code state } /** * Perform set of operations required in order to prepare the current glass for a given * text code that will be used as glass Id. The glass Id is part of the state that define * a state of an object to associate shared widgets with. * * @param glassId String that represent the selected title for sharing a comment on currently selected newspaper * @param stateType info about the selected state type either page number or title of an article */ private void setGlassForState(String glassId, String stateType) { //present glassId on screen setContentInfoText(glassId, stateType); //start (or restart) the glass CGLayer layer = new CGLayer(CGFinals.DEFAULT, glassId, CGFinals.GLASS_TYPE_OBJECT, null, false); getCGApplication().startContentGlass(layer); //invalidate options menu in order to enable menu items getActivity().invalidateOptionsMenu(); //ask user for creating new comment askUserForCreate(); } /** * Set the selected context info * * @param barcode either page number or title of an article used as glass Id * @param stateType info about the selected state type either page number or title of an article */ private void setContentInfoText(String barcode, String stateType) { ((TextView) getActivity().findViewById(R.id.contextInfo)).setText("Sharing context: " + stateType + " " + StringUtil.defaultIfEmpty(barcode, "...")); } /** * Create text widget that wil be used as a comment to newspaper article. Articles are identified by their * title. */ private void createsSharedWidget() { try { getCGApplication().createAndShowNewWidget(AndroidCGFinals.BASIC_LIBRARY, WIDGET_TYPE, null, null); } catch (CGException e) { getCGApplication().popupError("Fail to create widget!", e); } } /** * Ask the user if want to create new comment for selected article. */ private void askUserForCreate() { AlertDialog.Builder builder = new AlertDialog.Builder(this); builder.setMessage("Create new widget?") .setCancelable(false) .setPositiveButton("Yes", new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int id) { dialog.dismiss(); createsSharedWidget(); } }) .setNegativeButton("No", new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int id) { dialog.dismiss(); } }); AlertDialog alert = builder.create(); alert.show(); } /** * @return the Id for setting options menu for this fragment */ @Override public int getOptionsMenuId() { return R.menu.my_object_glass_menu; } /** * Handle custom option menu events * * @param item the clicked menu * @return true if consumed, else false. */ public boolean onCustomOptionsItemSelected(MenuItem item) { int itemId = item.getItemId(); if (itemId == R.id.add_widget) { createsSharedWidget(); return true; } else if (itemId == R.id.set_sharing_context) { openSateWizard(); return true; } return false; } /** * Prepare the custom options menu for this glass * * @param menu menu * @return true */ @Override public boolean onPrepareCustomOptionsMenu(Menu menu) { boolean cgOn = getCGApplication().isContentGlassStarted(); boolean widgetsHidden = getCGApplication().isWidgetsHidden(); menu.getItem(1).setEnabled(cgOn); menu.getItem(2).setEnabled(cgOn); menu.getItem(3).setEnabled(!widgetsHidden && cgOn); menu.getItem(4).setEnabled(widgetsHidden && cgOn); return true; } /** * @return the menu Id for the context menu used by this glass */ @Override public int getContextMenuId() { return cg.android.client.R.menu.default_widget_context_menu; } /** * Invoked when item of widget's context menu is selected. * * @param item the clicked menu item * @return true if the operation for relating a product to barcode was selected. Else false */ @Override public boolean onCustomContextItemSelected(MenuItem item) { int itemId = item.getItemId(); return false; } }