Cardboard App Launcher Example
Cardboard App Launcher is an Android app launcher designed
for use with the Google Cardboard virtual reality head mounted display. Cardboard
App Launcher is intended to replace a regular launcher when the user places
their device inside a Google Cardboard viewer.
Unlike a regular Android launcher, Cardboard App Launcher uses
side by side 3D to display a launcher interface that is usable while the phone
is placed in Google Cardboard. The launcher can be controlled simply by the
user moving his or her head and "clicking" with the magnetic ring or
"hovering" over an icon to launch the corresponding application. Cardboard
App Launcher attempts to only display apps that are compatible with Google
Cardboard, but in the case that it fails to list a specific app, the user can
select it in the settings menus. When the user exits Cardboard Home, it brings
them back to their regular launcher, making for a fairly seamless experience. This project enables users to know about the use of VR Cardboard and its overall functioning.
Equipment Required – VR Cardboard, NFC enabled device.
How to use
the above functionality for end users?
The following steps involved in the use of VR application for Cardboard
App Launcher functionality are listed below:
Step 1- Start the application on your mobile devices.
Step 2- The Application will ask the user to put the device into
the cardboard.
Step 3- Pull the magnet to see the cardboard icon.
Step 4-You will find the cardboard app.
How to
develop this application on Eclipse?
It is very simple for developers to make this application go live.
Follow the below given steps to achieve your goal:
Step 1- Click on New in
the File Menu Bar.
Step 2- Go to the Android
Application Project in the drop down.
Step 3- A pop-up will appear showing the Application Name in which
you have to enter CardboardAppLauncher.Similarly, enter the project name as CardboardAppLauncher, you can also keep a different project
name.
Similarly,
enter the package name as cardboard.app.launcher,
this also you can keep with a different package name.
Step 4- The Minimum
Required SDK should be API 16: Android
4.1 (Jelly Bean), target SDK you can keep as much as you
want.
Step 5- Click the Next
Button thrice and Choose Blank Activity,
again click on Next and enter
Activity Name as LauncherActivity.
Step 6- Enter Layout Name as activity_game
and click on Finish button.
Step 7: Now, download the
cardboard.jar
Step 8- Go to the manifest file to give following permissions:
Ø
<uses-sdk
android:minSdkVersion="16"/> indicates that the device must be running API Level 16
(Jellybean) or higher.
Ø
<uses-sdk
android:targetSdkVersion="19"/> indicates our app is targetting API Level 19 (KitKat).
Ø
<uses-feature
android:glEsVersion="0x00020000" android:required="true"
/> indicates that the
device must support OpenGL ES 2.0 to run the demo app.
Ø
android:screenOrientation="landscape" indicates that the
activity's required screen orientation is "landscape." This is the
orientation you must set for VR apps. The view used by the Cardboard SDK, CardboardView, only renders on fullscreen and
landscape (landscape, reverseLandscape, sensorLandscape) modes.
Ø
The
setting android:configChanges="orientation|keyboardHidden" is also recommended, but
not mandatory.
Ø
android.permission.NFC permission is required by
the Cardboard SDK to access Cardboard's NFC tag.
Ø android.permission.READ_EXTERNAL_STORAGE and
android.permission.WRITE_EXTERNAL_STORAGE. These permissions are required
by the Cardboard SDK to pair the user's phone to their VR viewer.
Ø
android.permission.VIBRATE permission is required by
our demo app to make the phone vibrate to inform the user that something has
happened.
Ø android.permission.RECORD_AUDIO
permission is required by our demo app to record
the audio of the device.
Ø Define <category
android:name="android.intent.category.HOME" />
Ø
indicates that
when you press home button, your app will be listed as an option to launch the
launcher home or your home activity (along with all the applications which have
this category in their manifest for an activity).
Ø
<category
android:name="android.intent.category.DEFAULT"/>Setting
Category to Default doesn't mean that this Activity will be used by default
when your app launches. The Activity just says to system that " Oh I could be started, even if the starter Intent's category is
set to Nothing at all ! "
Step 9- Right click on the source file à New à Class à SeekBarPreference à Finish.
Step 10- Now, create the SeekBarPreference class with the help of DialogPreference which is described below:
A base class for Preference objects that are dialog-based. These preferences will, when clicked, open a dialog showing the actual preference controls.
A SeekBar is an extension of ProgressBar that adds a draggable thumb. The user can touch the thumb and drag left or right to set the current progress level or use the arrow keys. Placing focusable widgets to the left or right of a SeekBar is discouraged.
Clients of the SeekBar can attach a SeekBar.OnSeekBarChangeListener to be notified of the user's actions. The demo app's SeekBarPreference extends DialogPreference. SeekBarPreference implements the SeekBar.OnSeekBarChangeListener, OnClickListener.
The following code begins –
Step 10- Now, create the SeekBarPreference class with the help of DialogPreference which is described below:
A base class for Preference objects that are dialog-based. These preferences will, when clicked, open a dialog showing the actual preference controls.
A SeekBar is an extension of ProgressBar that adds a draggable thumb. The user can touch the thumb and drag left or right to set the current progress level or use the arrow keys. Placing focusable widgets to the left or right of a SeekBar is discouraged.
Clients of the SeekBar can attach a SeekBar.OnSeekBarChangeListener to be notified of the user's actions. The demo app's SeekBarPreference extends DialogPreference. SeekBarPreference implements the SeekBar.OnSeekBarChangeListener, OnClickListener.
The following code begins –
package
cardboard.app.launcher;
import
android.app.AlertDialog;
import android.content.Context;
import android.os.Bundle;
import
android.preference.DialogPreference;
import
android.util.AttributeSet;
import
android.view.Gravity;
import android.view.View;
import
android.view.View.OnClickListener;
import
android.widget.Button;
import
android.widget.LinearLayout;
import
android.widget.SeekBar;
import
android.widget.TextView;
public class SeekBarPreference extends DialogPreference implements
SeekBar.OnSeekBarChangeListener, OnClickListener
{
//
---------------------------------------------------------------------------------
//
Private attributes :
private static final String androidns="http://schemas.android.com/apk/res/android";
private SeekBar mSeekBar;
private TextView mSplashText,mValueText;
private Context mContext;
private String mDialogMessage, mSuffix;
private int mDefault, mMax, mValue = 0;
//
---------------------------------------------------------------------------------
//
Constructor :
public
SeekBarPreference(Context context, AttributeSet attrs) {
super(context,attrs);
mContext = context;
//
Get string value for dialogMessage :
int mDialogMessageId = attrs.getAttributeResourceValue(androidns, "dialogMessage", 0);
if(mDialogMessageId == 0) mDialogMessage = attrs.getAttributeValue(androidns, "dialogMessage");
else mDialogMessage = mContext.getString(mDialogMessageId);
//
Get string value for suffix (text attribute in xml file) :
int mSuffixId = attrs.getAttributeResourceValue(androidns, "text", 0);
if(mSuffixId == 0) mSuffix = attrs.getAttributeValue(androidns, "text");
else mSuffix = mContext.getString(mSuffixId);
//
Get default and max seekbar values :
mDefault = attrs.getAttributeIntValue(androidns, "defaultValue", 0);
mMax = attrs.getAttributeIntValue(androidns, "max", 100);
}
//
---------------------------------------------------------------------------------
//
DialogPreference methods :
@Override
protected View onCreateDialogView()
{
LinearLayout.LayoutParams params;
LinearLayout layout = new LinearLayout(mContext);
layout.setOrientation(LinearLayout.VERTICAL);
layout.setPadding(6,6,6,6);
mSplashText = new TextView(mContext);
mSplashText.setPadding(30, 10,
30, 10);
if (mDialogMessage != null)
mSplashText.setText(mDialogMessage);
layout.addView(mSplashText);
mValueText = new TextView(mContext);
mValueText.setGravity(Gravity.CENTER_HORIZONTAL);
mValueText.setTextSize(32);
params = new
LinearLayout.LayoutParams(
LinearLayout.LayoutParams.FILL_PARENT,
LinearLayout.LayoutParams.WRAP_CONTENT);
layout.addView(mValueText, params);
mSeekBar = new SeekBar(mContext);
mSeekBar.setOnSeekBarChangeListener(this);
layout.addView(mSeekBar, new
LinearLayout.LayoutParams(LinearLayout.LayoutParams.FILL_PARENT,
LinearLayout.LayoutParams.WRAP_CONTENT));
if (shouldPersist())
mValue = getPersistedInt(mDefault);
mSeekBar.setMax(mMax);
mSeekBar.setProgress(mValue);
return layout;
}
@Override
protected void
onBindDialogView(View v) {
super.onBindDialogView(v);
mSeekBar.setMax(mMax);
mSeekBar.setProgress(mValue);
}
@Override
protected void onSetInitialValue(boolean restore, Object defaultValue)
{
super.onSetInitialValue(restore, defaultValue);
if (restore)
mValue = shouldPersist() ? getPersistedInt(mDefault) : 0;
else
mValue = (Integer)defaultValue;
}
//
----------------------------------------------------------------------------------
//
OnSeekBarChangeListener methods :
@Override
public void
onProgressChanged(SeekBar seek, int value, boolean fromTouch)
{
String t = String.valueOf(value);
mValueText.setText(mSuffix == null ? t : t.concat(" " + mSuffix));
}
@Override
public void
onStartTrackingTouch(SeekBar seek) {}
@Override
public void
onStopTrackingTouch(SeekBar seek) {}
public void setMax(int max) { mMax = max; }
public int getMax() { return mMax; }
public void setProgress(int progress) {
mValue = progress;
if (mSeekBar != null)
mSeekBar.setProgress(progress);
}
public int getProgress() { return mValue; }
//
---------------------------------------------------------------------------------
//
Set the positive button listener and onClick action :
@Override
public void showDialog(Bundle state) {
super.showDialog(state);
Button positiveButton = ((AlertDialog) getDialog()).getButton(AlertDialog.BUTTON_POSITIVE);
positiveButton.setOnClickListener(this);
}
@Override
public void onClick(View v) {
if (shouldPersist()) {
mValue = mSeekBar.getProgress();
persistInt(mSeekBar.getProgress());
callChangeListener(Integer.valueOf(mSeekBar.getProgress()));
}
((AlertDialog) getDialog()).dismiss();
}
//
----------------------------------------------------------------------------------
}
Step 11-The Application item is created to define the
icons position, gravity, and its type whose code is as follows:
package
cardboard.app.launcher;
import
android.content.Context;
import
android.content.Intent;
import
android.content.pm.ApplicationInfo;
import
android.content.pm.PackageManager;
import
android.graphics.Bitmap;
import
android.graphics.Canvas;
import android.graphics.ColorMatrix;
import
android.graphics.ColorMatrixColorFilter;
import
android.graphics.Paint;
import
android.graphics.Rect;
import
android.graphics.drawable.BitmapDrawable;
public class ApplicationItem {
public int x = 0;
public Rect pos = new Rect();
public Bitmap icon;
public Bitmap iconGry;
public String name = "";
public int z = 10;
public ApplicationInfo appInfo;
private Intent launchIntent;
private Context context;
public ApplicationItem
(Rect pos, ApplicationInfo appInfo, PackageManager pkgMan, Context context) {
this.pos = pos;
this.icon = ((BitmapDrawable) appInfo.loadIcon(pkgMan)).getBitmap();
this.iconGry =
getGrayscaleBitmap(this.icon);
this.name = pkgMan.getApplicationLabel(appInfo).toString();
this.appInfo = appInfo;
this.launchIntent = pkgMan.getLaunchIntentForPackage(appInfo.packageName);
this.context = context;
}
public ApplicationItem
(Rect pos, Bitmap icon, int type, PackageManager pkgMan, Context context) {
this.pos = pos;
this.icon = icon;
this.iconGry =
getGrayscaleBitmap(this.icon);
if (type == 0) {
this.name = "Exit Google
Cardboard";
if (LauncherActivity.preferences.getBoolean("use_as_home", true)) {
this.launchIntent = new Intent(Intent.ACTION_MAIN);
this.launchIntent.addCategory(Intent.CATEGORY_HOME);
this.launchIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
this.launchIntent.setClassName("android", "com.android.internal.app.ResolverActivity");
}
else {
Intent intent = new Intent(Intent.ACTION_MAIN);
intent.addCategory(Intent.CATEGORY_HOME);
String currentHomePackage = pkgMan.resolveActivity(intent, PackageManager.MATCH_DEFAULT_ONLY).activityInfo.packageName;
this.launchIntent = pkgMan.getLaunchIntentForPackage(currentHomePackage);
}
}
else if (type == 1) {
this.name = "Preferences";
this.launchIntent = new Intent(context, SettingsActivity.class).addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
}
else if (type == 2) {
this.name = "Adjust
Volume";
this.launchIntent = new Intent(context, SettingsActivity.class).addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
}
this.context = context;
}
private Bitmap
getGrayscaleBitmap(Bitmap color) {
ColorMatrix colorMatrix = new ColorMatrix();
colorMatrix.setSaturation(0);
ColorMatrixColorFilter colorMatrixFilter = new ColorMatrixColorFilter(colorMatrix);
Bitmap grayscale = color.copy(Bitmap.Config.ARGB_8888, true);
Paint paint = new Paint();
paint.setColorFilter(colorMatrixFilter);
Canvas canvas = new Canvas(grayscale);
canvas.drawBitmap(grayscale, 0, 0, paint);
return grayscale;
}
public void move (int deltaTime) {
pos.left += deltaTime / 1000;
}
public void launch () {
context.startActivity(launchIntent);
android.os.Process.killProcess(android.os.Process.myPid());
}
}
Step 12- Now, create the folder with xml in the res
folder inside which two xml files will
be defined whose code is as follows:
Pref_general.xml à
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
<CheckBoxPreference
android:key="launch_on_hover"
android:title="@string/pref_title_launch_on_hover"
android:summary="@string/pref_description_launch_on_hover"
android:defaultValue="true" />
<CheckBoxPreference
android:key="use_as_home"
android:title="@string/pref_title_use_as_home"
android:summary="@string/pref_description_use_as_home"
android:defaultValue="true" />
<EditTextPreference
android:key="package_names_to_add"
android:title="@string/pref_title_package_names"
android:summary="@string/pref_description_package_names"
android:defaultValue="@string/pref_default_package_names"
android:selectAllOnFocus="false"
android:inputType="textNoSuggestions"
android:singleLine="true"
android:maxLines="1" />
<EditTextPreference
android:key="blacklist"
android:title="@string/pref_title_blacklist"
android:summary="@string/pref_description_blacklist"
android:defaultValue="@string/pref_default_blacklist"
android:selectAllOnFocus="false"
android:inputType="textNoSuggestions"
android:singleLine="true"
android:maxLines="1" />
<cardboard.app.launcher.SeekBarPreference
android:key="interpupilary_distance"
android:title="@string/pref_title_interpupilary_distance"
android:summary="@string/pref_description_interpupilary_distance"
android:dialogMessage="@string/pref_description_interpupilary_distance"
android:defaultValue="50"
android:max="100" />
<CheckBoxPreference
android:key="vibrate_on_selection"
android:title="@string/pref_title_vibrate"
android:defaultValue="true" />
<CheckBoxPreference
android:key="disable_volume_buttons"
android:title="@string/pref_title_disable_volume_buttons"
android:summary="@string/pref_description_disable_volume_buttons"
android:defaultValue="false" />
<CheckBoxPreference
android:key="draw_wallpaper"
android:title="@string/pref_title_draw_wallpaper"
android:summary="@string/pref_description_draw_wallpaper"
android:defaultValue="true" />
<CheckBoxPreference
android:key="listen_for_voice"
android:title="@string/pref_title_listen_for_voice"
android:summary="@string/pref_description_listen_for_voice"
android:defaultValue="true" />
</PreferenceScreen>
Pref_headers.xml à
<preference-headers xmlns:android="http://schemas.android.com/apk/res/android">
<!--
These settings headers are only used on tablets. -->
<header android:fragment="cardboard.app.launcher.SettingsActivity$GeneralPreferenceFragment"
android:title="@string/pref_header_general" />
</preference-headers>
Step 13- The Setting activity has been created with
the help of Preference activity which is described as follows:
This is the base class for an activity to show a hierarchy of preferences to the user. Prior to HONEYCOMB this class only allowed the display of a single set of preference; this functionality should now be found in the new PreferenceFragment class. The Code is as follows:
addPreferencesFromResource(R.xml.pref_general);
This is the base class for an activity to show a hierarchy of preferences to the user. Prior to HONEYCOMB this class only allowed the display of a single set of preference; this functionality should now be found in the new PreferenceFragment class. The Code is as follows:
package
cardboard.app.launcher;
import java.util.List;
import
android.annotation.TargetApi;
import android.content.Context;
import
android.content.Intent;
import
android.content.res.Configuration;
import android.os.Build;
import android.os.Bundle;
import
android.preference.ListPreference;
import
android.preference.Preference;
import
android.preference.PreferenceActivity;
import
android.preference.PreferenceCategory;
import
android.preference.PreferenceFragment;
import
android.preference.PreferenceManager;
/**
* A {@link PreferenceActivity} that presents a
set of application settings. On
* handset devices, settings are presented as a
single list. On tablets,
* settings are split by category, with
category headers shown to the left of
* the list of settings.
* <p>
* See <a href="http://developer.android.com/design/patterns/settings.html">
* Android Design: Settings</a> for design
guidelines and the <a
* href="http://developer.android.com/guide/topics/ui/settings.html">Settings
* API Guide</a> for more information on developing
a Settings UI.
*/
public class SettingsActivity extends PreferenceActivity {
/**
* Determines whether to always show the simplified settings UI, where
* settings are presented in a single list. When false, settings are
shown
* as a master/detail two-pane view on tablets. When true, a single pane is
* shown on tablets.
*/
private static final boolean ALWAYS_SIMPLE_PREFS = false;
@Override
protected void onPostCreate(Bundle savedInstanceState) {
super.onPostCreate(savedInstanceState);
setupSimplePreferencesScreen();
}
/**
* Shows the simplified settings UI if the device configuration if the
* device configuration dictates that a simplified, single-pane UI should be
* shown.
*/
private void
setupSimplePreferencesScreen() {
if (!isSimplePreferences(this)) {
return;
}
//
In the simplified UI, fragments are not used at all and we instead
//
use the older PreferenceActivity APIs.
//
Add 'general' preferences.
PreferenceCategory fakeHeader = new PreferenceCategory(this);
fakeHeader.setTitle(R.string.pref_header_general);
}
/**
{@inheritDoc} */
@Override
public boolean onIsMultiPane() {
return isXLargeTablet(this) && !isSimplePreferences(this);
}
/**
* Helper method to determine if the device has an extra-large screen. For
* example, 10" tablets are extra-large.
*/
private static boolean
isXLargeTablet(Context context) {
return (context.getResources().getConfiguration().screenLayout
& Configuration.SCREENLAYOUT_SIZE_MASK) >=
Configuration.SCREENLAYOUT_SIZE_XLARGE;
}
/**
* Determines whether the simplified settings UI should be shown. This is
* true if this is forced via {@link #ALWAYS_SIMPLE_PREFS}, or the device
* doesn't have newer APIs like {@link PreferenceFragment}, or the device
* doesn't have an extra-large screen. In these cases, a single-pane
* "simplified" settings UI should be shown.
*/
private static boolean
isSimplePreferences(Context context) {
return ALWAYS_SIMPLE_PREFS
|| Build.VERSION.SDK_INT <
Build.VERSION_CODES.HONEYCOMB
|| !isXLargeTablet(context);
}
/**
{@inheritDoc} */
@Override
@TargetApi(Build.VERSION_CODES.HONEYCOMB)
public void
onBuildHeaders(List<Header> target) {
if (!isSimplePreferences(this)) {
loadHeadersFromResource(R.xml.pref_headers, target);
}
}
/**
* A preference value change listener that updates the preference's
summary
* to reflect its new value.
*/
private static
Preference.OnPreferenceChangeListener sBindPreferenceSummaryToValueListener = new
Preference.OnPreferenceChangeListener() {
@Override
public boolean
onPreferenceChange(Preference preference, Object value) {
String stringValue = value.toString();
if (preference instanceof ListPreference) {
// For list preferences, look up the
correct display value in
// the preference's 'entries' list.
ListPreference listPreference = (ListPreference) preference;
int index = listPreference.findIndexOfValue(stringValue);
// Set the summary to reflect the new
value.
preference.setSummary(
index >= 0
? listPreference.getEntries()[index]
: null);
} else {
// For all other preferences, set
the summary to the value's
// simple string representation.
preference.setSummary(stringValue);
}
return true;
}
};
/**
* Binds a preference's summary to its value. More specifically, when the
* preference's value is changed, its summary (line of text below the
* preference title) is updated to reflect the value. The summary is also
* immediately updated upon calling this method. The exact display format
is
* dependent on the type of preference.
*
* @see #sBindPreferenceSummaryToValueListener
*/
private static void
bindPreferenceSummaryToValue(Preference preference) {
//
Set the listener to watch for value changes.
preference.setOnPreferenceChangeListener(sBindPreferenceSummaryToValueListener);
//
Trigger the listener immediately with the preference's
//
current value.
sBindPreferenceSummaryToValueListener.onPreferenceChange(preference,
PreferenceManager
.getDefaultSharedPreferences(preference.getContext())
.getString(preference.getKey(), ""));
}
/**
* This fragment shows general preferences only. It is used when the
* activity is showing a two-pane settings UI.
*/
@TargetApi(Build.VERSION_CODES.HONEYCOMB)
public static class
GeneralPreferenceFragment extends PreferenceFragment {
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
addPreferencesFromResource(R.xml.pref_general);
// Bind the summaries of
EditText/List/Dialog/Ringtone preferences
// to their values. When their values
change, their summaries are
// updated to reflect the new value, per
the Android Design
// guidelines.
bindPreferenceSummaryToValue(findPreference("package_names_to_add"));
bindPreferenceSummaryToValue(findPreference("example_list"));
}
}
@Override
public void onBackPressed () {
startActivity(new Intent(getApplicationContext(), LauncherActivity.class).addFlags(Intent.FLAG_ACTIVITY_NEW_TASK));
}
/**
* This fragment shows notification preferences only. It is used when the
* activity is showing a two-pane settings UI.
*/
}
Step 14- Open the class LauncherActivity as defined below:
CardboardActivity is the starting point for coding a cardboard app. CardboardActivity is the base activity that provides easy integration with Cardboard devices. It exposes events to interact with Cardboards and handles many of the details commonly required when creating an activity for VR rendering.
Note that CardboardActivity uses sticky immersive mode, in which the system UI is hidden, and the content takes up the whole screen. This is a requirement for a VR app, since CardboardView will only render when the activity is in fullscreen mode. See Using Immersive Full-Screen Mode for more discussion of this feature.
The demo app's LauncherActivity extends CardboardActivity. LauncherActivity implements the SensorEventListener SensorManager- lets you access the device's sensors. Get an instance of this class by calling Context.getSystemService() with the argument SENSOR_SERVICE. Always make sure to disable sensors you don't need, especially when your activity is paused. Failing to do so can drain the battery in just a few hours. Note that the system will not disable sensors automatically when the screen turns off.
In this demo use MagnetSensor. This is public class. MagnetSensor Magnetometer sensor detector for the cardboard trigger. Provides cardboard trigger events from the device magnetometer to listeners.
CardboardActivity is the starting point for coding a cardboard app. CardboardActivity is the base activity that provides easy integration with Cardboard devices. It exposes events to interact with Cardboards and handles many of the details commonly required when creating an activity for VR rendering.
Note that CardboardActivity uses sticky immersive mode, in which the system UI is hidden, and the content takes up the whole screen. This is a requirement for a VR app, since CardboardView will only render when the activity is in fullscreen mode. See Using Immersive Full-Screen Mode for more discussion of this feature.
The demo app's LauncherActivity extends CardboardActivity. LauncherActivity implements the SensorEventListener SensorManager- lets you access the device's sensors. Get an instance of this class by calling Context.getSystemService() with the argument SENSOR_SERVICE. Always make sure to disable sensors you don't need, especially when your activity is paused. Failing to do so can drain the battery in just a few hours. Note that the system will not disable sensors automatically when the screen turns off.
In this demo use MagnetSensor. This is public class. MagnetSensor Magnetometer sensor detector for the cardboard trigger. Provides cardboard trigger events from the device magnetometer to listeners.
MagnetSensor magnetSensor = new MagnetSensor(getApplicationContext());
MagnetSensor.OnCardboardTriggerListener magnetTriggerListener = new
MagnetSensor.OnCardboardTriggerListener() {
@Override
public void onCardboardTrigger()
{
gameView.magnetPull();
}
};
magnetSensor.setOnCardboardTriggerListener(magnetTriggerListener);
magnetSensor.start();
In NFC
Sensor -Provide access to Near Field Communication (NFC) functionality.
NfcSensor.OnCardboardNfcListener nfcListener = new
NfcSensor.OnCardboardNfcListener() {
&n*bsp; @Override
public void
onInsertedIntoCardboard(CardboardDeviceParams cardboardDeviceParams) {
}
@Override
public void
onRemovedFromCardboard() {
installedApps.get(installedApps.size() -
1).launch();
}
};
Now, please
go through the whole code as mentioned below:
package
cardboard.app.launcher;
import java.util.ArrayList;
import java.util.List;
import
android.app.WallpaperManager;
import
android.content.ComponentName;
import
android.content.Context;
import
android.content.Intent;
import
android.content.IntentFilter;
import
android.content.SharedPreferences;
import
android.content.pm.ApplicationInfo;
import
android.content.pm.PackageManager;
import
android.graphics.Bitmap;
import
android.graphics.BitmapFactory;
import
android.graphics.Canvas;
import
android.graphics.Color;
import android.graphics.Paint;
import
android.graphics.Rect;
import
android.graphics.RectF;
import
android.graphics.drawable.BitmapDrawable;
import
android.hardware.Sensor;
import
android.hardware.SensorEvent;
import
android.hardware.SensorEventListener;
import
android.hardware.SensorManager;
import
android.media.AudioManager;
import android.os.Bundle;
import android.os.Vibrator;
import
android.preference.PreferenceManager;
import
android.speech.RecognitionListener;
import
android.speech.RecognizerIntent;
import
android.speech.SpeechRecognizer;
import
android.view.Display;
import
android.view.KeyEvent;
import android.view.Menu;
import
android.view.MenuItem;
import
android.view.MotionEvent;
import android.view.View;
import
android.view.WindowManager;
import
android.widget.Toast;
import
com.google.vrtoolkit.cardboard.CardboardActivity;
import
com.google.vrtoolkit.cardboard.CardboardDeviceParams;
import
com.google.vrtoolkit.cardboard.sensors.HeadTracker;
import
com.google.vrtoolkit.cardboard.sensors.MagnetSensor;
import com.google.vrtoolkit.cardboard.sensors.NfcSensor;
public class LauncherActivity extends CardboardActivity implements
SensorEventListener
{
public static final double TAU = Math.PI * 2;
private MyView gameView = null;
public static
ArrayList<ApplicationItem> installedApps = new
ArrayList<ApplicationItem>();
public int iconCenter = 0;
public float rotationalOffset = 0;
public static float accelData = 0f;
public static float rawAccelData = 0f;
public static float rawGyroData = 0f;
public static float accelDataOld = 0f;
private SensorManager mSensorManager;
private Sensor mAccelerometer;
private Sensor mGyroscope;
private HeadTracker headTracker;
private boolean forceAccelerometer = false;
private float tweenStep = 0;
public static SharedPreferences preferences;
public Vibrator mVibrator;
private Bitmap wallpaper;
private boolean drawWallpaper = true;
private boolean startingApp = false;
private ApplicationItem appToLaunch = null;
private SpeechRecognizer speechRecog = null;
private Intent recognizerIntent = null;
private boolean readyToListen = false;
private boolean listening = false;
private int originalVolume = 0;
public boolean volumePanelExpanded = false;
public int volumePanelPosition = 0;
public int volumePanelKnobPosition = 0;
public int volumePanelWidth = 1;
private final int APP_SPACING = 115;
private final float TWEEN_TIMING = 0.5f * 60;
private final String[] LAUNCH_COMMANDS = { "open", "launch", "play",
"start", "begin" };
private final String[] EXIT_COMMANDS = { "exit", "close", "quit", "stop" };
private final String[] SETTINGS_COMMANDS = { "preferences", "settings",
"options" };
@Override
protected void onCreate(Bundle
savedInstanceState) {
super.onCreate(savedInstanceState);
preferences = PreferenceManager
.getDefaultSharedPreferences(getBaseContext());
gameView = new MyView(this);
setContentView(gameView);
makeImmersive();
if
(!isMyAppLauncherDefault()
&&
preferences.getBoolean("use_as_home", true)) {
Intent
intent = new Intent(Intent.ACTION_MAIN);
intent.addCategory(Intent.CATEGORY_HOME);
intent.setClassName("android",
"com.android.internal.app.ResolverActivity");
startActivity(intent);
}
installedApps.clear();
drawWallpaper = preferences.getBoolean("draw_wallpaper", true);
// get installed apps
List<ApplicationInfo>
packages = getPackageManager()
.getInstalledApplications(PackageManager.GET_META_DATA);
for (int i = 0; i <
packages.size(); i++) {
if ((packages.get(i).packageName.toLowerCase()
.contains("cardboard")
||
packages.get(i).packageName.toLowerCase().contains(
"dive")
||
packages.get(i).packageName.toLowerCase().contains("vr")
||
packages.get(i).packageName.toLowerCase().contains(
"virtual") || packages.get(i).packageName
.toLowerCase().contains("reality"))
&&
!packages.get(i).packageName
.equals("cardboard.app.launcher")) {
String[]
ignoreNames = preferences.getString("blacklist", "")
.split(", ");
boolean appOnBlacklist = false;
for (int j = 0; j <
ignoreNames.length; j++) {
if (getPackageManager()
.getApplicationLabel(packages.get(i)).toString()
.toLowerCase().equals(ignoreNames[j].toLowerCase()))
{
appOnBlacklist
= true;
}
}
if (!appOnBlacklist)
installedApps.add(new ApplicationItem(new Rect(
(installedApps.size() - 1) * APP_SPACING, 315, 92,
92),
packages.get(i), getPackageManager(),
getBaseContext()));
}
else {
String[]
packageNames = preferences.getString(
"package_names_to_add", "").split(", ");
for (int j = 0; j <
packageNames.length; j++) {
if (getPackageManager()
.getApplicationLabel(packages.get(i)).toString()
.toLowerCase()
.equals(packageNames[j].toLowerCase())
&& !packages.get(i).packageName
.equals("cardboard.app.launcher")) {
installedApps.add(new ApplicationItem(new Rect(
(installedApps.size() - 1) * APP_SPACING, 315,
92,
92), packages.get(i), getPackageManager(),
getBaseContext()));
}
}
}
}
installedApps.add(new ApplicationItem(new Rect(
(installedApps.size() - 1) * APP_SPACING, 315, 92, 92),
BitmapFactory.decodeResource(getResources(),
R.drawable.volume_icon), 2,
getPackageManager(),
getBaseContext()));
installedApps.add(new ApplicationItem(new Rect(
(installedApps.size() - 1) * APP_SPACING, 315, 92, 92),
BitmapFactory.decodeResource(getResources(),
R.drawable.settings_icon), 1,
getPackageManager(),
getBaseContext()));
installedApps.add(new ApplicationItem(new Rect(
(installedApps.size() - 1) * APP_SPACING, 315, 92, 92),
BitmapFactory.decodeResource(getResources(),
R.drawable.exit_icon), 0,
getPackageManager(),
getBaseContext()));
iconCenter = (int) ((((installedApps.size() + 1) / 2) -
4.5) * APP_SPACING);
mVibrator = (Vibrator)
getSystemService(Context.VIBRATOR_SERVICE);
SensorEventListener
mSensorListener = new SensorEventListener() {
@Override
public void
onAccuracyChanged(Sensor arg0, int arg1) {
}
@Override
public void
onSensorChanged(SensorEvent event) {
Sensor
sensor = event.sensor;
if (sensor.getType() ==
Sensor.TYPE_ACCELEROMETER) {
accelDataOld = rawAccelData;
rawAccelData = event.values[1];
tweenStep = (rawAccelData - accelData) / TWEEN_TIMING;
}
else if (sensor.getType() ==
Sensor.TYPE_GYROSCOPE) {
rawGyroData += event.values[0];
}
}
};
mSensorManager = (SensorManager)
getSystemService(Context.SENSOR_SERVICE);
mGyroscope = mSensorManager.getDefaultSensor(Sensor.TYPE_GYROSCOPE);
mAccelerometer = mSensorManager
.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
mSensorManager.registerListener(mSensorListener,
mGyroscope,
SensorManager.SENSOR_DELAY_NORMAL);
mSensorManager.registerListener(mSensorListener,
mAccelerometer,
SensorManager.SENSOR_DELAY_NORMAL);
headTracker = new
HeadTracker(getApplicationContext());
headTracker.startTracking();
MagnetSensor
magnetSensor = new MagnetSensor(getApplicationContext());
MagnetSensor.OnCardboardTriggerListener
magnetTriggerListener = new MagnetSensor.OnCardboardTriggerListener() {
@Override
public void onCardboardTrigger()
{
gameView.magnetPull();
}
};
magnetSensor.setOnCardboardTriggerListener(magnetTriggerListener);
magnetSensor.start();
// NfcSensor
nfcSensor = NfcSensor.getInstance(getApplicationContext());
NfcSensor.OnCardboardNfcListener
nfcListener = new NfcSensor.OnCardboardNfcListener() {
@Override
public void onInsertedIntoCardboard(
CardboardDeviceParams
cardboardDeviceParams) {
}
@Override
public void
onRemovedFromCardboard() {
installedApps.get(installedApps.size() -
1).launch();
}
};
AudioManager
mgr = (AudioManager) getSystemService(Context.AUDIO_SERVICE);
originalVolume =
mgr.getStreamVolume(AudioManager.STREAM_MUSIC);
if (preferences.getBoolean("listen_for_voice", true))
mgr.setStreamVolume(AudioManager.STREAM_MUSIC, 0,
AudioManager.FLAG_REMOVE_SOUND_AND_VIBRATE);
if (preferences.getBoolean("listen_for_voice", true)) {
RecognitionListener
recognitionListener = new RecognitionListener() {
@Override
public void
onReadyForSpeech(Bundle bundle) {
}
@Override
public void
onBeginningOfSpeech() {
listening = true;
}
@Override
public void onRmsChanged(float v) {
}
@Override
public void onBufferReceived(byte[] bytes) {
}
@Override
public void onEndOfSpeech() {
listening = false;
}
@Override
public void onError(int i) {
}
@Override
public void onResults(Bundle
bundle) {
checkForCommands(bundle);
listening = false;
}
@Override
public void
onPartialResults(Bundle bundle) {
checkForCommands(bundle);
}
private void
checkForCommands(Bundle bundle) {
ArrayList<String>
voiceText = bundle
.getStringArrayList(SpeechRecognizer.RESULTS_RECOGNITION);
for (int i = 0; i <
voiceText.size(); i++) {
if (voiceText.get(i).toLowerCase()
.contains("okay
cardboard")
&&
voiceText.get(i).toLowerCase()
.split("okay
cardboard").length > 0) {
String
request = voiceText.get(i).toLowerCase()
.split("okay
cardboard")[1].toLowerCase()
.replace(" ", "");
print(request);
for (int j = 0; j < LAUNCH_COMMANDS.length; j++) {
if (request.contains(LAUNCH_COMMANDS[j])) {
print(request);
for (int k = 0; k < installedApps.size(); k++) {
if (request.contains(installedApps
.get(k).name.toLowerCase()
.replace(" ", ""))) {
mVibrator.vibrate(90);
installedApps.get(k).launch();
return;
}
}
}
}
for (int j = 0; j < EXIT_COMMANDS.length; j++) {
if (request.contains(EXIT_COMMANDS[j])) {
installedApps.get(installedApps.size() - 1)
.launch();
return;
}
}
for (int j = 0; j < SETTINGS_COMMANDS.length; j++) {
if (request.contains(SETTINGS_COMMANDS[j])) {
installedApps.get(installedApps.size() - 2)
.launch();
return;
}
}
}
}
}
@Override
public void onEvent(int i, Bundle bundle) {
}
};
if (recognizerIntent == null) {
Toast.makeText(LauncherActivity.this, "No Voice
Avalible ",
Toast.LENGTH_LONG).show();
}
else {
recognizerIntent = RecognizerIntent
.getVoiceDetailsIntent(getBaseContext());
recognizerIntent
.putExtra(
RecognizerIntent.EXTRA_SPEECH_INPUT_COMPLETE_SILENCE_LENGTH_MILLIS,
30000);
recognizerIntent
.putExtra(
RecognizerIntent.EXTRA_SPEECH_INPUT_MINIMUM_LENGTH_MILLIS,
30000);
recognizerIntent
.putExtra(
RecognizerIntent.EXTRA_SPEECH_INPUT_POSSIBLY_COMPLETE_SILENCE_LENGTH_MILLIS,
30000);
speechRecog = SpeechRecognizer
.createSpeechRecognizer(getBaseContext());
speechRecog.setRecognitionListener(recognitionListener);
readyToListen = true;
listening = true;
speechRecog.startListening(recognizerIntent);
}
}
wallpaper = ((BitmapDrawable)
WallpaperManager.getInstance(this)
.getDrawable()).getBitmap();
}
private void print(String
request) {
System.out.println(request);
}
private void updateTweens() {
accelData += tweenStep;
}
private void
prepareToLaunch(ApplicationItem app) {
appToLaunch = app;
startingApp = true;
}
private void launchApp() {
startingApp = false;
if (appToLaunch.name.equals("Adjust Volume")) {
// Bring up volume
adjustment panel
// if
(originalVolume == 0) {
// AudioManager
audioManager = (AudioManager)
//
getSystemService(Context.AUDIO_SERVICE);
// originalVolume =
//
audioManager.getStreamMaxVolume(audioManager.STREAM_MUSIC);
// }
// else
// originalVolume =
0;
volumePanelExpanded = true;
volumePanelPosition = (int) ((gameView.headFloats[0] - rotationalOffset) * 500);
AudioManager
audioManager = (AudioManager) getSystemService(Context.AUDIO_SERVICE);
volumePanelKnobPosition = (originalVolume / audioManager
.getStreamMaxVolume(audioManager.STREAM_MUSIC)) * 100;
// Ready to launch
other app
gameView.appStartAnimationPosition = 0;
}
else {
AudioManager
audioManager = (AudioManager) getSystemService(Context.AUDIO_SERVICE);
audioManager.setStreamVolume(AudioManager.STREAM_MUSIC,
originalVolume, AudioManager.FLAG_REMOVE_SOUND_AND_VIBRATE);
appToLaunch.launch();
}
}
private void makeImmersive() {
if (android.os.Build.VERSION.SDK_INT >=
android.os.Build.VERSION_CODES.KITKAT) {
getWindow().getDecorView().setSystemUiVisibility(
View.SYSTEM_UI_FLAG_LAYOUT_STABLE
|
View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
|
View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
| View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
|
View.SYSTEM_UI_FLAG_FULLSCREEN
|
View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY);
}
}
public boolean
isMyAppLauncherDefault() {
IntentFilter
filter = new IntentFilter(Intent.ACTION_MAIN);
filter.addCategory(Intent.CATEGORY_HOME);
List<IntentFilter>
filters = new ArrayList<IntentFilter>();
filters.add(filter);
// the packageName
of your application
String
packageName = getPackageName();
List<ComponentName>
preferredActivities = new ArrayList<ComponentName>();
final PackageManager
packageManager = getPackageManager();
// You can use name
of your package here as third argument
packageManager.getPreferredActivities(filters,
preferredActivities,
null);
for (ComponentName
activity : preferredActivities) {
if
(packageName.equals(activity.getPackageName())) {
return true;
}
}
return false;
}
@Override
public boolean onKeyDown(int keyCode, KeyEvent
event) {
if (keyCode ==
KeyEvent.KEYCODE_BACK)
return true;
else if ((keyCode ==
KeyEvent.KEYCODE_VOLUME_DOWN || keyCode == KeyEvent.KEYCODE_VOLUME_UP)
&&
preferences.getBoolean("disable_volume_buttons", false))
return true;
else
return super.onKeyDown(keyCode,
event);
}
@Override
public boolean onCreateOptionsMenu(Menu
menu) {
// Inflate the
menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.game, menu);
return true;
}
@Override
public boolean
onOptionsItemSelected(MenuItem item) {
// Handle action
bar item clicks here. The action bar will
// automatically
handle clicks on the Home/Up button, so long
// as you specify a
parent activity in AndroidManifest.xml.
int id =
item.getItemId();
return (id == R.id.action_settings || super.onOptionsItemSelected(item));
}
protected void onResume() {
super.onResume();
mSensorManager.registerListener(this, mAccelerometer,
SensorManager.SENSOR_DELAY_NORMAL);
makeImmersive();
}
protected void onPause() {
super.onPause();
mSensorManager.unregisterListener(this);
}
@Override
public void
onSensorChanged(SensorEvent sensorEvent) {
}
@Override
public void
onAccuracyChanged(Sensor sensor, int i) {
}
public class MyView extends View {
private Paint paint = new Paint();
private int width = 0;
private int height = 0;
private Rect cursorPosition = new Rect(width / 2 - 1, height / 2 - 1,
width / 2 + 1, height / 2 + 1);
private int appStartAnimationPosition = 0;
private Rect freeAllocate = new Rect();
long timeOfLastFrame = 0;
public MyView(Context
context) {
super(context);
paint.setStyle(Paint.Style.FILL);
width = getWidth() / 2;
volumePanelWidth = width - 480;
height = getHeight();
timeOfLastFrame = System.currentTimeMillis();
gameLoop();
}
private int selectedApp = -1;
private long timeSelected = -1;
private final long selectionTime = 200;
private void gameLoop() {
move();
updateTweens();
if (width == 0) {
width = getWidth() / 2;
volumePanelWidth = width - 480;
height = getHeight();
cursorPosition = new Rect(width / 2 - 1, 0, width / 2 + 1,
height);
}
if (selectedApp == -1) {
for (int i = 0; i < installedApps.size(); i++) {
freeAllocate.set(installedApps.get(i).x,
installedApps.get(i).pos.top,
installedApps.get(i).x
+
installedApps.get(i).pos.right,
installedApps.get(i).pos.top
+
installedApps.get(i).pos.bottom);
if (Rect.intersects(cursorPosition, freeAllocate)) {
selectedApp = i;
timeSelected = 0;
}
}
}
else {
timeSelected++;
if (timeSelected > selectionTime) {
if (preferences.getBoolean("launch_on_hover", true)) {
if (preferences
.getBoolean("vibrate_on_selection", true))
mVibrator.vibrate(50);
//
installedApps.get(selectedApp).launch();
prepareToLaunch(installedApps.get(selectedApp));
}
}
freeAllocate.set(
installedApps.get(selectedApp).x,
installedApps.get(selectedApp).pos.top,
installedApps.get(selectedApp).x
+
installedApps.get(selectedApp).pos.right,
installedApps.get(selectedApp).pos.top
+
installedApps.get(selectedApp).pos.bottom);
if (!Rect.intersects(cursorPosition, freeAllocate)) {
selectedApp = -1;
timeSelected = -1;
}
}
if (startingApp) {
if (appStartAnimationPosition > width / 2
&&
appStartAnimationPosition > height / 2) {
launchApp();
}
else {
appStartAnimationPosition += 30;
}
}
if (readyToListen /* && !listening
*/
&&
preferences.getBoolean("listen_for_voice", true)) {
listening = true;
speechRecog.startListening(recognizerIntent);
}
// cause redraw
invalidate();
}
public void magnetPull() {
if (volumePanelExpanded) {
// set volume
volumePanelExpanded = false;
AudioManager
audioManager = (AudioManager) getSystemService(Context.AUDIO_SERVICE);
originalVolume = (int) (audioManager
.getStreamMaxVolume(audioManager.STREAM_MUSIC) * (Math
.abs(volumePanelKnobPosition - volumePanelWidth) / (float) volumePanelWidth));
}
else if (selectedApp != -1) {
if (preferences.getBoolean("vibrate_on_selection", true))
mVibrator.vibrate(50);
prepareToLaunch(installedApps.get(selectedApp));
}
else {
// reset rotation
rotationalOffset = headFloats[0];
}
}
float headMatrix[] = new float[16];
float headFloats[] = new float[3];
private void move() {
if (volumePanelExpanded) {
//
volumePanelKnobPosition = (int) (((headFloats[0] -
// rotationalOffset) * 500) /
volumePanelPosition) * 100;
volumePanelKnobPosition = (int) ((gameView.headFloats[0] - rotationalOffset) * 500)
-
volumePanelPosition;
volumePanelKnobPosition = (volumePanelKnobPosition < 0) ? 0
:
volumePanelKnobPosition;
volumePanelKnobPosition = (volumePanelKnobPosition > volumePanelWidth) ? volumePanelWidth
:
volumePanelKnobPosition;
}
for (int i = 0; i < installedApps.size(); i++) {
installedApps.get(i).pos.top = (height / 2)
-
(installedApps.get(i).pos.bottom / 2);
if (rawGyroData == 0.0000f || forceAccelerometer
||
headTracker == null) {
installedApps.get(i).x = (int) (installedApps.get(i).pos.left + (accelData * 100))
-
iconCenter;
}
else {
headTracker.getLastHeadView(headMatrix, 0);
headFloats = getEulerFromMat(headMatrix);
installedApps.get(i).x = (int) (installedApps.get(i).pos.left + ((headFloats[0] - rotationalOffset) * 500))
-
iconCenter;
}
}
}
float normalizeRotation(float rot) {
if (rot > TAU)
rot
-= TAU;
else if (rot < -TAU)
rot
+= TAU;
else
return rot;
return
normalizeRotation(rot);
}
float[] getEulerFromMat(float[] rotMatrix) {
float x, y, z;
float _11, _12,
_13, _14;
float _21, _22, _23, _24;
float _31, _32, _33,
_34;
float _41, _42,
_43, _44;
_11
= rotMatrix[0];
_12
= rotMatrix[1];
_13
= rotMatrix[2];
_14
= rotMatrix[3];
_21
= rotMatrix[4];
_22
= rotMatrix[5];
_23
= rotMatrix[6];
_24
= rotMatrix[7];
_31
= rotMatrix[8];
_32
= rotMatrix[9];
_33
= rotMatrix[10];
_34
= rotMatrix[11];
_41
= rotMatrix[12];
_42
= rotMatrix[13];
_43
= rotMatrix[14];
_44
= rotMatrix[15];
if (_11 == 1.0f) {
x
= (float) Math.atan2(_13,
_34);
y
= 0;
z
= 0;
}
else if (_11 == -1.0f) {
x
= (float) Math.atan2(_13,
_34);
y
= 0;
z
= 0;
}
else {
x
= (float) Math.atan2(-_31,
_11);
y
= (float) Math.asin(_21);
z
= (float) Math.atan2(-_23,
_22);
}
float[] _return = { x, y,
z };
return _return;
}
float[] getMatFromEuler(float[] eulerAngles) {
float x = eulerAngles[0],
y = eulerAngles[1], z = eulerAngles[2];
float _11 = 0, _12
= 0, _13 = 0, _14 = 0;
float _21 = 0, _22
= 0, _23 = 0, _24 = 0;
float _31 = 0, _32
= 0, _33 = 0, _34 = 0;
float _41 = 0, _42
= 0, _43 = 0, _44 = 0;
float transMatrix[] = new float[16];
float X[][] = new float[3][3];
float Y[][] = new float[3][3];
float Z[][] = new float[3][3];
X[2][2]
= (float) Math.cos(x);
X[2][3]
= (float) -Math.sin(x);
X[3][2]
= (float) Math.sin(x);
X[3][3]
= (float) Math.cos(x);
Y[1][1]
= (float) Math.cos(y);
Y[1][3]
= (float) Math.sin(y);
Y[3][1]
= (float) -Math.sin(y);
Y[3][3]
= (float) Math.cos(y);
Z[1][1]
= (float) Math.cos(z);
Z[1][2]
= (float) -Math.sin(z);
Z[2][1]
= (float) Math.sin(z);
Z[2][2]
= (float) Math.cos(z);
float[][] _rotMatrix =
matrixMultiply(matrixMultiply(Z, Y), X);
// int k =
0;
// for (int
i = 0; i < _rotMatrix.length; i++) {
// for (int
j = 0; j < _rotMatrix[i].length; j++) {
// transMatrix[k] =
_rotMatrix[i][j];
// k++;
// }
// transMatrix[k] =
0;
// k++;
// }
// transMatrix[12]
= 0;
// transMatrix[13]
= 0;
// transMatrix[14]
= 0;
// transMatrix[15]
= 1;
transMatrix[0]
= _rotMatrix[0][0];
transMatrix[1]
= _rotMatrix[0][1];
transMatrix[2]
= _rotMatrix[0][2];
transMatrix[3]
= 0;
transMatrix[4]
= _rotMatrix[1][0];
transMatrix[5]
= _rotMatrix[1][1];
transMatrix[6]
= _rotMatrix[1][2];
transMatrix[7]
= 0;
transMatrix[8]
= _rotMatrix[2][0];
transMatrix[9]
= _rotMatrix[2][1];
transMatrix[10]
= _rotMatrix[2][2];
transMatrix[11]
= 0;
transMatrix[12]
= 0;
transMatrix[13]
= 0;
transMatrix[14]
= 0;
transMatrix[15]
= 1;
return transMatrix;
}
public float[][] matrixMultiply(float[][] A, float[][] B) {
int aRows = A.length;
int aColumns = A[0].length;
int bRows = B.length;
int bColumns = B[0].length;
if (aColumns != bRows)
{
throw new IllegalArgumentException("A:Rows:
" +
aColumns
+
"
did not match B:Columns " + bRows + ".");
}
float[][] C = new float[aRows][bColumns];
for (int i = 0; i < 2;
i++) {
for (int j = 0; j < 2;
j++) {
C[i][j]
= 0.00000f;
}
}
for (int i = 0; i < aRows;
i++) { //
aRow
for (int j = 0; j <
bColumns; j++) { // bColumn
for (int k = 0; k <
aColumns; k++) { // aColumn
C[i][j]
+= A[i][k] * B[k][j];
}
}
}
return C;
}
Display
dis = ((WindowManager)
getApplicationContext()
.getSystemService(Context.WINDOW_SERVICE)).getDefaultDisplay();
Bitmap
lEyeBit = Bitmap.createBitmap(dis.getWidth()
/ 2,
dis.getHeight(),
Bitmap.Config.ARGB_8888);
Bitmap
rEyeBit = Bitmap.createBitmap(dis.getWidth()
/ 2,
dis.getHeight(),
Bitmap.Config.ARGB_8888);
Canvas
lEye = new Canvas(lEyeBit);
Canvas
rEye = new Canvas(rEyeBit);
@Override
protected void onDraw(Canvas
canvas) {
super.onDraw(canvas);
int radius;
radius
= 100;
paint.setColor(Color.BLACK);
canvas.drawPaint(paint);
// draw left eye
if (drawWallpaper) {
freeAllocate.set(0, 0, width, height);
lEye.drawBitmap(wallpaper, null, freeAllocate, paint);
}
else {
paint.setColor(Color.BLACK);
lEye.drawPaint(paint);
}
paint.setAntiAlias(true);
paint.setColor(Color.parseColor("#CD5C5C"));
if (preferences.getBoolean("launch_on_hover", true))
lEye.drawCircle(width / 2, height / 2, radius
*
((float) timeSelected / selectionTime), paint);
paint.setStyle(Paint.Style.STROKE);
paint.setStrokeWidth(3);
lEye.drawCircle(width / 2, height / 2, radius, paint);
paint.setStyle(Paint.Style.FILL);
if (startingApp) {
lEye.drawCircle(width / 2, height / 2,
appStartAnimationPosition, paint);
}
for (int i = 0; i < installedApps.size(); i++) {
if (installedApps.get(i).x < width
&&
installedApps.get(i).x
+
installedApps.get(i).pos.right > 0) {
if (i != selectedApp || volumePanelExpanded) {
freeAllocate.set(
installedApps.get(i).x - 1,
installedApps.get(i).pos.top,
installedApps.get(i).x - 1
+
installedApps.get(i).pos.right,
installedApps.get(i).pos.top
+
installedApps.get(i).pos.bottom);
lEye.drawBitmap(installedApps.get(i).iconGry, null,
freeAllocate, paint);
}
}
}
if (volumePanelExpanded) {
paint.setColor(Color.argb(200,
100, 100, 100));
if (100 - (int) ((headFloats[0] - rotationalOffset) * 500) > width / 2 - 25) // overscroll
// to
// the
// right
freeAllocate.set(width / 2 - width - 200 - 25,
height / 2 - 25, width / 2 + 25, height / 2 + 25);
else if (100 + (int) ((headFloats[0] - rotationalOffset) * 500) > width / 2 - 25) // overscroll
// to
// the
// left
freeAllocate.set(width / 2 - 25, height / 2 - 25, width,
height / 2 + 25);
else
freeAllocate
.set(100
+ (int) ((headFloats[0] - rotationalOffset) * 500),
height / 2 - 25,
width
-
100
+
(int) ((headFloats[0] - rotationalOffset) * 500),
height / 2 + 25);
lEye.drawRoundRect(new RectF(freeAllocate), 50f, 50f, paint);
paint.setColor(Color.rgb(0,
100, 255));
if (120 + (int) ((headFloats[0] - rotationalOffset) * 500) > width / 2 - 5)
freeAllocate.set(width / 2 - 5, height / 2 - 5, width,
height / 2 + 5);
else if (width - 120
+
(int) ((headFloats[0] - rotationalOffset) * 500) < width / 2 + 5)
freeAllocate.set(width / 2 - width - 240, height / 2 - 5,
width / 2 - 5, height / 2 + 5);
else
freeAllocate
.set(120
+ (int) ((headFloats[0] - rotationalOffset) * 500),
height / 2 - 5,
width
-
120
+
(int) ((headFloats[0] - rotationalOffset) * 500),
height / 2 + 5);
lEye.drawRect(freeAllocate, paint);
paint.setColor(Color.rgb(0,
50, 255));
lEye.drawCircle(width / 2, height / 2, 14, paint);
}
if (!volumePanelExpanded && selectedApp != -1) {
freeAllocate
.set(installedApps.get(selectedApp).x + 1
+
installedApps.get(selectedApp).z - 14,
installedApps.get(selectedApp).pos.top - 14,
installedApps.get(selectedApp).x
+
1
+
installedApps.get(selectedApp).z
+
installedApps.get(selectedApp).pos.right
+
28,
installedApps.get(selectedApp).pos.top
+
installedApps.get(selectedApp).pos.bottom
+
28);
lEye.drawBitmap(installedApps.get(selectedApp).icon, null,
freeAllocate, paint);
paint.setTextAlign(Paint.Align.CENTER);
paint.setColor(Color.WHITE);
paint.setTextSize(20);
lEye.drawText(
installedApps.get(selectedApp).name,
installedApps.get(selectedApp).x
+
(installedApps.get(selectedApp).pos.right / 2),
installedApps.get(selectedApp).pos.top + 120, paint);
}
// draw right eye
if (drawWallpaper) {
freeAllocate.set(0, 0, width, height);
rEye.drawBitmap(wallpaper, null, freeAllocate, paint);
}
else {
paint.setColor(Color.BLACK);
rEye.drawPaint(paint);
}
paint.setColor(Color.parseColor("#CD5C5C"));
if (preferences.getBoolean("launch_on_hover", true))
rEye.drawCircle(width / 2, height / 2, radius
*
((float) timeSelected / selectionTime), paint);
paint.setStyle(Paint.Style.STROKE);
paint.setStrokeWidth(3);
rEye.drawCircle(width / 2, height / 2, radius, paint);
paint.setStyle(Paint.Style.FILL);
if (startingApp) {
rEye.drawCircle(width / 2, height / 2,
appStartAnimationPosition, paint);
}
for (int i = 0; i < installedApps.size(); i++) {
if (installedApps.get(i).x < width
&&
installedApps.get(i).x
+
installedApps.get(i).pos.right > 0) {
if (i != selectedApp || volumePanelExpanded) {
freeAllocate.set(
installedApps.get(i).x - 1,
installedApps.get(i).pos.top,
installedApps.get(i).x - 1
+
installedApps.get(i).pos.right,
installedApps.get(i).pos.top
+
installedApps.get(i).pos.bottom);
rEye.drawBitmap(installedApps.get(i).iconGry, null,
freeAllocate, paint);
}
}
}
if (volumePanelExpanded) {
paint.setColor(Color.argb(200,
100, 100, 100));
if (100 - (int) ((headFloats[0] - rotationalOffset) * 500) > width / 2 - 25) // overscroll
// to
// the
// right
freeAllocate.set(width / 2 - width - 200 - 25,
height / 2 - 25, width / 2 + 25, height / 2 + 25);
else if (100 + (int) ((headFloats[0] - rotationalOffset) * 500) > width / 2 - 25) // overscroll
// to
// the
// left
freeAllocate.set(width / 2 - 25, height / 2 - 25, width,
height / 2 + 25);
else
freeAllocate
.set(100
+ (int) ((headFloats[0] - rotationalOffset) * 500),
height / 2 - 25,
width
- 100
+
(int) ((headFloats[0] - rotationalOffset) * 500),
height / 2 + 25);
rEye.drawRoundRect(new RectF(freeAllocate), 50f, 50f, paint);
paint.setColor(Color.rgb(0,
100, 255));
if (120 + (int) ((headFloats[0] - rotationalOffset) * 500) > width / 2 - 5)
freeAllocate.set(width / 2 - 5, height / 2 - 5, width,
height / 2 + 5);
else if (width - 120
+
(int) ((headFloats[0] - rotationalOffset) * 500) < width / 2 + 5)
freeAllocate.set(width / 2 - width - 240, height / 2 - 5,
width / 2 - 5, height / 2 + 5);
else
freeAllocate
.set(120
+ (int) ((headFloats[0] - rotationalOffset) * 500),
height / 2 - 5,
width
-
120
+
(int) ((headFloats[0] - rotationalOffset) * 500),
height / 2 + 5);
rEye.drawRect(freeAllocate, paint);
paint.setColor(Color.rgb(0,
50, 255));
rEye.drawCircle(width / 2, height / 2, 14, paint);
}
if (!volumePanelExpanded && selectedApp != -1) {
freeAllocate
.set(installedApps.get(selectedApp).x - 1
-
installedApps.get(selectedApp).z - 14 - 4,
installedApps.get(selectedApp).pos.top - 14,
installedApps.get(selectedApp).x
-
1
-
installedApps.get(selectedApp).z
+
installedApps.get(selectedApp).pos.right
+
28,
installedApps.get(selectedApp).pos.top
+
installedApps.get(selectedApp).pos.bottom
+
28);
rEye.drawBitmap(installedApps.get(selectedApp).icon, null,
freeAllocate, paint);
paint.setTextAlign(Paint.Align.CENTER);
paint.setColor(Color.WHITE);
paint.setTextSize(20);
rEye.drawText(
installedApps.get(selectedApp).name,
installedApps.get(selectedApp).x
+
(installedApps.get(selectedApp).pos.right / 2),
installedApps.get(selectedApp).pos.top + 120, paint);
}
// render eyes to
screen
float pupilaryDist = ((100
- preferences.getInt(
"interpupilary_distance", 50)) / 50) *
0.0635f; //
this
should be the distance between the user's pupils in meters -- 0.0635m is average
int shiftImage = (int) (pupilaryDist *
1023.6220472); // for the
// OPO, this needs to equal 65
// print("An interpupilary
distance of " + pupilaryDist +
// " is equal
to " + shiftImage * 2 + " pixels.");
canvas.drawBitmap(lEyeBit, new Rect(0, 0, width, height), new Rect(
shiftImage,
0, width + shiftImage, height), paint);
canvas.drawBitmap(rEyeBit, new Rect(shiftImage, 0, width, height),
new Rect(width, 0, getWidth() -
shiftImage, height), paint);
// draw divider
paint.setColor(Color.GRAY);
canvas.drawRect(width - 5, 0, width + 5, height, paint);
if (appStartAnimationPosition > 0) {
paint.setColor(Color.BLACK);
paint.setAlpha((int) ((appStartAnimationPosition / (height / 2f)) * 255f));
canvas.drawPaint(paint);
}
gameLoop();
}
@Override
public boolean onTouchEvent(MotionEvent
motionEvent) {
magnetPull();
return false;
}
}
}
This comment has been removed by the author.
ReplyDeletei want to create same menu in unity3d with google cardboard can you help me on this?
ReplyDeleteHi Thanks for the code.How can I achieve to rotate de icons and text?
ReplyDeleteen mexico http://www.visorcardboardmexico.com.mx/
ReplyDelete