This document is for developers that uses Java to make apps for Android. The docs target developers using Android Studio, Eclipse, or IntelliJ.
Java apps/games use the ouya-sdk.jar
library included in the ODK
downloadable from the Cortex developer portal.
Place the Xiaomi libraries in the following destinations:
-
assets/MiGameCenterSDKService.apk
-
libs/SDK_MIBOX_2.0.1.jar
Xiaomi's SDK requires several additional permissions in AndroidManifest.xml
in order to work.
<uses-permission android:name="com.xiaomi.sdk.permission.PAYMENT"/>
<uses-permission android:name="android.permission.GET_TASKS"/>
<uses-permission android:name="android.permission.READ_PHONE_STATE"/>
- Using
Java
, prepare aBundle
with additionalkey/value
pairs which are used to initialize theOuyaFacade
.
// Your developer id can be found in the Developer Portal
public static final String DEVELOPER_ID = "00000000-0000-0000-0000-000000000000";
// Both of these values will be emailed to you by the Cortex team after you've been
// selected by the Cortex team
public static final String XIAOMI_APP_ID = "0000000000000";
public static final String XIAOMI_APP_KEY = "000000000000000000";
// All product IDs that might be used
public static final String[] ALL_PRODUCT_IDS = new String[] {
"long_sword",
"sharp_axe",
"100_extra_lives"
}
// listener for initialization
private CancelIgnoringOuyaResponseListener<Bundle> mInitCompletedListener = null;
@Override
protected void onCreate(Bundle savedInstanceState) {
Bundle developerInfo = new Bundle();
// "tv.ouya.developer_id"
developerInfo.putString(OuyaFacade.OUYA_DEVELOPER_ID, DEVELOPER_ID);
developerInfo.putByteArray(OuyaFacade.OUYA_DEVELOPER_PUBLIC_KEY, loadApplicationKey());
// We must tell the OuyaFacade that we can use the Xiaomi market for purchases.
// "com.xiaomi.app_id"
developerInfo.putString(OuyaFacade.XIAOMI_APPLICATION_ID, XIAOMI_APP_ID);
// "com.xiaomi.app_key"
developerInfo.putString(OuyaFacade.XIAOMI_APPLICATION_KEY, XIAOMI_APP_KEY);
// "tv.ouya.product_id_list"
developerInfo.putStringArray(OuyaFacade.OUYA_PRODUCT_ID_LIST, ALL_PRODUCT_IDS);
// initialization listener callbacks
mInitCompletedListener = new CancelIgnoringOuyaResponseListener<Bundle>() {
@Override
public void onSuccess(Bundle info) {
Log.i(TAG, "InitCompletedListener: onSuccess");
}
@Override
public void onFailure(int errorCode, String errorMessage, Bundle optionalData) {
Log.i(TAG, "InitCompletedListener: onFailure errorCode="+errorCode+" errorMessage="+errorMessage);
}
};
// Get the OUYA Facade instance
OuyaFacade ouyaFacade = OuyaFacade.getInstance();
// Register the init callback
ouyaFacade.registerInitCompletedListener(mInitCompletedListener);
// Pass the Activity for the context parameter when initializing the OuyaFacade.
// Passing the application context for the context paramter will not work on Xiaomi.
ouyaFacade.init(this, developerInfo);
super.onCreate(savedInstanceState);
}
- The screensaver should be disabled while your game is running. Invoke
View.setKeepScreenOn(true)
to disable the screensaver. Here is a common scenario of an activity loading the layout and using the content View to disable the screensaver.
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
View content = (View)findViewById(android.R.id.content);
if (null != content) {
// Disable screensaver
content.setKeepScreenOn(true);
}
}
The ouya_xiaomi_icon.png
284x160 icon should be places in res/drawable-xhdpi/ouya_xiaomi_icon.png
.
Java uses standard Android localization for localized string resources.
Strings can be placed in designated folders which are automatically selected using the current language.
-
res/values/strings.xml
(Default) -
res/values-de/strings.xml
(Dutch) -
res/values-en/strings.xml
(English) -
res/values-es/strings.xml
(Spanish) -
res/values-fr/strings.xml
(French) -
res/values-it/strings.xml
(Italian) -
res/values-zh-rCN/strings.xml
(Simplified Chinese)
The virtual controller example exercises the new OUYA-Everywhere input. The button names and images are now accessible from the API. And the virtual controller buttons highlight with multiple controllers for supported controllers. TextViews display the incoming keycode values and the remapped keycodes after the OuyaInputMapper has remapped the input.
The AndroidVirtualController
source code can be found within the ouya-sdk-examples.
The AndroidVirtualController
example uses the OuyaController.getButtonData
API to display device specific controller images.
The project has a small number of key files that makes the example work.
Specifies the target Android API level of 16 and the starting activity.
The starting and only activity in the project responsible for the logic to display text, buttons, and toggle image visibility based on input.
A debug class for displaying keycode and axis value input in human-readable format in the logcat.
The Java library released through the ODK in the developer portal which provides access to the OUYA SDK.
The Android layout that specifies the position and content that displays text and images.
The drawable resources hold the icons and controller images used in the example.
The MainActivity extends the OuyaActivity from the ouya-sdk.jar for the easiest way to add OUYA-Everywhere input.
public class MainActivity extends OuyaActivity {
Accepts the ImageView that will display the button image and the keyCode id for the corresponding button image.
private void setDrawable(ImageView imageView, int keyCode) {
ButtonData data = OuyaController.getButtonData(keyCode);
if (null != data) {
imageView.setImageDrawable(data.buttonDrawable);
}
}
The ButtonData
image can also be converted to a Bitmap
which can be used in other cases.
private Bitmap getButtonDataBitmap(int button) {
OuyaController.ButtonData buttonData = OuyaController.getButtonData(button);
if (null == buttonData)
{
return null;
}
BitmapDrawable drawable = (BitmapDrawable)buttonData.buttonDrawable;
if (null == drawable)
{
return null;
}
Bitmap bitmap = drawable.getBitmap();
return bitmap;
}
Loads the layout and gets the references to the ImageView controls that will handle toggling button visibility when toggled.
@Override
protected void onCreate(Bundle savedInstanceState) {
setContentView(R.layout.activity_main);
super.onCreate(savedInstanceState);
txtSystem = (TextView)findViewById(R.id.txtSystem);
txtController = (TextView)findViewById(R.id.txtController);
imgButtonMenu = (ImageView)findViewById(R.id.imgButtonMenu);
txtKeyCode = (TextView)findViewById(R.id.txtKeyCode);
imgControllerO = (ImageView)findViewById(R.id.imgControllerO);
imgControllerU = (ImageView)findViewById(R.id.imgControllerU);
imgControllerY = (ImageView)findViewById(R.id.imgControllerY);
imgControllerA = (ImageView)findViewById(R.id.imgControllerA);
imgControllerL1 = (ImageView)findViewById(R.id.imgControllerL1);
imgControllerL2 = (ImageView)findViewById(R.id.imgControllerL2);
imgControllerL3 = (ImageView)findViewById(R.id.imgControllerl3);
imgControllerR1 = (ImageView)findViewById(R.id.imgControllerR1);
imgControllerR2 = (ImageView)findViewById(R.id.imgControllerR2);
imgControllerR3 = (ImageView)findViewById(R.id.imgControllerR3);
imgControllerDpadDown = (ImageView)findViewById(R.id.imgControllerDpadDown);
imgControllerDpadLeft = (ImageView)findViewById(R.id.imgControllerDpadLeft);
imgControllerDpadRight = (ImageView)findViewById(R.id.imgControllerDpadRight);
imgControllerDpadUp = (ImageView)findViewById(R.id.imgControllerDpadUp);
imgControllerMenu = (ImageView)findViewById(R.id.imgControllerMenu);
imgButtonA = (ImageView)findViewById(R.id.imgButtonA);
imgDpadDown = (ImageView)findViewById(R.id.imgDpadDown);
imgDpadLeft = (ImageView)findViewById(R.id.imgDpadLeft);
imgDpadRight = (ImageView)findViewById(R.id.imgDpadRight);
imgDpadUp = (ImageView)findViewById(R.id.imgDpadUp);
imgLeftStick = (ImageView)findViewById(R.id.imgLeftStick);
imgLeftBumper = (ImageView)findViewById(R.id.imgLeftBumper);
imgLeftTrigger = (ImageView)findViewById(R.id.imgLeftTrigger);
imgButtonO = (ImageView)findViewById(R.id.imgButtonO);
imgRightStick = (ImageView)findViewById(R.id.imgRightStick);
imgRightBumper = (ImageView)findViewById(R.id.imgRightBumper);
imgRightTrigger = (ImageView)findViewById(R.id.imgRightTrigger);
imgLeftThumb = (ImageView)findViewById(R.id.imgLeftThumb);
imgRightThumb = (ImageView)findViewById(R.id.imgRightThumb);
imgButtonU = (ImageView)findViewById(R.id.imgButtonU);
imgButtonY = (ImageView)findViewById(R.id.imgButtonY);
Input can be event based, or spawn a thread to set visibility on an interval. The menu button is pressed and the visibility needs to be cleared after an interval rather than waiting for an event to clear it.
// spawn thread to toggle menu button
Thread timer = new Thread()
{
public void run()
{
while (mWaitToExit)
{
if (mMenuDetected != 0 &&
mMenuDetected < System.nanoTime())
{
mMenuDetected = 0;
Runnable runnable = new Runnable()
{
public void run()
{
imgButtonMenu.setVisibility(View.INVISIBLE);
}
};
runOnUiThread(runnable);
}
try
{
Thread.sleep(50);
}
catch (InterruptedException e)
{
}
}
}
};
timer.start();
Initialization displays build information and sets the drawable button images from the new api.
@Override
protected void onStart() {
super.onStart();
txtSystem.setText("Brand=" + android.os.Build.BRAND + " Model=" + android.os.Build.MODEL + " Version=" + android.os.Build.VERSION.SDK_INT);
setDrawable(imgControllerO, OuyaController.BUTTON_O);
setDrawable(imgControllerU, OuyaController.BUTTON_U);
setDrawable(imgControllerY, OuyaController.BUTTON_Y);
setDrawable(imgControllerA, OuyaController.BUTTON_A);
setDrawable(imgControllerL1, OuyaController.BUTTON_L1);
setDrawable(imgControllerL2, OuyaController.BUTTON_L2);
setDrawable(imgControllerL3, OuyaController.BUTTON_L3);
setDrawable(imgControllerR1, OuyaController.BUTTON_R1);
setDrawable(imgControllerR2, OuyaController.BUTTON_R2);
setDrawable(imgControllerR3, OuyaController.BUTTON_R3);
setDrawable(imgControllerDpadDown, OuyaController.BUTTON_DPAD_DOWN);
setDrawable(imgControllerDpadLeft, OuyaController.BUTTON_DPAD_LEFT);
setDrawable(imgControllerDpadRight, OuyaController.BUTTON_DPAD_RIGHT);
setDrawable(imgControllerDpadUp, OuyaController.BUTTON_DPAD_UP);
setDrawable(imgControllerMenu, OuyaController.BUTTON_MENU);
}
The axis events arrive with onGenericMotionEvent after the OUYA-Everywhere has remapped the input.
@Override
public boolean onGenericMotionEvent(MotionEvent motionEvent) {
float lsX = motionEvent.getAxisValue(OuyaController.AXIS_LS_X);
float lsY = motionEvent.getAxisValue(OuyaController.AXIS_LS_Y);
float rsX = motionEvent.getAxisValue(OuyaController.AXIS_RS_X);
float rsY = motionEvent.getAxisValue(OuyaController.AXIS_RS_Y);
float l2 = motionEvent.getAxisValue(OuyaController.AXIS_L2);
float r2 = motionEvent.getAxisValue(OuyaController.AXIS_R2);
When a button is pressed the corresponding image is highlighted. When the system button is detected, the image is highlighted for an interval.
@Override
public boolean onKeyDown(int keyCode, KeyEvent keyEvent) {
switch (keyCode)
{
case OuyaController.BUTTON_L1:
imgLeftBumper.setVisibility(View.VISIBLE);
return true;
case OuyaController.BUTTON_L3:
imgLeftStick.setVisibility(View.INVISIBLE);
imgLeftThumb.setVisibility(View.VISIBLE);
return true;
case OuyaController.BUTTON_R1:
imgRightBumper.setVisibility(View.VISIBLE);
return true;
case OuyaController.BUTTON_R3:
imgRightStick.setVisibility(View.INVISIBLE);
imgRightThumb.setVisibility(View.VISIBLE);
return true;
case OuyaController.BUTTON_O:
imgButtonO.setVisibility(View.VISIBLE);
return true;
case OuyaController.BUTTON_U:
imgButtonU.setVisibility(View.VISIBLE);
return true;
case OuyaController.BUTTON_Y:
imgButtonY.setVisibility(View.VISIBLE);
return true;
case OuyaController.BUTTON_A:
imgButtonA.setVisibility(View.VISIBLE);
return true;
case OuyaController.BUTTON_DPAD_DOWN:
imgDpadDown.setVisibility(View.VISIBLE);
return true;
case OuyaController.BUTTON_DPAD_LEFT:
imgDpadLeft.setVisibility(View.VISIBLE);
return true;
case OuyaController.BUTTON_DPAD_RIGHT:
imgDpadRight.setVisibility(View.VISIBLE);
return true;
case OuyaController.BUTTON_DPAD_UP:
imgDpadUp.setVisibility(View.VISIBLE);
return true;
case OuyaController.BUTTON_MENU:
imgButtonMenu.setVisibility(View.VISIBLE);
mMenuDetected = System.nanoTime() + 1000000000;
return true;
}
return true;
}
When the button is no longer pressed the ImageView for the highlighted button is hidden.
@Override
public boolean onKeyUp(int keyCode, KeyEvent keyEvent) {
switch (keyCode)
{
case OuyaController.BUTTON_L1:
imgLeftBumper.setVisibility(View.INVISIBLE);
return true;
case OuyaController.BUTTON_L3:
imgLeftStick.setVisibility(View.VISIBLE);
imgLeftThumb.setVisibility(View.INVISIBLE);
return true;
case OuyaController.BUTTON_R1:
imgRightBumper.setVisibility(View.INVISIBLE);
return true;
case OuyaController.BUTTON_R3:
imgRightStick.setVisibility(View.VISIBLE);
imgRightThumb.setVisibility(View.INVISIBLE);
return true;
case OuyaController.BUTTON_O:
imgButtonO.setVisibility(View.INVISIBLE);
return true;
case OuyaController.BUTTON_U:
imgButtonU.setVisibility(View.INVISIBLE);
return true;
case OuyaController.BUTTON_Y:
imgButtonY.setVisibility(View.INVISIBLE);
return true;
case OuyaController.BUTTON_A:
imgButtonA.setVisibility(View.INVISIBLE);
return true;
case OuyaController.BUTTON_DPAD_DOWN:
imgDpadDown.setVisibility(View.INVISIBLE);
return true;
case OuyaController.BUTTON_DPAD_LEFT:
imgDpadLeft.setVisibility(View.INVISIBLE);
return true;
case OuyaController.BUTTON_DPAD_RIGHT:
imgDpadRight.setVisibility(View.INVISIBLE);
return true;
case OuyaController.BUTTON_DPAD_UP:
imgDpadUp.setVisibility(View.INVISIBLE);
return true;
case OuyaController.BUTTON_MENU:
//wait 1 second
return true;
}
return true;
}
The RazerVirtualController
example includes virtual controller images and mappings for the Razer Serval Controller. The RazerVirtualController
source code can be found within the ouya-sdk-examples.
The ODK
download in the developer portal includes an in-app-purchase sample application.
The OuyaFacade
has several listeners for in-app-purchase
callbacks.
public class CustomActivity extends Activity
{
// The tag for log messages
private static final String TAG = ActivityCommon.class.getSimpleName();
// Your game talks to the OuyaFacade, which hides all the mechanics of doing an in-app purchase.
private OuyaFacade mOuyaFacade = null;
// listener for fetching gamer info
private CancelIgnoringOuyaResponseListener<GamerInfo> mRequestGamerInfoListener = null;
// listener for getting products
private CancelIgnoringOuyaResponseListener<List<Product>> mRequestProductsListener = null;
// listener for requesting purchase
private OuyaResponseListener<PurchaseResult> mRequestPurchaseListener = null;
// listener for getting receipts
private OuyaResponseListener<Collection<Receipt>> mRequestReceiptsListener = null;
}
Implement the listeners to pass to the OuyaFacade
IAP methods.
mRequestGamerInfoListener = new CancelIgnoringOuyaResponseListener<GamerInfo>() {
@Override
public void onSuccess(GamerInfo info) {
Log.d(TAG, "RequestGamerInfoListener: onSuccess");
}
@Override
public void onFailure(int errorCode, String errorMessage, Bundle optionalData) {
Log.d(TAG, "RequestGamerInfoListener: onFailure errorCode="+errorCode+" errorMessage="+errorMessage);
}
};
mRequestProductsListener = new CancelIgnoringOuyaResponseListener<List<Product>>() {
@Override
public void onSuccess(final List<Product> products) {
Log.d(TAG, "RequestProductsListener: onSuccess received "+products.size()+" products");
}
@Override
public void onFailure(int errorCode, String errorMessage, Bundle optionalData) {
Log.d(TAG, "sRequestProductsListener: onFailure errorCode="+errorCode+" errorMessage="+errorMessage);
}
};
mRequestPurchaseListener = new OuyaResponseListener<PurchaseResult>() {
@Override
public void onSuccess(PurchaseResult result) {
Log.d(TAG, "RequestPurchaseListener: onSuccess");
}
@Override
public void onFailure(int errorCode, String errorMessage, Bundle optionalData) {
Log.d(TAG, "RequestPurchaseListener: onFailure errorCode="+errorCode+" errorMessage="+errorMessage);
}
@Override
public void onCancel() {
Log.d(TAG, "RequestPurchaseListener: onCancel");
}
};
mRequestReceiptsListener = new OuyaResponseListener<Collection<Receipt>>() {
@Override
public void onSuccess(Collection<Receipt> receipts) {
Log.d(TAG, "RequestReceiptsListener: onSuccess received "+receipts.size() + " receipts");
}
@Override
public void onFailure(int errorCode, String errorMessage, Bundle optionalData) {
Log.d(TAG, "RequestReceiptsListener: onFailure: errorCode="+errorCode+" errorMessage="+errorMessage);
}
@Override
public void onCancel() {
Log.d(TAG, "RequestReceiptsListener: onCancel");
}
};
- Be sure to check out the in-app-purchasing document for additional
IAP
details.