Initial commit

This commit is contained in:
alexpete
2021-03-05 11:26:34 -08:00
commit a10351f38d
27091 changed files with 5521199 additions and 0 deletions
@@ -0,0 +1,28 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
package com.amazon.lumberyard;
import android.app.Activity;
import android.content.Intent;
////////////////////////////////////////////////////////////////
public abstract class ActivityResultsListener
{
public ActivityResultsListener(Activity activity)
{
((LumberyardActivity)activity).RegisterActivityResultsListener(this);
}
public abstract boolean ProcessActivityResult(int requestCode, int resultCode, Intent data);
}
@@ -0,0 +1,31 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
package com.amazon.lumberyard;
import android.app.ActivityManager;
import android.content.Context;
////////////////////////////////////////////////////////////////////////////////////////////////////
public class AndroidDeviceManager
{
public static Context context;
public static final float bytesInGB = (1024.0f * 1024.0f * 1024.0f);
public static float GetDeviceRamInGB()
{
ActivityManager actManager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
ActivityManager.MemoryInfo memInfo = new ActivityManager.MemoryInfo();
actManager.getMemoryInfo(memInfo);
float totalMemory = memInfo.totalMem / bytesInGB;
return totalMemory;
}
}
@@ -0,0 +1,506 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
package com.amazon.lumberyard;
import android.app.Activity;
import android.app.AlertDialog;
import android.app.NativeActivity;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.res.AssetManager;
import android.content.res.Resources;
import android.graphics.Point;
import android.Manifest;
import android.media.AudioManager;
import android.os.Bundle;
import android.os.Looper;
import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat;
import android.util.Log;
import android.view.Display;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.View;
import android.view.WindowManager;
import android.widget.LinearLayout;
import android.widget.PopupWindow;
import android.widget.TextView;
import java.io.File;
import java.io.InputStream;
import java.io.IOException;
import java.lang.InterruptedException;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import com.amazon.lumberyard.io.APKHandler;
import com.amazon.lumberyard.io.obb.ObbDownloaderActivity;
////////////////////////////////////////////////////////////////
public class LumberyardActivity extends NativeActivity
{
////////////////////////////////////////////////////////////////
// Native methods
public static native void nativeOnRequestPermissionsResult(boolean granted);
////////////////////////////////////////////////////////////////
@Override
public void onBackPressed()
{
// by doing nothing here will prevent the activity from being exited (the default behaviour)
}
////////////////////////////////////////////////////////////////
// called from the native to get the application package name
// e.g. com.lumberyard.samples for SamplesProject
public String GetPackageName()
{
return getApplicationContext().getPackageName();
}
////////////////////////////////////////////////////////////////
// called from the native to get the app version code
// android:versionCode in the AndroidManifest.xml.
public int GetAppVersionCode()
{
try
{
PackageInfo pInfo = getPackageManager().getPackageInfo(getPackageName(), 0);
return pInfo.versionCode;
}
catch (NameNotFoundException e)
{
return 0;
}
}
////////////////////////////////////////////////////////////////
// called from the native code to show the Java splash screen
public void ShowSplashScreen()
{
Log.d(TAG, "ShowSplashScreen called");
this.runOnUiThread(new Runnable() {
@Override
public void run()
{
if (!m_splashShowing)
{
ShowSplashScreenImpl();
}
else
{
Log.d(TAG, "The splash screen is already showing");
}
}
});
}
////////////////////////////////////////////////////////////////
// called from the native code to dismiss the Java splash screen
public void DismissSplashScreen()
{
Log.d(TAG, "DismissSplashScreen called");
if (m_splashShowing)
{
this.runOnUiThread(new Runnable() {
@Override
public void run()
{
if (m_slashWindow != null)
{
Log.d(TAG, "Dismissing the splash screen");
m_slashWindow.dismiss();
m_slashWindow = null;
}
else
{
Log.d(TAG, "There is no splash screen to dismiss");
}
}
});
m_splashShowing = false;
}
}
////////////////////////////////////////////////////////////////
public void RegisterActivityResultsListener(ActivityResultsListener listener)
{
m_activityResultsListeners.add(listener);
}
////////////////////////////////////////////////////////////////
public void UnregisterActivityResultsListener(ActivityResultsListener listener)
{
m_activityResultsListeners.remove(listener);
}
////////////////////////////////////////////////////////////////
// Starts the download of the obb files and waits (block) until the activity finishes.
// Return true in case of success, false otherwise.
public boolean DownloadObb()
{
Intent downloadIntent = new Intent(this, ObbDownloaderActivity.class);
ActivityResult result = new ActivityResult();
if (launchActivity(downloadIntent, DOWNLOAD_OBB_REQUEST, true, result))
{
return result.m_result == Activity.RESULT_OK;
}
return false;
}
////////////////////////////////////////////////////////////////
// Returns the value of a boolean resource.
public boolean GetBooleanResource(String resourceName)
{
Resources resources = this.getResources();
int resourceId = resources.getIdentifier(resourceName, "bool", this.getPackageName());
return resources.getBoolean(resourceId);
}
////////////////////////////////////////////////////////////////
// Request permissions at runtime.
public void RequestPermission(final String permission, final String rationale)
{
if (ContextCompat.checkSelfPermission(this, permission) != PackageManager.PERMISSION_GRANTED)
{
Random rand = new Random();
m_runtimePermissionRequestCode = rand.nextInt(500);
final int requestCode = m_runtimePermissionRequestCode;
if (ActivityCompat.shouldShowRequestPermissionRationale(this, permission))
{
final LumberyardActivity activity = this;
Runnable uiDialog = new Runnable()
{
public void run()
{
AlertDialog.Builder builder = new AlertDialog.Builder(activity);
TextView textView = new TextView(activity);
String title = new String("Reason for requesting " + permission);
textView.setText(title + "\n" + rationale);
builder.setCustomTitle(textView);
builder.setItems(new String[]{"OK"}, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int index) {
ActivityCompat.requestPermissions(activity, new String[]{permission}, requestCode);
}
});
AlertDialog dialog = builder.create();
dialog.show();
}
};
activity.runOnUiThread(uiDialog);
}
else
{
ActivityCompat.requestPermissions(this, new String[]{permission}, requestCode);
}
}
else
{
nativeOnRequestPermissionsResult(true);
}
}
// ----
////////////////////////////////////////////////////////////////
@Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
if (GetBooleanResource("enable_keep_screen_on"))
{
getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
}
ProcessImmersiveModeSetting();
APKHandler.SetAssetManager(getAssets());
AndroidDeviceManager.context = this;
boolean useMainObb = GetBooleanResource("use_main_obb");
boolean usePatchObb = GetBooleanResource("use_patch_obb");
if (IsBootstrapInAPK() && (useMainObb || usePatchObb))
{
Log.d(TAG, "Using OBB expansion files for game assets");
File obbRootPath = getApplicationContext().getObbDir();
String packageName = GetPackageName();
int appVersionCode = GetAppVersionCode();
String mainObbFilePath = String.format("%s/main.%d.%s.obb", obbRootPath, appVersionCode, packageName);
String patchObbFilePath = String.format("%s/patch.%d.%s.obb", obbRootPath, appVersionCode, packageName);
File mainObbFile = new File(mainObbFilePath);
File patchObbFile = new File(patchObbFilePath);
boolean needToDownload = ( (useMainObb && !mainObbFile.canRead())
|| (usePatchObb && !patchObbFile.canRead()));
if (needToDownload)
{
Log.d(TAG, "Attempting to download the OBB expansion files");
boolean downloadResult = DownloadObb();
if (!downloadResult)
{
Log.e(TAG, "****************************************************************");
Log.e(TAG, "Failed to download the OBB expansion file. Exiting...");
Log.e(TAG, "****************************************************************");
finish();
}
}
}
else
{
Log.d(TAG, "Assets already on the device, not using the OBB expansion files.");
}
// ensure we use the music media stream
setVolumeControlStream(AudioManager.STREAM_MUSIC);
}
////////////////////////////////////////////////////////////////
@Override
protected void onDestroy()
{
// Signal any thread that is waiting for the result of an activity
for(ActivityResult result : m_waitingResultList)
{
synchronized(result)
{
result.m_isRunning = false;
result.notifyAll();
}
}
// Ideally we should be calling super.onDestroy() here and going through the "graceful" shutdown process,
// however some deadlock(s) happen in the static de-allocation preventing the process to naturally exit.
// On phones, and most tablets, this doesn't happen because the process is terminated by the system but
// while running in Samsung DEX mode it's kept alive until it seemingly exits naturally. Manually killing
// the process in the onDestroy is probably the best compromise until the graceful exit is fixed with LY-70527
android.os.Process.killProcess(android.os.Process.myPid());
}
////////////////////////////////////////////////////////////////
@Override
public void onWindowFocusChanged(boolean hasFocus)
{
super.onWindowFocusChanged(hasFocus);
if (hasFocus)
{
ProcessImmersiveModeSetting();
}
}
////////////////////////////////////////////////////////////////
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data)
{
for (ActivityResultsListener listener : m_activityResultsListeners)
{
listener.ProcessActivityResult(requestCode, resultCode, data);
}
}
////////////////////////////////////////////////////////////////
@Override
public void onRequestPermissionsResult(int requestCode, String permissions[], int[] grantResults)
{
if (requestCode == m_runtimePermissionRequestCode)
{
if (grantResults.length > 0)
{
if (grantResults[0] == PackageManager.PERMISSION_GRANTED)
{
Log.d(TAG, "Permission Granted");
nativeOnRequestPermissionsResult(true);
}
else
{
Log.d(TAG, "Permission Denied");
nativeOnRequestPermissionsResult(false);
}
}
else
{
// Request was cancelled
nativeOnRequestPermissionsResult(false);
}
m_runtimePermissionRequestCode = -1;
}
}
// ----
////////////////////////////////////////////////////////////////
private boolean launchActivity(Intent intent, final int activityRequestCode, boolean waitForResult, final ActivityResult result)
{
if (waitForResult)
{
if (Looper.myLooper() == Looper.getMainLooper())
{
// Can't block if we are on the UI Thread.
return false;
}
ActivityResultsListener activityListener = new ActivityResultsListener(this)
{
@Override
public boolean ProcessActivityResult(int requestCode, int resultCode, Intent data)
{
if (requestCode == activityRequestCode)
{
synchronized(result)
{
result.m_result = resultCode;
result.m_isRunning = false;
result.notify();
}
return true;
}
return false;
}
};
this.RegisterActivityResultsListener(activityListener);
m_waitingResultList.add(result);
result.m_isRunning = true;
startActivityForResult(intent, activityRequestCode);
synchronized(result)
{
// Wait until the downloader activity finishes.
boolean ret = true;
while (result.m_isRunning)
{
try
{
result.wait();
}
catch(InterruptedException exception)
{
ret = false;
}
}
this.UnregisterActivityResultsListener(activityListener);
m_waitingResultList.remove(result);
return ret;
}
}
else
{
startActivityForResult(intent, activityRequestCode);
return true;
}
}
////////////////////////////////////////////////////////////////
private boolean IsBootstrapInAPK()
{
try
{
InputStream bootstrap = getAssets().open("bootstrap.cfg", AssetManager.ACCESS_UNKNOWN);
bootstrap.close();
return true;
}
catch (IOException exception)
{
return false;
}
}
////////////////////////////////////////////////////////////////
private void ShowSplashScreenImpl()
{
Log.d(TAG, "Showing the Splash Screen");
// load the splash screen view
Resources resources = getResources();
int layoutId = resources.getIdentifier("splash_screen", "layout", getPackageName());
LayoutInflater factory = LayoutInflater.from(this);
View splashView = factory.inflate(layoutId, null);
// get the resolution of the display
Display display = getWindowManager().getDefaultDisplay();
Point size = new Point();
display.getSize(size);
// create the popup with the splash screen layout. this is because the standard
// view hierarchy for Android apps doesn't exist when using the NativeActivity
m_slashWindow = new PopupWindow(splashView, size.x, size.y);
m_slashWindow.setClippingEnabled(false);
// add a dummy layout to the main view for the splash popup window
LinearLayout mainLayout = new LinearLayout(this);
setContentView(mainLayout);
// show the splash window
m_slashWindow.showAtLocation(mainLayout, Gravity.CENTER, 0, 0);
m_slashWindow.update();
m_splashShowing = true;
}
////////////////////////////////////////////////////////////////
private void ProcessImmersiveModeSetting()
{
int systemUiFlags = (View.SYSTEM_UI_FLAG_LAYOUT_STABLE | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN);
if (!GetBooleanResource("disable_immersive_mode"))
{
systemUiFlags |= (View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION |
View.SYSTEM_UI_FLAG_HIDE_NAVIGATION |
View.SYSTEM_UI_FLAG_FULLSCREEN |
View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY);
}
getWindow().getDecorView().setSystemUiVisibility(systemUiFlags);
}
// ----
////////////////////////////////////////////////////////////////
private class ActivityResult
{
public int m_result;
public boolean m_isRunning;
}
// ----
private static final int DOWNLOAD_OBB_REQUEST = 1337;
private static final String TAG = "LMBR";
private PopupWindow m_slashWindow = null;
private boolean m_splashShowing = false;
private int m_runtimePermissionRequestCode = -1;
private List<ActivityResultsListener> m_activityResultsListeners = new ArrayList<ActivityResultsListener>();
private List<ActivityResult> m_waitingResultList = new ArrayList<ActivityResult>();
}
@@ -0,0 +1,61 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
package com.amazon.lumberyard.NativeUI;
import android.app.Activity;
import android.app.AlertDialog;
import android.content.DialogInterface;
import android.util.Log;
import android.widget.TextView;
import java.util.ArrayList;
import java.util.concurrent.atomic.AtomicReference;
public class LumberyardNativeUI
{
public static void DisplayDialog(final Activity activity, final String title, final String message, final String[] options)
{
Log.d("LMBR", "DisplayDialog called");
userSelection = new AtomicReference<String>("");
userSelection.set("");
Runnable uiDialog = new Runnable()
{
public void run()
{
AlertDialog.Builder builder = new AlertDialog.Builder(activity);
TextView textView = new TextView(activity);
textView.setText(title + "\n" + message);
builder.setCustomTitle(textView);
builder.setItems(options, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int index) {
userSelection.set(options[index]);
Log.d("LMBR", "Selected option: " + userSelection.get());
}
});
AlertDialog dialog = builder.create();
dialog.show();
}
};
activity.runOnUiThread(uiDialog);
}
public static String GetUserSelection()
{
return userSelection.get();
}
public static AtomicReference<String> userSelection;
}
@@ -0,0 +1,196 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
package com.amazon.lumberyard.input;
import android.app.Activity;
import android.content.Context;
import android.util.Log;
import android.view.KeyEvent;
import android.view.ViewGroup;
import android.view.inputmethod.InputMethodManager;
import android.view.View;
////////////////////////////////////////////////////////////////
public class KeyboardHandler
{
// ----
// KeyboardHandler (public)
// ----
public static native void SendUnicodeText(String unicodeText);
////////////////////////////////////////////////////////////////
public KeyboardHandler(Activity activity)
{
m_activity = activity;
m_inputManager = (InputMethodManager)m_activity.getSystemService(Context.INPUT_METHOD_SERVICE);
}
////////////////////////////////////////////////////////////////
public void ShowTextInput()
{
m_activity.runOnUiThread(new Runnable() {
@Override
public void run()
{
if (m_textView == null)
{
m_textView = new DummyTextView(m_activity);
ViewGroup viewGroup = (ViewGroup)GetView();
viewGroup.addView(m_textView);
}
m_textView.Show();
m_inputManager.showSoftInput(m_textView, 0);
}
});
}
////////////////////////////////////////////////////////////////
public void HideTextInput()
{
if (m_textView != null)
{
m_activity.runOnUiThread(new Runnable() {
@Override
public void run()
{
m_inputManager.hideSoftInputFromWindow(m_textView.getWindowToken(), 0);
m_textView.Hide();
}
});
}
}
////////////////////////////////////////////////////////////////
public boolean IsShowing()
{
if (m_textView != null)
{
return m_textView.IsShowing();
}
return false;
}
// ----
private class DummyTextView extends View
{
////////////////////////////////////////////////////////////////
public DummyTextView(Context context)
{
super(context);
setFocusableInTouchMode(true);
setFocusable(true);
m_isShowing = false;
}
////////////////////////////////////////////////////////////////
@Override
public boolean onKeyDown(int keyCode, KeyEvent event)
{
if (event.isPrintingKey())
{
int unicode = event.getUnicodeChar();
String character = String.valueOf((char)unicode);
Log.d(s_tag, String.format("OnKeyDown - Unicode: %s - Printed character: %s", unicode, character));
SendUnicodeText(character);
}
return super.onKeyDown(keyCode, event);
}
////////////////////////////////////////////////////////////////
@Override
public boolean onKeyMultiple(int keyCode, int count, KeyEvent event)
{
if(event.getAction() == KeyEvent.ACTION_MULTIPLE && keyCode == KeyEvent.KEYCODE_UNKNOWN)
{
String text = event.getCharacters();
Log.d(s_tag, String.format("onKeyMultiple - Text: %s", text));
if (text != null)
{
SendUnicodeText(text);
}
}
return super.onKeyMultiple(keyCode, count, event);
}
////////////////////////////////////////////////////////////////
@Override
public boolean onKeyPreIme(int keyCode, KeyEvent event)
{
if (keyCode == KeyEvent.KEYCODE_BACK)
{
Hide();
}
return super.onKeyPreIme(keyCode, event);
}
////////////////////////////////////////////////////////////////
public boolean IsShowing()
{
return m_isShowing;
}
////////////////////////////////////////////////////////////////
public void Show()
{
m_windowFlags = GetView().getSystemUiVisibility();
setVisibility(View.VISIBLE);
requestFocus();
m_isShowing = true;
}
////////////////////////////////////////////////////////////////
public void Hide()
{
setVisibility(View.GONE);
m_isShowing = false;
GetView().setSystemUiVisibility(m_windowFlags);
}
// ----
private boolean m_isShowing;
private int m_windowFlags;
}
// ----
////////////////////////////////////////////////////////////////
private View GetView()
{
return m_activity.getWindow().getDecorView();
}
// ----
private static final String s_tag = "KeyboardHandler";
private Activity m_activity;
private InputMethodManager m_inputManager;
private DummyTextView m_textView;
}
@@ -0,0 +1,429 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
package com.amazon.lumberyard.input;
import android.app.Activity;
import android.content.Context;
import android.hardware.Sensor;
import android.hardware.SensorEvent;
import android.hardware.SensorEventListener;
import android.hardware.SensorManager;
import android.view.Display;
import android.view.MotionEvent;
import android.view.Surface;
import android.view.SurfaceView;
import android.view.View;
import android.view.WindowManager;
import java.lang.Math;
////////////////////////////////////////////////////////////////////////////////////////////////////
public class MotionSensorManager implements SensorEventListener
{
private class MotionSensorData
{
private class SensorData3D
{
private float x;
private float y;
private float z;
private boolean updated;
}
private class SensorData4D
{
private float x;
private float y;
private float z;
private float w;
private boolean updated;
}
private SensorData3D accelerationRaw = new SensorData3D();
private SensorData3D accelerationUser = new SensorData3D();
private SensorData3D accelerationGravity = new SensorData3D();
private SensorData3D rotationRateRaw = new SensorData3D();
private SensorData3D rotationRateUnbiased = new SensorData3D();
private SensorData3D magneticFieldRaw = new SensorData3D();
private SensorData3D magneticFieldUnbiased = new SensorData3D();
private SensorData4D orientation = new SensorData4D();
}
private static final float METRES_PER_SECOND_SQUARED_TO_GFORCE = -1.0f / SensorManager.GRAVITY_EARTH;
private static final int MOTION_SENSOR_DATA_PACKED_LENGTH = 34;
private MotionSensorData m_motionSensorData = new MotionSensorData();
private float[] m_motionSensorDataPacked = new float[MOTION_SENSOR_DATA_PACKED_LENGTH];
private SensorManager m_sensorManager = null;
private Display m_defaultDisplay = null;
private float m_orientationAdjustmentRadiansZ = 0.0f;
private int m_orientationSensorToUse = Sensor.TYPE_GAME_ROTATION_VECTOR;
public MotionSensorManager(Activity activity)
{
m_sensorManager = (SensorManager)activity.getSystemService(Context.SENSOR_SERVICE);
m_defaultDisplay = ((WindowManager)activity.getSystemService(Context.WINDOW_SERVICE)).getDefaultDisplay();
// If the game rotation vector is not available, default to the regular rotation vector.
if ((m_sensorManager != null) && (m_sensorManager.getDefaultSensor(m_orientationSensorToUse) == null))
{
m_orientationSensorToUse = Sensor.TYPE_ROTATION_VECTOR;
}
}
// Called when a motion sensor's accuracy changes
@Override
public void onAccuracyChanged(Sensor sensor, int accuracy)
{
}
// Called when a motion sensor event is dispatched
@Override
public void onSensorChanged(SensorEvent event)
{
int currentDisplayRotation = m_defaultDisplay != null ?
m_defaultDisplay.getRotation() :
Surface.ROTATION_0;
Sensor sensor = event.sensor;
switch (sensor.getType())
{
case Sensor.TYPE_ACCELEROMETER:
{
// Convert to the same unit of measurement as returned natively by iOS,
// which is (arguably) more useful to use directly for game development.
AlignWithDisplay(m_motionSensorData.accelerationRaw,
event.values[0] * METRES_PER_SECOND_SQUARED_TO_GFORCE,
event.values[1] * METRES_PER_SECOND_SQUARED_TO_GFORCE,
event.values[2] * METRES_PER_SECOND_SQUARED_TO_GFORCE,
currentDisplayRotation);
}
break;
case Sensor.TYPE_LINEAR_ACCELERATION:
{
// Convert to the same unit of measurement as returned natively by iOS,
// which is (arguably) more useful to use directly for game development.
AlignWithDisplay(m_motionSensorData.accelerationUser,
event.values[0] * METRES_PER_SECOND_SQUARED_TO_GFORCE,
event.values[1] * METRES_PER_SECOND_SQUARED_TO_GFORCE,
event.values[2] * METRES_PER_SECOND_SQUARED_TO_GFORCE,
currentDisplayRotation);
}
break;
case Sensor.TYPE_GRAVITY:
{
// Convert to the same unit of measurement as returned natively by iOS,
// which is (arguably) more useful to use directly for game development.
AlignWithDisplay(m_motionSensorData.accelerationGravity,
event.values[0] * METRES_PER_SECOND_SQUARED_TO_GFORCE,
event.values[1] * METRES_PER_SECOND_SQUARED_TO_GFORCE,
event.values[2] * METRES_PER_SECOND_SQUARED_TO_GFORCE,
currentDisplayRotation);
}
break;
case Sensor.TYPE_GYROSCOPE_UNCALIBRATED:
{
AlignWithDisplay(m_motionSensorData.rotationRateRaw,
event.values[0],
event.values[1],
event.values[2],
currentDisplayRotation);
}
break;
case Sensor.TYPE_GYROSCOPE:
{
AlignWithDisplay(m_motionSensorData.rotationRateUnbiased,
event.values[0],
event.values[1],
event.values[2],
currentDisplayRotation);
}
break;
case Sensor.TYPE_MAGNETIC_FIELD_UNCALIBRATED:
{
AlignWithDisplay(m_motionSensorData.magneticFieldRaw,
event.values[0],
event.values[1],
event.values[2],
currentDisplayRotation);
}
break;
case Sensor.TYPE_MAGNETIC_FIELD:
{
AlignWithDisplay(m_motionSensorData.magneticFieldUnbiased,
event.values[0],
event.values[1],
event.values[2],
currentDisplayRotation);
}
break;
case Sensor.TYPE_GAME_ROTATION_VECTOR:
case Sensor.TYPE_ROTATION_VECTOR:
{
m_motionSensorData.orientation.x = event.values[0];
m_motionSensorData.orientation.y = event.values[1];
m_motionSensorData.orientation.z = event.values[2];
m_motionSensorData.orientation.w = event.values[3];
// Android doesn't provide us with any quaternion math,
// so we do the alignment in MotionSensorinputDevice.cpp
m_orientationAdjustmentRadiansZ = GetOrientationAdjustmentRadiansZ(currentDisplayRotation);
m_motionSensorData.orientation.updated = true;
}
break;
}
}
private void AlignWithDisplay(MotionSensorData.SensorData3D o_sensorData, float x, float y, float z, int displayRotation)
{
switch (displayRotation)
{
case Surface.ROTATION_90:
{
o_sensorData.x = -y;
o_sensorData.y = -z;
o_sensorData.z = x;
}
break;
case Surface.ROTATION_180:
{
o_sensorData.x = -x;
o_sensorData.y = -z;
o_sensorData.z = -y;
}
break;
case Surface.ROTATION_270:
{
o_sensorData.x = y;
o_sensorData.y = -z;
o_sensorData.z = -x;
}
break;
case Surface.ROTATION_0:
default:
{
o_sensorData.x = x;
o_sensorData.y = -z;
o_sensorData.z = y;
}
break;
}
o_sensorData.updated = true;
}
private float GetOrientationAdjustmentRadiansZ(int displayRotation)
{
switch (displayRotation)
{
case Surface.ROTATION_90:
{
return (float)(-Math.PI * 0.5d);
}
case Surface.ROTATION_180:
{
return (float)Math.PI;
}
case Surface.ROTATION_270:
{
return (float)(Math.PI * 0.5d);
}
case Surface.ROTATION_0:
default:
{
return 0.0f;
}
}
}
// Called from native code to query availability of motion sensor data.
public boolean IsMotionSensorDataAvailable(boolean accelerometerRaw,
boolean accelerometerUser,
boolean accelerometerGravity,
boolean rotationRateRaw,
boolean rotationRateUnbiased,
boolean magneticFieldRaw,
boolean magneticFieldUnbiased,
boolean orientation)
{
if (m_sensorManager == null)
{
return false;
}
if (accelerometerRaw && m_sensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER) == null)
{
return false;
}
if (accelerometerUser && m_sensorManager.getDefaultSensor(Sensor.TYPE_LINEAR_ACCELERATION) == null)
{
return false;
}
if (accelerometerGravity && m_sensorManager.getDefaultSensor(Sensor.TYPE_GRAVITY) == null)
{
return false;
}
if (rotationRateRaw && m_sensorManager.getDefaultSensor(Sensor.TYPE_GYROSCOPE_UNCALIBRATED) == null)
{
return false;
}
if (rotationRateUnbiased && m_sensorManager.getDefaultSensor(Sensor.TYPE_GYROSCOPE) == null)
{
return false;
}
if (magneticFieldRaw && m_sensorManager.getDefaultSensor(Sensor.TYPE_MAGNETIC_FIELD_UNCALIBRATED) == null)
{
return false;
}
if (magneticFieldUnbiased && m_sensorManager.getDefaultSensor(Sensor.TYPE_MAGNETIC_FIELD) == null)
{
return false;
}
if (orientation && m_sensorManager.getDefaultSensor(m_orientationSensorToUse) == null)
{
return false;
}
return false;
}
// Called from native code to refresh motion sensors.
// state: -1 = disable, 0 = unchanged, 1 = enable
public void RefreshMotionSensors(float updateIntervalSeconds,
int accelerometerRawState,
int accelerometerUserState,
int accelerometerGravityState,
int rotationRateRawState,
int rotationRateUnbiasedState,
int magneticFieldRawState,
int magneticFieldUnbiasedState,
int orientationState)
{
int updateIntervalMicroeconds = (int)(updateIntervalSeconds * 1000000);
RefreshMotionSensor(Sensor.TYPE_ACCELEROMETER, accelerometerRawState, updateIntervalMicroeconds);
RefreshMotionSensor(Sensor.TYPE_LINEAR_ACCELERATION, accelerometerUserState, updateIntervalMicroeconds);
RefreshMotionSensor(Sensor.TYPE_GRAVITY, accelerometerGravityState, updateIntervalMicroeconds);
RefreshMotionSensor(Sensor.TYPE_GYROSCOPE_UNCALIBRATED, rotationRateRawState, updateIntervalMicroeconds);
RefreshMotionSensor(Sensor.TYPE_GYROSCOPE, rotationRateUnbiasedState, updateIntervalMicroeconds);
RefreshMotionSensor(Sensor.TYPE_MAGNETIC_FIELD_UNCALIBRATED, magneticFieldRawState, updateIntervalMicroeconds);
RefreshMotionSensor(Sensor.TYPE_MAGNETIC_FIELD, magneticFieldUnbiasedState, updateIntervalMicroeconds);
RefreshMotionSensor(m_orientationSensorToUse, orientationState, updateIntervalMicroeconds);
}
private void RefreshMotionSensor(int sensorType, int state, int updateIntervalMicroeconds)
{
if (m_sensorManager == null)
{
return;
}
// state: -1 = disable, 0 = unchanged, 1 = enable
switch (state)
{
case 1:
{
Sensor defaultSensor = m_sensorManager.getDefaultSensor(sensorType);
if (defaultSensor != null)
{
m_sensorManager.registerListener(this, defaultSensor, updateIntervalMicroeconds);
}
}
break;
case -1:
{
Sensor defaultSensor = m_sensorManager.getDefaultSensor(sensorType);
if (defaultSensor != null)
{
m_sensorManager.unregisterListener(this, defaultSensor);
}
}
break;
}
}
// Called from native code to retrieve the latest motion sensor data.
public float[] RequestLatestMotionSensorData()
{
// While we would ideally like to just return m_motionSensorData directly,
// the native C++ code would then need to 'reach back' through the JNI
// for each field just to access the raw data. Simply returning a float
// array packed with all the required values is far more efficient, at
// the expense of the native C++ code having to access the data through
// 'magic' array indices instead of explcitly naming the class fields.
//
// Additionally, while explicitly calling out the class fields provides
// a modicum of safety, lots of boilerplate code is needed, and while we
// may in future append additional sensor data the existing elements are
// unlikely to ever change. Combined with the above mentioned performance
// considerations, this approach seems preferable on most fronts.
m_motionSensorDataPacked[0] = m_motionSensorData.accelerationRaw.updated ? 1 : 0;
m_motionSensorDataPacked[1] = m_motionSensorData.accelerationRaw.x;
m_motionSensorDataPacked[2] = m_motionSensorData.accelerationRaw.y;
m_motionSensorDataPacked[3] = m_motionSensorData.accelerationRaw.z;
m_motionSensorData.accelerationRaw.updated = false;
m_motionSensorDataPacked[4] = m_motionSensorData.accelerationUser.updated ? 1 : 0;
m_motionSensorDataPacked[5] = m_motionSensorData.accelerationUser.x;
m_motionSensorDataPacked[6] = m_motionSensorData.accelerationUser.y;
m_motionSensorDataPacked[7] = m_motionSensorData.accelerationUser.z;
m_motionSensorData.accelerationUser.updated = false;
m_motionSensorDataPacked[8] = m_motionSensorData.accelerationGravity.updated ? 1 : 0;
m_motionSensorDataPacked[9] = m_motionSensorData.accelerationGravity.x;
m_motionSensorDataPacked[10] = m_motionSensorData.accelerationGravity.y;
m_motionSensorDataPacked[11] = m_motionSensorData.accelerationGravity.z;
m_motionSensorData.accelerationGravity.updated = false;
m_motionSensorDataPacked[12] = m_motionSensorData.rotationRateRaw.updated ? 1 : 0;
m_motionSensorDataPacked[13] = m_motionSensorData.rotationRateRaw.x;
m_motionSensorDataPacked[14] = m_motionSensorData.rotationRateRaw.y;
m_motionSensorDataPacked[15] = m_motionSensorData.rotationRateRaw.z;
m_motionSensorData.rotationRateRaw.updated = false;
m_motionSensorDataPacked[16] = m_motionSensorData.rotationRateUnbiased.updated ? 1 : 0;
m_motionSensorDataPacked[17] = m_motionSensorData.rotationRateUnbiased.x;
m_motionSensorDataPacked[18] = m_motionSensorData.rotationRateUnbiased.y;
m_motionSensorDataPacked[19] = m_motionSensorData.rotationRateUnbiased.z;
m_motionSensorData.rotationRateUnbiased.updated = false;
m_motionSensorDataPacked[20] = m_motionSensorData.magneticFieldRaw.updated ? 1 : 0;
m_motionSensorDataPacked[21] = m_motionSensorData.magneticFieldRaw.x;
m_motionSensorDataPacked[22] = m_motionSensorData.magneticFieldRaw.y;
m_motionSensorDataPacked[23] = m_motionSensorData.magneticFieldRaw.z;
m_motionSensorData.magneticFieldRaw.updated = false;
m_motionSensorDataPacked[24] = m_motionSensorData.magneticFieldUnbiased.updated ? 1 : 0;
m_motionSensorDataPacked[25] = m_motionSensorData.magneticFieldUnbiased.x;
m_motionSensorDataPacked[26] = m_motionSensorData.magneticFieldUnbiased.y;
m_motionSensorDataPacked[27] = m_motionSensorData.magneticFieldUnbiased.z;
m_motionSensorData.magneticFieldUnbiased.updated = false;
m_motionSensorDataPacked[28] = m_motionSensorData.orientation.updated ? 1 : 0;
m_motionSensorDataPacked[29] = m_motionSensorData.orientation.x;
m_motionSensorDataPacked[30] = m_motionSensorData.orientation.y;
m_motionSensorDataPacked[31] = m_motionSensorData.orientation.z;
m_motionSensorDataPacked[32] = m_motionSensorData.orientation.w;
m_motionSensorDataPacked[33] = m_orientationAdjustmentRadiansZ;
m_motionSensorData.orientation.updated = false;
return m_motionSensorDataPacked;
}
}
@@ -0,0 +1,112 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
package com.amazon.lumberyard.input;
import android.app.Activity;
import android.content.Context;
import android.hardware.input.InputManager;
import android.view.InputDevice;
import java.util.HashSet;
import java.util.Set;
public class MouseDevice
implements InputManager.InputDeviceListener
{
public native void OnMouseConnected();
public native void OnMouseDisconnected();
public MouseDevice(Activity activity)
{
m_inputManager = (InputManager)activity.getSystemService(Context.INPUT_SERVICE);
int[] devices = m_inputManager.getInputDeviceIds();
for (int deviceId : devices)
{
if (IsMouseDevice(deviceId))
{
m_mouseDeviceIds.add(deviceId);
}
}
final InputManager.InputDeviceListener listener = this;
activity.runOnUiThread(new Runnable() {
@Override
public void run() {
// run the registration on the main thread to use it's looper as the handler
// instead of creating one specifically for listening to mouse [dis]connections
m_inputManager.registerInputDeviceListener(listener, null);
}
});
}
@Override
public void onInputDeviceAdded(int deviceId)
{
if (IsMouseDevice(deviceId))
{
m_mouseDeviceIds.add(deviceId);
// only inform the native code if we change from having no mice connected, extra
// are effectively ignored and folded into one "master" device
if (m_mouseDeviceIds.size() == 1)
{
OnMouseConnected();
}
}
}
@Override
public void onInputDeviceChanged(int deviceId)
{
// do nothing
}
@Override
public void onInputDeviceRemoved(int deviceId)
{
if (m_mouseDeviceIds.contains(deviceId))
{
m_mouseDeviceIds.remove(deviceId);
// only inform the native code if we change to having no mice connected
if (m_mouseDeviceIds.size() == 0)
{
OnMouseDisconnected();
}
}
}
public boolean IsConnected()
{
return (m_mouseDeviceIds.size() > 0);
}
private boolean IsMouseDevice(int deviceId)
{
InputDevice device = m_inputManager.getInputDevice(deviceId);
if (device == null)
{
return false;
}
int sources = device.getSources();
return (sources == InputDevice.SOURCE_MOUSE);
}
private InputManager m_inputManager = null;
private Set<Integer> m_mouseDeviceIds = new HashSet<>();
}
@@ -0,0 +1,91 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
package com.amazon.lumberyard.io;
import android.content.res.AssetManager;
import android.util.Log;
import java.io.IOException;
import android.app.Activity;
////////////////////////////////////////////////////////////////
public class APKHandler
{
////////////////////////////////////////////////////////////////
public static void SetAssetManager(AssetManager assetManager)
{
s_assetManager = assetManager;
}
////////////////////////////////////////////////////////////////
public static String[] GetFilesAndDirectoriesInPath(String path)
{
String[] filelist = {};
try
{
filelist = s_assetManager.list(path);
}
catch (IOException e)
{
Log.e(s_tag, String.format("File I/O error: %s", e.getMessage()));
e.printStackTrace();
}
finally
{
if (s_debug)
{
Log.d(s_tag, String.format("Files in path: %s", path));
for(String name : filelist)
{
Log.d(s_tag, String.format(" -- %s", name));
}
}
return filelist;
}
}
////////////////////////////////////////////////////////////////
public static boolean IsDirectory(String path)
{
String[] filelist = {};
boolean retVal = false;
try
{
filelist = s_assetManager.list(path);
if(filelist.length > 0)
{
retVal = true;
}
}
catch (IOException e)
{
Log.e(s_tag, String.format("File I/O error: %s", e.getMessage()));
e.printStackTrace();
}
finally
{
return retVal;
}
}
// ----
private static final String s_tag = "LMBR";
private static AssetManager s_assetManager = null;
private static boolean s_debug = false;
}
@@ -0,0 +1,323 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
package com.amazon.lumberyard.io.obb;
import android.app.Activity;
import android.app.PendingIntent;
import android.content.Intent;
import android.content.res.Resources;
import android.os.AsyncTask;
import android.os.Bundle;
import android.os.Messenger;
import android.provider.Settings;
import android.view.View;
import android.widget.Button;
import android.widget.ProgressBar;
import android.widget.TextView;
import com.google.android.vending.expansion.downloader.Constants;
import com.google.android.vending.expansion.downloader.DownloadProgressInfo;
import com.google.android.vending.expansion.downloader.DownloaderClientMarshaller;
import com.google.android.vending.expansion.downloader.DownloaderServiceMarshaller;
import com.google.android.vending.expansion.downloader.Helpers;
import com.google.android.vending.expansion.downloader.IDownloaderClient;
import com.google.android.vending.expansion.downloader.IDownloaderService;
import com.google.android.vending.expansion.downloader.IStub;
import java.lang.Exception;
////////////////////////////////////////////////////////////////
// Activity that handles the download of the APK expansion package (Obb)
public class ObbDownloaderActivity extends Activity implements IDownloaderClient
{
////////////////////////////////////////////////////////////////
@Override
public void onServiceConnected(Messenger messenger)
{
m_remoteService = DownloaderServiceMarshaller.CreateProxy(messenger);
m_remoteService.onClientUpdated(m_downloaderClientStub.getMessenger());
}
////////////////////////////////////////////////////////////////
@Override
public void onDownloadStateChanged(int newState)
{
setState(newState);
boolean showDashboard = true;
boolean showCellMessage = false;
boolean paused;
boolean indeterminate;
switch (newState)
{
case IDownloaderClient.STATE_IDLE:
// STATE_IDLE means the service is listening, so it's
// safe to start making calls via m_remoteService.
paused = false;
indeterminate = true;
break;
case IDownloaderClient.STATE_CONNECTING:
case IDownloaderClient.STATE_FETCHING_URL:
showDashboard = true;
paused = false;
indeterminate = true;
break;
case IDownloaderClient.STATE_DOWNLOADING:
paused = false;
showDashboard = true;
indeterminate = false;
break;
case IDownloaderClient.STATE_FAILED_CANCELED:
case IDownloaderClient.STATE_FAILED:
case IDownloaderClient.STATE_FAILED_FETCHING_URL:
case IDownloaderClient.STATE_FAILED_UNLICENSED:
paused = true;
showDashboard = false;
indeterminate = false;
break;
case IDownloaderClient.STATE_PAUSED_NEED_CELLULAR_PERMISSION:
case IDownloaderClient.STATE_PAUSED_WIFI_DISABLED_NEED_CELLULAR_PERMISSION:
showDashboard = false;
paused = true;
indeterminate = false;
showCellMessage = true;
break;
case IDownloaderClient.STATE_PAUSED_BY_REQUEST:
paused = true;
indeterminate = false;
break;
case IDownloaderClient.STATE_PAUSED_ROAMING:
case IDownloaderClient.STATE_PAUSED_SDCARD_UNAVAILABLE:
paused = true;
indeterminate = false;
break;
case IDownloaderClient.STATE_COMPLETED:
showDashboard = false;
paused = false;
indeterminate = false;
endActivity(Activity.RESULT_OK);
return;
default:
paused = true;
indeterminate = true;
showDashboard = true;
}
int newDashboardVisibility = showDashboard ? View.VISIBLE : View.GONE;
if (m_dashboard.getVisibility() != newDashboardVisibility)
{
m_dashboard.setVisibility(newDashboardVisibility);
}
int cellMessageVisibility = showCellMessage ? View.VISIBLE : View.GONE;
if (m_cellMessage.getVisibility() != cellMessageVisibility)
{
m_cellMessage.setVisibility(cellMessageVisibility);
}
m_progressBar.setIndeterminate(indeterminate);
setButtonPausedState(paused);
}
////////////////////////////////////////////////////////////////
@Override
public void onDownloadProgress(DownloadProgressInfo progress)
{
m_averageSpeed.setText(getString(m_kbPerSecondTextId, Helpers.getSpeedString(progress.mCurrentSpeed)));
m_timeRemaining.setText(getString(m_timeRemainingTextId, Helpers.getTimeRemaining(progress.mTimeRemaining)));
progress.mOverallTotal = progress.mOverallTotal;
m_progressBar.setMax((int) (progress.mOverallTotal >> 8));
m_progressBar.setProgress((int) (progress.mOverallProgress >> 8));
m_progressPercent.setText(Long.toString(progress.mOverallProgress * 100 / progress.mOverallTotal) + "%");
m_progressFraction.setText(Helpers.getDownloadProgressString(progress.mOverallProgress, progress.mOverallTotal));
}
////////////////////////////////////////////////////////////////
@Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
// Build an Intent to start this activity from the Notification
Intent notifierIntent = new Intent(ObbDownloaderActivity.this, ObbDownloaderActivity.this.getClass());
notifierIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP);
PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, notifierIntent, PendingIntent.FLAG_UPDATE_CURRENT);
try
{
// Start the download service (if required)
int startResult = DownloaderClientMarshaller.startDownloadServiceIfRequired(this, pendingIntent, ObbDownloaderService.class);
// If download has started, initialize this activity to show download progress
if (startResult != DownloaderClientMarshaller.NO_DOWNLOAD_REQUIRED)
{
initializeUI();
return;
}
}
catch (Exception e)
{
endActivity(Activity.RESULT_CANCELED);
return;
}
endActivity(Activity.RESULT_OK);
}
////////////////////////////////////////////////////////////////
@Override
protected void onResume()
{
if (m_downloaderClientStub != null)
{
m_downloaderClientStub.connect(this);
}
super.onResume();
}
////////////////////////////////////////////////////////////////
@Override
protected void onStop()
{
if (m_downloaderClientStub != null)
{
m_downloaderClientStub.disconnect(this);
}
super.onStop();
}
////////////////////////////////////////////////////////////////
protected void endActivity(int result)
{
if (isFinishing())
{
return;
}
Intent returnIntent = new Intent();
setResult(result, returnIntent);
finish();
}
////////////////////////////////////////////////////////////////
private void setState(int newState)
{
if (m_state != newState)
{
m_state = newState;
m_statusText.setText(Helpers.getDownloaderStringResourceIDFromState(newState));
}
}
////////////////////////////////////////////////////////////////
private void setButtonPausedState(boolean paused)
{
m_statePaused = paused;
int stringResourceID = paused ? m_buttonResumeTextId : m_buttonPauseTextId;
m_pauseButton.setText(stringResourceID);
}
////////////////////////////////////////////////////////////////
private void initializeUI()
{
Resources resources = this.getResources();
String packageName = getApplicationContext().getPackageName();
m_downloaderClientStub = DownloaderClientMarshaller.CreateStub(this, ObbDownloaderService.class);
setContentView(resources.getIdentifier("obb_downloader", "layout", packageName));
m_progressBar = (ProgressBar) findViewById(resources.getIdentifier("progressBar", "id", packageName));
m_statusText = (TextView) findViewById(resources.getIdentifier("statusText", "id", packageName));
m_progressFraction = (TextView) findViewById(resources.getIdentifier("progressAsFraction", "id", packageName));
m_progressPercent = (TextView) findViewById(resources.getIdentifier("progressAsPercentage", "id", packageName));
m_averageSpeed = (TextView) findViewById(resources.getIdentifier("progressAverageSpeed", "id", packageName));
m_timeRemaining = (TextView) findViewById(resources.getIdentifier("progressTimeRemaining", "id", packageName));
m_dashboard = findViewById(resources.getIdentifier("downloaderDashboard", "id", packageName));
m_cellMessage = findViewById(resources.getIdentifier("approveCellular", "id", packageName));
m_pauseButton = (Button) findViewById(resources.getIdentifier("pauseButton", "id", packageName));
m_wiFiSettingsButton = (Button) findViewById(resources.getIdentifier("wifiSettingsButton", "id", packageName));
m_buttonResumeTextId = resources.getIdentifier("text_button_resume", "string", packageName);
m_buttonPauseTextId = resources.getIdentifier("text_button_pause", "string", packageName);
m_timeRemainingTextId = resources.getIdentifier("time_remaining", "string", packageName);
m_kbPerSecondTextId = resources.getIdentifier("kilobytes_per_second", "string", packageName);
m_pauseButton.setOnClickListener(new View.OnClickListener()
{
@Override
public void onClick(View view)
{
if (m_statePaused)
{
m_remoteService.requestContinueDownload();
}
else
{
m_remoteService.requestPauseDownload();
}
setButtonPausedState(!m_statePaused);
}
});
m_wiFiSettingsButton.setOnClickListener(new View.OnClickListener()
{
@Override
public void onClick(View v)
{
startActivity(new Intent(Settings.ACTION_WIFI_SETTINGS));
}
});
Button resumeOnCell = (Button) findViewById(resources.getIdentifier("resumeOverCellular", "id", packageName));
resumeOnCell.setOnClickListener(new View.OnClickListener()
{
@Override
public void onClick(View view)
{
m_remoteService.setDownloadFlags(IDownloaderService.FLAGS_DOWNLOAD_OVER_CELLULAR);
m_remoteService.requestContinueDownload();
m_cellMessage.setVisibility(View.GONE);
}
});
}
private static final String TAG = "ObbDownloaderActivity";
private ProgressBar m_progressBar;
private TextView m_statusText;
private TextView m_progressFraction;
private TextView m_progressPercent;
private TextView m_averageSpeed;
private TextView m_timeRemaining;
private View m_dashboard;
private View m_cellMessage;
private Button m_pauseButton;
private Button m_wiFiSettingsButton;
private boolean m_statePaused;
private int m_state;
private IDownloaderService m_remoteService;
private IStub m_downloaderClientStub;
private int m_buttonResumeTextId;
private int m_buttonPauseTextId;
private int m_kbPerSecondTextId;
private int m_timeRemainingTextId;
}
@@ -0,0 +1,40 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
package com.amazon.lumberyard.io.obb;
import com.google.android.vending.expansion.downloader.DownloaderClientMarshaller;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager.NameNotFoundException;
////////////////////////////////////////////////////////////////
// Alarm receiver needeed by the Downloader library for reasuming the download of the Obb.
public class ObbDownloaderAlarmReceiver extends BroadcastReceiver
{
////////////////////////////////////////////////////////////////
@Override
public void onReceive(Context context, Intent intent)
{
try
{
DownloaderClientMarshaller.startDownloadServiceIfRequired(context, intent, ObbDownloaderService.class);
}
catch (NameNotFoundException e)
{
e.printStackTrace();
}
}
}
@@ -0,0 +1,78 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
package com.amazon.lumberyard.io.obb;
import android.content.Context;
import android.content.res.Resources;
import android.util.Log;
import com.google.android.vending.expansion.downloader.impl.DownloaderService;
import com.google.android.vending.licensing.util.Base64;
import com.google.android.vending.licensing.util.Base64DecoderException;
////////////////////////////////////////////////////////////////
// Service needed by the downloader library in order to get the public key, salt and alarm receiver class.
public class ObbDownloaderService extends DownloaderService
{
////////////////////////////////////////////////////////////////
@Override
public void onCreate ()
{
super.onCreate();
Context context = getApplicationContext();
Resources resources = getResources();
int stringId = resources.getIdentifier("public_key", "string", context.getPackageName());
m_base64PublicKey = resources.getString(stringId);
stringId = resources.getIdentifier("obfuscator_salt", "string", context.getPackageName());
String base64Salt = resources.getString(stringId);
if (!base64Salt.isEmpty())
{
try
{
m_salt = Base64.decode(base64Salt);
}
catch (Base64DecoderException e)
{
Log.e("ObbDownloaderService", "Failed to decode the salt string");
}
}
}
////////////////////////////////////////////////////////////////
@Override
public String getPublicKey()
{
return m_base64PublicKey;
}
////////////////////////////////////////////////////////////////
@Override
public byte[] getSALT()
{
return m_salt;
}
////////////////////////////////////////////////////////////////
@Override
public String getAlarmReceiverClassName()
{
return ObbDownloaderAlarmReceiver.class.getName();
}
////////////////////////////////////////////////////////////////
private String m_base64PublicKey;
private byte[] m_salt = new byte[] { 23, 12, 4, -12, -34, 23,
-120, 122, -23, -104, -2, -4, 12, 3, -21, 123, -11, 4, -11, 32
};
}
@@ -0,0 +1,87 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
package com.amazon.test;
import java.util.Random;
public class SimpleObject
{
public class Foo
{
public Foo(int seed)
{
Random rand = new Random(seed);
m_value = rand.nextInt();
}
// ----
public int m_value;
}
// ----
public SimpleObject() {}
public boolean GetBool() { return true; }
public boolean[] GetBoolArray() { return new boolean[] { true, false, true, false }; }
public char GetChar() { return 'L'; }
public char[] GetCharArray() { return new char[] { 'L', 'u', 'm', 'b', 'e', 'r', 'y', 'a', 'r', 'd' }; }
public byte GetByte() { return 1; }
public byte[] GetByteArray() { return new byte[] { 1, 2, 4, 8, 16, 32 }; }
public short GetShort() { return 128; }
public short[] GetShortArray() { return new short[] { 128, 256, 512, 1024, 2048, 8192 }; }
public int GetInt() { return 32768; }
public int[] GetIntArray() { return new int[] { 32768, 65536, 131072, 262144, 524288, 1048576 }; }
public float GetFloat() { return (float)Math.PI; }
public float[] GetFloatArray()
{
float[] result = new float[6];
for (int i = 0; i < 6; ++i)
{
result[i] = (float)(i + 1) / 7.0f;
}
return result;
}
public double GetDouble() { return Math.PI; }
public double[] GetDoubleArray()
{
double[] result = new double[6];
for (int i = 0; i < 6; ++i)
{
result[i] = (double)(i + 1) / 7.0;
}
return result;
}
public Class GetClass() { return this.getClass(); }
public String GetString() { return "Amazon Lumberyard"; }
public Foo GetObject() { return new Foo(1); }
public Foo[] GetObjectArray() { return new Foo[] { new Foo(1), new Foo(2), new Foo(3), new Foo(4) }; }
// ----
private static final String TAG = "SimpleObject";
}