[Android] Add Controller support
|
@ -43,6 +43,16 @@
|
|||
android:label="@string/ScanRomsActivity_title"
|
||||
android:theme="@style/Theme.AppCompat" >
|
||||
</activity>
|
||||
<activity
|
||||
android:name="emu.project64.profile.ControllerProfileActivity"
|
||||
android:exported="false"
|
||||
android:label="@string/ControllerProfileActivity_title"
|
||||
android:theme="@style/Theme.Project64.Apearance" >
|
||||
<intent-filter>
|
||||
<action android:name=".profile.ControllerProfileActivity" />
|
||||
<category android:name="android.intent.category.DEFAULT" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
|
||||
<!--
|
||||
For the GameActivities, do not restart the activity when the phone's slider
|
||||
|
|
After Width: | Height: | Size: 1.4 KiB |
After Width: | Height: | Size: 1.4 KiB |
After Width: | Height: | Size: 877 B |
After Width: | Height: | Size: 1.3 KiB |
After Width: | Height: | Size: 1.2 KiB |
After Width: | Height: | Size: 1.2 KiB |
After Width: | Height: | Size: 617 B |
After Width: | Height: | Size: 1.2 KiB |
After Width: | Height: | Size: 1.7 KiB |
After Width: | Height: | Size: 1.7 KiB |
After Width: | Height: | Size: 1.1 KiB |
After Width: | Height: | Size: 1.5 KiB |
|
@ -0,0 +1,11 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:shape="oval" >
|
||||
<gradient
|
||||
android:startColor="@color/gray1"
|
||||
android:endColor="@color/gray2"
|
||||
android:angle="90" />
|
||||
<stroke
|
||||
android:width="6dp"
|
||||
android:color="@color/gray2" />
|
||||
</shape>
|
|
@ -0,0 +1,43 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<selector
|
||||
xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
|
||||
<item android:state_pressed="true" >
|
||||
<shape
|
||||
android:shape="oval" >
|
||||
<gradient
|
||||
android:startColor="@color/holo_blue1"
|
||||
android:endColor="@color/holo_blue2"
|
||||
android:angle="90" />
|
||||
<stroke
|
||||
android:width="3dp"
|
||||
android:color="@color/holo_blue2" />
|
||||
</shape>
|
||||
</item>
|
||||
|
||||
<item android:state_focused="true" >
|
||||
<shape
|
||||
android:shape="oval" >
|
||||
<gradient
|
||||
android:startColor="@color/holo_purple1"
|
||||
android:endColor="@color/holo_purple2"
|
||||
android:angle="90" />
|
||||
<stroke
|
||||
android:width="3dp"
|
||||
android:color="@color/holo_purple2" />
|
||||
</shape>
|
||||
</item>
|
||||
|
||||
<item>
|
||||
<shape
|
||||
android:shape="oval" >
|
||||
<gradient
|
||||
android:startColor="@color/blue1"
|
||||
android:endColor="@color/blue2"
|
||||
android:angle="90" />
|
||||
<stroke
|
||||
android:width="3dp"
|
||||
android:color="@color/blue2" />
|
||||
</shape>
|
||||
</item>
|
||||
</selector>
|
|
@ -0,0 +1,44 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<selector
|
||||
xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
|
||||
<item android:state_pressed="true" >
|
||||
<shape
|
||||
android:shape="rectangle" >
|
||||
<gradient
|
||||
android:startColor="@color/holo_blue1"
|
||||
android:endColor="@color/holo_blue2"
|
||||
android:angle="90" />
|
||||
<stroke
|
||||
android:width="3dp"
|
||||
android:color="@color/holo_blue2" />
|
||||
<corners
|
||||
android:radius="20dp" />
|
||||
</shape>
|
||||
</item>
|
||||
|
||||
<item android:state_focused="true" >
|
||||
<shape
|
||||
android:shape="rectangle" >
|
||||
<gradient
|
||||
android:startColor="@color/holo_purple1"
|
||||
android:endColor="@color/holo_purple2"
|
||||
android:angle="90" />
|
||||
<stroke
|
||||
android:width="3dp"
|
||||
android:color="@color/holo_purple2" />
|
||||
<corners
|
||||
android:radius="20dp" />
|
||||
</shape>
|
||||
</item>
|
||||
|
||||
<item>
|
||||
<shape
|
||||
android:shape="rectangle" >
|
||||
<solid
|
||||
android:color="@android:color/transparent" />
|
||||
<corners
|
||||
android:radius="20dp" />
|
||||
</shape>
|
||||
</item>
|
||||
</selector>
|
|
@ -0,0 +1,43 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<selector
|
||||
xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
|
||||
<item android:state_pressed="true" >
|
||||
<shape
|
||||
android:shape="oval" >
|
||||
<gradient
|
||||
android:startColor="@color/holo_blue1"
|
||||
android:endColor="@color/holo_blue2"
|
||||
android:angle="90" />
|
||||
<stroke
|
||||
android:width="3dp"
|
||||
android:color="@color/holo_blue2" />
|
||||
</shape>
|
||||
</item>
|
||||
|
||||
<item android:state_focused="true" >
|
||||
<shape
|
||||
android:shape="oval" >
|
||||
<gradient
|
||||
android:startColor="@color/holo_purple1"
|
||||
android:endColor="@color/holo_purple2"
|
||||
android:angle="90" />
|
||||
<stroke
|
||||
android:width="3dp"
|
||||
android:color="@color/holo_purple2" />
|
||||
</shape>
|
||||
</item>
|
||||
|
||||
<item>
|
||||
<shape
|
||||
android:shape="oval" >
|
||||
<gradient
|
||||
android:startColor="@color/green1"
|
||||
android:endColor="@color/green2"
|
||||
android:angle="90" />
|
||||
<stroke
|
||||
android:width="3dp"
|
||||
android:color="@color/green2" />
|
||||
</shape>
|
||||
</item>
|
||||
</selector>
|
|
@ -0,0 +1,43 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<selector
|
||||
xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
|
||||
<item android:state_pressed="true" >
|
||||
<shape
|
||||
android:shape="oval" >
|
||||
<gradient
|
||||
android:startColor="@color/holo_blue1"
|
||||
android:endColor="@color/holo_blue2"
|
||||
android:angle="90" />
|
||||
<stroke
|
||||
android:width="3dp"
|
||||
android:color="@color/holo_blue2" />
|
||||
</shape>
|
||||
</item>
|
||||
|
||||
<item android:state_focused="true" >
|
||||
<shape
|
||||
android:shape="oval" >
|
||||
<gradient
|
||||
android:startColor="@color/holo_purple1"
|
||||
android:endColor="@color/holo_purple2"
|
||||
android:angle="90" />
|
||||
<stroke
|
||||
android:width="3dp"
|
||||
android:color="@color/holo_purple2" />
|
||||
</shape>
|
||||
</item>
|
||||
|
||||
<item>
|
||||
<shape
|
||||
android:shape="oval" >
|
||||
<gradient
|
||||
android:startColor="@color/yellow1"
|
||||
android:endColor="@color/yellow2"
|
||||
android:angle="90" />
|
||||
<stroke
|
||||
android:width="3dp"
|
||||
android:color="@color/yellow2" />
|
||||
</shape>
|
||||
</item>
|
||||
</selector>
|
|
@ -0,0 +1,49 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<selector
|
||||
xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
|
||||
<item android:state_pressed="true" >
|
||||
<shape
|
||||
android:shape="rectangle" >
|
||||
<gradient
|
||||
android:startColor="@color/holo_blue1"
|
||||
android:endColor="@color/holo_blue2"
|
||||
android:angle="90" />
|
||||
<stroke
|
||||
android:width="3dp"
|
||||
android:color="@color/holo_blue2" />
|
||||
<corners
|
||||
android:radius="4dp" />
|
||||
</shape>
|
||||
</item>
|
||||
|
||||
<item android:state_focused="true" >
|
||||
<shape
|
||||
android:shape="rectangle" >
|
||||
<gradient
|
||||
android:startColor="@color/holo_purple1"
|
||||
android:endColor="@color/holo_purple2"
|
||||
android:angle="90" />
|
||||
<stroke
|
||||
android:width="3dp"
|
||||
android:color="@color/holo_purple2" />
|
||||
<corners
|
||||
android:radius="4dp" />
|
||||
</shape>
|
||||
</item>
|
||||
|
||||
<item>
|
||||
<shape
|
||||
android:shape="rectangle" >
|
||||
<gradient
|
||||
android:startColor="@color/gray3"
|
||||
android:endColor="@color/gray4"
|
||||
android:angle="90" />
|
||||
<stroke
|
||||
android:width="3dp"
|
||||
android:color="@color/gray4" />
|
||||
<corners
|
||||
android:radius="4dp" />
|
||||
</shape>
|
||||
</item>
|
||||
</selector>
|
|
@ -0,0 +1,43 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<selector
|
||||
xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
|
||||
<item android:state_pressed="true" >
|
||||
<shape
|
||||
android:shape="oval" >
|
||||
<gradient
|
||||
android:startColor="@color/holo_blue1"
|
||||
android:endColor="@color/holo_blue2"
|
||||
android:angle="90" />
|
||||
<stroke
|
||||
android:width="3dp"
|
||||
android:color="@color/holo_blue2" />
|
||||
</shape>
|
||||
</item>
|
||||
|
||||
<item android:state_focused="true" >
|
||||
<shape
|
||||
android:shape="oval" >
|
||||
<gradient
|
||||
android:startColor="@color/holo_purple1"
|
||||
android:endColor="@color/holo_purple2"
|
||||
android:angle="90" />
|
||||
<stroke
|
||||
android:width="3dp"
|
||||
android:color="@color/holo_purple2" />
|
||||
</shape>
|
||||
</item>
|
||||
|
||||
<item>
|
||||
<shape
|
||||
android:shape="oval" >
|
||||
<gradient
|
||||
android:startColor="@color/red1"
|
||||
android:endColor="@color/red2"
|
||||
android:angle="90" />
|
||||
<stroke
|
||||
android:width="3dp"
|
||||
android:color="@color/red2" />
|
||||
</shape>
|
||||
</item>
|
||||
</selector>
|
|
@ -0,0 +1,64 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:id="@+id/aPad"
|
||||
android:layout_width="@dimen/inputMapPreferenceButtonSizeX3"
|
||||
android:layout_height="@dimen/inputMapPreferenceButtonSizeX3"
|
||||
android:background="@drawable/analog_ring"
|
||||
android:focusable="false"
|
||||
android:focusableInTouchMode="false" >
|
||||
|
||||
<Button
|
||||
android:id="@+id/buttonAC"
|
||||
style="@style/btnMappable"
|
||||
android:layout_width="@dimen/inputMapPreferenceButtonSizeX1_5"
|
||||
android:layout_height="@dimen/inputMapPreferenceButtonSizeX1_5"
|
||||
android:layout_centerInParent="true"
|
||||
android:background="@drawable/analog_ring"
|
||||
android:enabled="false"
|
||||
android:focusable="false"
|
||||
android:focusableInTouchMode="false" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/buttonAU"
|
||||
style="@style/btnMappableNoText"
|
||||
android:layout_height="@dimen/inputMapPreferenceButtonSizeX1_5"
|
||||
android:layout_alignParentTop="true"
|
||||
android:layout_centerHorizontal="true"
|
||||
android:background="@drawable/button_apad"
|
||||
android:drawableTop="@drawable/ic_arrow_u"
|
||||
android:text="@string/inputMapActivity_btnAU" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/buttonAD"
|
||||
style="@style/btnMappableNoText"
|
||||
android:layout_height="@dimen/inputMapPreferenceButtonSizeX1_5"
|
||||
android:layout_alignParentBottom="true"
|
||||
android:layout_centerHorizontal="true"
|
||||
android:background="@drawable/button_apad"
|
||||
android:drawableBottom="@drawable/ic_arrow_d"
|
||||
android:text="@string/inputMapActivity_btnAD" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/buttonAL"
|
||||
style="@style/btnMappableNoText"
|
||||
android:layout_width="@dimen/inputMapPreferenceButtonSizeX1_5"
|
||||
android:layout_alignParentLeft="true"
|
||||
android:layout_centerVertical="true"
|
||||
android:background="@drawable/button_apad"
|
||||
android:drawableLeft="@drawable/ic_arrow_l"
|
||||
android:text="@string/inputMapActivity_btnAL"
|
||||
tools:ignore="RtlHardcoded" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/buttonAR"
|
||||
style="@style/btnMappableNoText"
|
||||
android:layout_width="@dimen/inputMapPreferenceButtonSizeX1_5"
|
||||
android:layout_alignParentRight="true"
|
||||
android:layout_centerVertical="true"
|
||||
android:background="@drawable/button_apad"
|
||||
android:drawableRight="@drawable/ic_arrow_r"
|
||||
android:text="@string/inputMapActivity_btnAR"
|
||||
tools:ignore="RtlHardcoded" />
|
||||
|
||||
</RelativeLayout>
|
|
@ -0,0 +1,41 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:id="@+id/bPad"
|
||||
android:layout_width="@dimen/inputMapPreferenceButtonSizeX5"
|
||||
android:layout_height="@dimen/inputMapPreferenceButtonSizeX3"
|
||||
android:focusable="false"
|
||||
android:focusableInTouchMode="false" >
|
||||
|
||||
<Button
|
||||
android:id="@+id/buttonZ"
|
||||
style="@style/btnMappable"
|
||||
android:layout_alignParentTop="true"
|
||||
android:layout_centerHorizontal="true"
|
||||
android:text="@string/inputMapActivity_btnZ" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/buttonR"
|
||||
style="@style/btnMappable"
|
||||
android:layout_alignParentRight="true"
|
||||
android:layout_alignParentTop="true"
|
||||
android:text="@string/inputMapActivity_btnR"
|
||||
tools:ignore="RtlHardcoded" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/buttonL"
|
||||
style="@style/btnMappable"
|
||||
android:layout_alignParentLeft="true"
|
||||
android:layout_alignParentTop="true"
|
||||
android:text="@string/inputMapActivity_btnL"
|
||||
tools:ignore="RtlHardcoded" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/buttonS"
|
||||
style="@style/btnMappable"
|
||||
android:layout_alignParentBottom="true"
|
||||
android:layout_centerHorizontal="true"
|
||||
android:background="@drawable/button_start"
|
||||
android:text="@string/inputMapActivity_btnS" />
|
||||
|
||||
</RelativeLayout>
|
|
@ -0,0 +1,59 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:id="@+id/cPad"
|
||||
android:layout_width="@dimen/inputMapPreferenceButtonSizeX3"
|
||||
android:layout_height="@dimen/inputMapPreferenceButtonSizeX3"
|
||||
android:focusable="false"
|
||||
android:focusableInTouchMode="false" >
|
||||
|
||||
<Button
|
||||
android:id="@+id/buttonCU"
|
||||
style="@style/btnMappableNoText"
|
||||
android:layout_alignParentTop="true"
|
||||
android:layout_centerHorizontal="true"
|
||||
android:background="@drawable/button_cpad"
|
||||
android:drawableTop="@drawable/ic_arrow_u"
|
||||
android:text="@string/inputMapActivity_btnCU" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/buttonCD"
|
||||
style="@style/btnMappableNoText"
|
||||
android:layout_alignParentBottom="true"
|
||||
android:layout_centerHorizontal="true"
|
||||
android:background="@drawable/button_cpad"
|
||||
android:drawableBottom="@drawable/ic_arrow_d"
|
||||
android:text="@string/inputMapActivity_btnCD" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/buttonCL"
|
||||
style="@style/btnMappableNoText"
|
||||
android:layout_alignParentLeft="true"
|
||||
android:layout_centerVertical="true"
|
||||
android:background="@drawable/button_cpad"
|
||||
android:drawableLeft="@drawable/ic_arrow_l"
|
||||
android:text="@string/inputMapActivity_btnCL"
|
||||
tools:ignore="RtlHardcoded" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/buttonCR"
|
||||
style="@style/btnMappableNoText"
|
||||
android:layout_alignParentRight="true"
|
||||
android:layout_centerVertical="true"
|
||||
android:background="@drawable/button_cpad"
|
||||
android:drawableRight="@drawable/ic_arrow_r"
|
||||
android:text="@string/inputMapActivity_btnCR"
|
||||
tools:ignore="RtlHardcoded" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/buttonCC"
|
||||
style="@style/btnMappable"
|
||||
android:layout_centerInParent="true"
|
||||
android:background="@android:color/transparent"
|
||||
android:enabled="false"
|
||||
android:focusable="false"
|
||||
android:focusableInTouchMode="false"
|
||||
android:text="@string/inputMapActivity_btnC"
|
||||
tools:ignore="RtlHardcoded" />
|
||||
|
||||
</RelativeLayout>
|
|
@ -0,0 +1,134 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="vertical"
|
||||
android:focusable="false"
|
||||
android:focusableInTouchMode="false"
|
||||
>
|
||||
<android.support.v7.widget.Toolbar
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:id="@+id/toolbar"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="?attr/actionBarSize"
|
||||
android:background="?attr/background_actionbar"
|
||||
android:theme="@style/Project64.Toolbar" />
|
||||
|
||||
<!--
|
||||
***********************************************************************************
|
||||
IMPORTANT:
|
||||
|
||||
Except for the root view, all views in this file MUST set these flags to FALSE:
|
||||
|
||||
android:focusable="false"
|
||||
android:focusableInTouchMode="false"
|
||||
|
||||
This allows focus to stay in the root view at all times. This is critical because
|
||||
the root view is the only view that listens to input from gamepads, keyboards, etc.
|
||||
***********************************************************************************
|
||||
-->
|
||||
<ScrollView
|
||||
android:id="@+id/inputMapPreference"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:fillViewport="true"
|
||||
android:focusable="true"
|
||||
android:focusableInTouchMode="true"
|
||||
android:padding="5dp" >
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:focusable="false"
|
||||
android:focusableInTouchMode="false"
|
||||
android:gravity="center"
|
||||
android:orientation="vertical" >
|
||||
|
||||
<RelativeLayout
|
||||
android:id="@+id/all_pads"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:focusable="false"
|
||||
android:focusableInTouchMode="false" >
|
||||
|
||||
<include
|
||||
android:id="@+id/include_b_pad"
|
||||
android:layout_width="@dimen/inputMapPreferenceButtonSizeX5"
|
||||
android:layout_height="@dimen/inputMapPreferenceButtonSizeX3"
|
||||
android:layout_alignParentTop="true"
|
||||
android:layout_centerHorizontal="true"
|
||||
layout="@layout/b_pad" />
|
||||
|
||||
<include
|
||||
android:id="@+id/include_d_pad"
|
||||
android:layout_width="@dimen/inputMapPreferenceButtonSizeX3"
|
||||
android:layout_height="@dimen/inputMapPreferenceButtonSizeX3"
|
||||
android:layout_alignTop="@+id/include_b_pad"
|
||||
android:layout_marginTop="@dimen/inputMapPreferenceButtonSize"
|
||||
android:layout_toLeftOf="@+id/include_b_pad"
|
||||
layout="@layout/d_pad"
|
||||
tools:ignore="RtlHardcoded" />
|
||||
|
||||
<include
|
||||
android:id="@+id/include_c_pad"
|
||||
android:layout_width="@dimen/inputMapPreferenceButtonSizeX3"
|
||||
android:layout_height="@dimen/inputMapPreferenceButtonSizeX3"
|
||||
android:layout_alignTop="@+id/include_b_pad"
|
||||
android:layout_marginTop="@dimen/inputMapPreferenceButtonSize"
|
||||
android:layout_toRightOf="@+id/include_b_pad"
|
||||
layout="@layout/c_pad"
|
||||
tools:ignore="RtlHardcoded" />
|
||||
|
||||
<include
|
||||
android:id="@+id/include_a_pad"
|
||||
android:layout_width="@dimen/inputMapPreferenceButtonSizeX3"
|
||||
android:layout_height="@dimen/inputMapPreferenceButtonSizeX3"
|
||||
android:layout_below="@id/include_b_pad"
|
||||
android:layout_centerHorizontal="true"
|
||||
android:layout_marginTop="@dimen/inputMapPreferenceButtonSize"
|
||||
layout="@layout/a_pad" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/buttonB"
|
||||
style="@style/btnMappable"
|
||||
android:layout_alignBottom="@+id/include_c_pad"
|
||||
android:layout_toLeftOf="@+id/include_c_pad"
|
||||
android:background="@drawable/button_b"
|
||||
android:text="@string/inputMapActivity_btnB"
|
||||
tools:ignore="RtlHardcoded" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/buttonA"
|
||||
style="@style/btnMappable"
|
||||
android:layout_below="@+id/buttonB"
|
||||
android:layout_toRightOf="@+id/buttonB"
|
||||
android:background="@drawable/button_a"
|
||||
android:text="@string/inputMapActivity_btnA"
|
||||
tools:ignore="RtlHardcoded" />
|
||||
</RelativeLayout>
|
||||
|
||||
<EditText
|
||||
android:id="@+id/dummyImeListener"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="0dp"
|
||||
android:inputType="text"
|
||||
android:visibility="invisible" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/textFeedback"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="@dimen/inputMapPreferenceButtonSize"
|
||||
android:focusable="false"
|
||||
android:focusableInTouchMode="false"
|
||||
android:text="@string/inputMapActivity_dummyFeedback"
|
||||
android:textAppearance="?android:attr/textAppearanceMedium"
|
||||
android:textColor="@android:color/primary_text_dark_nodisable" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</ScrollView>
|
||||
</LinearLayout>
|
|
@ -0,0 +1,118 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="vertical"
|
||||
android:focusable="false"
|
||||
android:focusableInTouchMode="false"
|
||||
>
|
||||
<android.support.v7.widget.Toolbar
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:id="@+id/toolbar"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="?attr/actionBarSize"
|
||||
android:background="?attr/background_actionbar"
|
||||
android:theme="@style/Project64.Toolbar" />
|
||||
|
||||
<!--
|
||||
***********************************************************************************
|
||||
IMPORTANT:
|
||||
|
||||
Except for the root view, all views in this file MUST set these flags to FALSE:
|
||||
|
||||
android:focusable="false"
|
||||
android:focusableInTouchMode="false"
|
||||
|
||||
This allows focus to stay in the root view at all times. This is critical because
|
||||
the root view is the only view that listens to input from gamepads, keyboards, etc.
|
||||
***********************************************************************************
|
||||
-->
|
||||
<ScrollView
|
||||
android:id="@+id/inputMapPreference"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:fillViewport="true"
|
||||
android:focusable="true"
|
||||
android:focusableInTouchMode="true"
|
||||
android:padding="5dp" >
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:focusable="false"
|
||||
android:focusableInTouchMode="false"
|
||||
android:gravity="center"
|
||||
android:orientation="vertical" >
|
||||
|
||||
<include
|
||||
android:id="@+id/include_b_pad"
|
||||
android:layout_width="@dimen/inputMapPreferenceButtonSizeX5"
|
||||
android:layout_height="@dimen/inputMapPreferenceButtonSizeX3"
|
||||
layout="@layout/b_pad" />
|
||||
|
||||
<include
|
||||
android:id="@+id/include_d_pad"
|
||||
android:layout_width="@dimen/inputMapPreferenceButtonSizeX3"
|
||||
android:layout_height="@dimen/inputMapPreferenceButtonSizeX3"
|
||||
android:layout_marginTop="@dimen/inputMapPreferenceButtonSize"
|
||||
layout="@layout/d_pad" />
|
||||
|
||||
<include
|
||||
android:id="@+id/include_a_pad"
|
||||
android:layout_width="@dimen/inputMapPreferenceButtonSizeX3"
|
||||
android:layout_height="@dimen/inputMapPreferenceButtonSizeX3"
|
||||
android:layout_marginTop="@dimen/inputMapPreferenceButtonSize"
|
||||
layout="@layout/a_pad" />
|
||||
|
||||
<include
|
||||
android:id="@+id/include_c_pad"
|
||||
android:layout_width="@dimen/inputMapPreferenceButtonSizeX3"
|
||||
android:layout_height="@dimen/inputMapPreferenceButtonSizeX3"
|
||||
android:layout_marginTop="@dimen/inputMapPreferenceButtonSize"
|
||||
layout="@layout/c_pad" />
|
||||
|
||||
<TableRow
|
||||
android:id="@+id/tableRowFaceButtons"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="@dimen/inputMapPreferenceButtonSize" >
|
||||
|
||||
<Button
|
||||
android:id="@+id/buttonB"
|
||||
style="@style/btnMappable"
|
||||
android:background="@drawable/button_b"
|
||||
android:text="@string/inputMapActivity_btnB" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/buttonA"
|
||||
style="@style/btnMappable"
|
||||
android:layout_marginLeft="@dimen/inputMapPreferenceButtonSize"
|
||||
android:background="@drawable/button_a"
|
||||
android:text="@string/inputMapActivity_btnA"
|
||||
tools:ignore="RtlHardcoded" />
|
||||
</TableRow>
|
||||
|
||||
<EditText
|
||||
android:id="@+id/dummyImeListener"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="0dp"
|
||||
android:inputType="text"
|
||||
android:visibility="invisible" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/textFeedback"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="@dimen/inputMapPreferenceButtonSize"
|
||||
android:focusable="false"
|
||||
android:focusableInTouchMode="false"
|
||||
android:text="@string/inputMapActivity_dummyFeedback"
|
||||
android:textAppearance="?android:attr/textAppearanceMedium"
|
||||
android:textColor="@android:color/primary_text_dark_nodisable" />
|
||||
|
||||
</LinearLayout>
|
||||
</ScrollView>
|
||||
</LinearLayout>
|
|
@ -0,0 +1,52 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:id="@+id/dPad"
|
||||
android:layout_width="@dimen/inputMapPreferenceButtonSizeX3"
|
||||
android:layout_height="@dimen/inputMapPreferenceButtonSizeX3"
|
||||
android:focusable="false"
|
||||
android:focusableInTouchMode="false" >
|
||||
|
||||
<Button
|
||||
android:id="@+id/buttonDU"
|
||||
style="@style/btnMappableNoText"
|
||||
android:layout_alignParentTop="true"
|
||||
android:layout_centerHorizontal="true"
|
||||
android:drawableTop="@drawable/ic_arrow_u"
|
||||
android:text="@string/inputMapActivity_btnDU" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/buttonDD"
|
||||
style="@style/btnMappableNoText"
|
||||
android:layout_alignParentBottom="true"
|
||||
android:layout_centerHorizontal="true"
|
||||
android:drawableBottom="@drawable/ic_arrow_d"
|
||||
android:text="@string/inputMapActivity_btnDD" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/buttonDL"
|
||||
style="@style/btnMappableNoText"
|
||||
android:layout_alignParentLeft="true"
|
||||
android:layout_centerVertical="true"
|
||||
android:drawableLeft="@drawable/ic_arrow_l"
|
||||
android:text="@string/inputMapActivity_btnDL"
|
||||
tools:ignore="RtlHardcoded" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/buttonDR"
|
||||
style="@style/btnMappableNoText"
|
||||
android:layout_alignParentRight="true"
|
||||
android:layout_centerVertical="true"
|
||||
android:drawableRight="@drawable/ic_arrow_r"
|
||||
android:text="@string/inputMapActivity_btnDR"
|
||||
tools:ignore="RtlHardcoded" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/buttonDC"
|
||||
style="@style/btnMappable"
|
||||
android:layout_centerInParent="true"
|
||||
android:enabled="false"
|
||||
android:focusable="false"
|
||||
android:focusableInTouchMode="false" />
|
||||
|
||||
</RelativeLayout>
|
|
@ -0,0 +1,21 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="vertical"
|
||||
android:padding="10dp" >
|
||||
|
||||
<TextView
|
||||
android:id="@+id/textFeedback"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="center_horizontal"
|
||||
android:textAppearance="?android:attr/textAppearanceLarge"
|
||||
android:textIsSelectable="false" />
|
||||
|
||||
<SeekBar
|
||||
android:id="@+id/seekbar"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content" />
|
||||
|
||||
</LinearLayout>
|
|
@ -0,0 +1,37 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:orientation="horizontal"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:padding="5dp">
|
||||
<RadioButton
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_weight="0"
|
||||
android:layout_gravity="center_vertical"
|
||||
android:id="@+id/two_lines_list_view_row_radiobtn">
|
||||
</RadioButton>
|
||||
<LinearLayout
|
||||
android:orientation="vertical"
|
||||
android:layout_width="0dip"
|
||||
android:layout_height="fill_parent"
|
||||
android:layout_weight="1"
|
||||
android:padding="0dip"
|
||||
>
|
||||
<TextView
|
||||
android:id="@+id/two_lines_list_view_row_text"
|
||||
style="?android:attr/textAppearanceMedium"
|
||||
android:gravity="center_vertical"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
/>
|
||||
<TextView
|
||||
android:id="@+id/two_lines_list_view_row_subtext"
|
||||
style="?android:attr/textAppearanceSmall"
|
||||
android:gravity="center_vertical"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content" />
|
||||
</LinearLayout>
|
||||
|
||||
</LinearLayout>
|
|
@ -6,4 +6,13 @@
|
|||
<attr name="font_actionbar" format="reference|color" />
|
||||
<attr name="font_actionbar_selected" format="reference|color" />
|
||||
<attr name="textMenuColor" format="reference|color" />
|
||||
<declare-styleable name="SeekBarPreference">
|
||||
<attr name="minimumValue" format="integer" />
|
||||
<attr name="maximumValue" format="integer" />
|
||||
<attr name="stepSize" format="integer" />
|
||||
<attr name="units" format="string" />
|
||||
</declare-styleable>
|
||||
<declare-styleable name="TwoLinesListPreference">
|
||||
<attr name="entriesSubtitles" format="string"/>
|
||||
</declare-styleable>
|
||||
</resources>
|
|
@ -3,7 +3,18 @@
|
|||
|
||||
<color name="black">#000001</color>
|
||||
<color name="white">#ffffff</color>
|
||||
|
||||
<color name="yellow1">#F8E334</color>
|
||||
<color name="yellow2">#F29620</color>
|
||||
<color name="red1">#F1001C</color>
|
||||
<color name="red2">#A70014</color>
|
||||
<color name="green1">#2F9454</color>
|
||||
<color name="green2">#27512B</color>
|
||||
<color name="blue1">#3249BC</color>
|
||||
<color name="blue2">#262F7D</color>
|
||||
<color name="gray1">#E5E5E5</color>
|
||||
<color name="gray2">#9C9897</color>
|
||||
<color name="gray3">#747273</color>
|
||||
<color name="gray4">#424042</color>
|
||||
<color name="grey50">#fafafa</color>
|
||||
<color name="grey100">#f5f5f5</color>
|
||||
<color name="grey200">#eeeeee</color>
|
||||
|
@ -16,5 +27,9 @@
|
|||
<color name="grey850">#323232</color>
|
||||
<color name="grey875">#2a2a2a</color>
|
||||
<color name="grey900">#212121</color>
|
||||
<color name="holo_blue1">#33B5E5</color>
|
||||
<color name="holo_blue2">#0099CC</color>
|
||||
<color name="holo_purple1">#AA66CC</color>
|
||||
<color name="holo_purple2">#9933CC</color>
|
||||
|
||||
</resources>
|
|
@ -1,9 +1,12 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<dimen name="inputMapPreferenceButtonSize">40dp</dimen>
|
||||
<dimen name="inputMapPreferenceButtonSizeX1_5">60dp</dimen>
|
||||
<dimen name="inputMapPreferenceButtonSizeX3">120dp</dimen>
|
||||
<dimen name="inputMapPreferenceButtonSizeX5">200dp</dimen>
|
||||
|
||||
<dimen name="galleryImageWidth">450dp</dimen>
|
||||
<dimen name="galleryImageHeight">15dp</dimen> <!-- galleryImageWidth * 177 / 256 -->
|
||||
<dimen name="galleryImageHeight">15dp</dimen>
|
||||
<dimen name="galleryHalfSpacing">5dp</dimen>
|
||||
<dimen name="actionBarSize">56dp</dimen>
|
||||
|
||||
</resources>
|
||||
|
|
|
@ -5,11 +5,13 @@
|
|||
<string name="app_name" translatable="false">Project64</string>
|
||||
<string name="assetExtractor_uriHelp" translatable="false">www.pj64-emu.com</string>
|
||||
<!-- Strings only seen in the eclipse editor -->
|
||||
<string name="inputMapActivity_dummyFeedback" translatable="false">KEYCODE_XXXX</string>
|
||||
<string name="galleryItemAdapter_dummyText" translatable="false">Good Name Goes Here</string>
|
||||
|
||||
<!-- Activity titles, aliased to other strings -->
|
||||
<string name="SplashActivity_title" translatable="false">@string/app_name</string>
|
||||
<string name="GalleryActivity_title" translatable="false">@string/app_name</string>
|
||||
<string name="ControllerProfileActivity_title" translatable="false">@string/app_name</string>
|
||||
<string name="ScanRomsActivity_title" translatable="false">@string/app_name</string>
|
||||
<string name="GameActivity_title" translatable="false">@string/app_name</string>
|
||||
|
||||
|
|
|
@ -66,9 +66,37 @@
|
|||
<string name="galleryRecentlyPlayed">Recently played</string>
|
||||
<string name="galleryLibrary">Games</string>
|
||||
|
||||
<!-- Seek Bar Preference -->
|
||||
<string name="seekBarPreference_summary">%1$d %2$s</string>
|
||||
|
||||
<!-- Input Mapping -->
|
||||
<string name="inputMapActivity_popupMessage">Press a button, key, or joystick to map…\n\nCurrent mappings:\n%1$s</string>
|
||||
<string name="inputMapActivity_popupUnmap">Unmap</string>
|
||||
<string name="inputMapActivity_btnA">A</string>
|
||||
<string name="inputMapActivity_btnB">B</string>
|
||||
<string name="inputMapActivity_btnC">C</string>
|
||||
<string name="inputMapActivity_btnL">L</string>
|
||||
<string name="inputMapActivity_btnR">R</string>
|
||||
<string name="inputMapActivity_btnZ">Z</string>
|
||||
<string name="inputMapActivity_btnS">S</string>
|
||||
<string name="inputMapActivity_btnCR">C-pad →</string>
|
||||
<string name="inputMapActivity_btnCL">C-pad ←</string>
|
||||
<string name="inputMapActivity_btnCD">C-pad ↓</string>
|
||||
<string name="inputMapActivity_btnCU">C-pad ↑</string>
|
||||
<string name="inputMapActivity_btnDR">D-pad →</string>
|
||||
<string name="inputMapActivity_btnDL">D-pad ←</string>
|
||||
<string name="inputMapActivity_btnDD">D-pad ↓</string>
|
||||
<string name="inputMapActivity_btnDU">D-pad ↑</string>
|
||||
<string name="inputMapActivity_btnAR">Analog →</string>
|
||||
<string name="inputMapActivity_btnAL">Analog ←</string>
|
||||
<string name="inputMapActivity_btnAD">Analog ↓</string>
|
||||
<string name="inputMapActivity_btnAU">Analog ↑</string>
|
||||
|
||||
<!-- Settings -->
|
||||
<string name="Hardware">Hardware</string>
|
||||
<string name="Other">Other</string>
|
||||
<string name="touch_screen_summary">On-screen controller setttings</string>
|
||||
<string name="touch_screen_title">Touchscreen</string>
|
||||
<string name="input_screen_summary">Controller settings</string>
|
||||
<string name="input_screen_title">Input</string>
|
||||
<string name="video_screen_summary">Graphic settings</string>
|
||||
|
@ -141,6 +169,10 @@
|
|||
<string name="VerticalInterruptsPerSecond">Vertical interrupts per second</string>
|
||||
<string name="ForceGfxReset_title">Reset GFX Plugin</string>
|
||||
<string name="ForceGfxReset_summary">Always reload GFX plugin</string>
|
||||
<string name="touchscreenScale_title">Button scale</string>
|
||||
<string name="touchscreenLayout_title">Touchscreen layout</string>
|
||||
<string name="gamepad_summary">Configure gamepad to use in game</string>
|
||||
<string name="gamepad_title">Gamepad settings</string>
|
||||
<string name="settings_reset_title">Reset settings</string>
|
||||
<string name="settings_reset_message">Reset all settings back to their defaults?</string>
|
||||
|
||||
|
@ -190,7 +222,7 @@
|
|||
<string name="GetSaveSupport_message">Save support is a one time purchase and the only purchase in Project64.\n\nProject64 may not be perfect and making this as an upgrade it allows you to play and test the emulator before you have to spend any money.\n\nThis is also a good way for you to support and give back to the creation of the emulator..</string>
|
||||
<string name="GetSaveSupport_OkButton">Purchase</string>
|
||||
|
||||
<!-- Asset Extractor -->
|
||||
<!-- Asset Extractor -->
|
||||
<string name="assetExtractor_startingUp">Starting Up …</string>
|
||||
<string name="assetExtractor_version">version</string>
|
||||
<string name="assetExtractor_progress">%1$.0f%% done: extracting - %2$s</string>
|
||||
|
|
|
@ -55,4 +55,16 @@
|
|||
</style>
|
||||
|
||||
<style name="Project64.Toolbar" parent="ThemeOverlay.AppCompat.Dark.ActionBar" />
|
||||
<style name="btnMappable" parent="@android:style/Widget.Button">
|
||||
<item name="android:layout_width">@dimen/inputMapPreferenceButtonSize</item>
|
||||
<item name="android:layout_height">@dimen/inputMapPreferenceButtonSize</item>
|
||||
<item name="android:background">@drawable/button_gray</item>
|
||||
<item name="android:focusable">false</item>
|
||||
<item name="android:focusableInTouchMode">false</item>
|
||||
<item name="android:textColor">@android:color/primary_text_dark_nodisable</item>
|
||||
</style>
|
||||
|
||||
<style name="btnMappableNoText" parent="@style/btnMappable">
|
||||
<item name="android:textColor">@android:color/transparent</item>
|
||||
</style>
|
||||
</resources>
|
||||
|
|
|
@ -0,0 +1,13 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:focusable="false"
|
||||
android:focusableInTouchMode="false"
|
||||
android:gravity="center"
|
||||
android:orientation="vertical" >
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</PreferenceScreen>
|
|
@ -0,0 +1,15 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<Preference
|
||||
android:summary="@string/touch_screen_summary"
|
||||
android:title="@string/touch_screen_title"
|
||||
android:key="settings_touch_screen"
|
||||
android:icon="@drawable/ic_phone"
|
||||
/>
|
||||
<Preference
|
||||
android:summary="@string/gamepad_summary"
|
||||
android:title="@string/gamepad_title"
|
||||
android:key="settings_gamepad_screen"
|
||||
android:icon="@drawable/ic_gamepad"
|
||||
/>
|
||||
</PreferenceScreen>
|
|
@ -0,0 +1,22 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:project64="http://schemas.android.com/apk/res/emu.project64"
|
||||
android:key="screenRoot" >
|
||||
|
||||
<emu.project64.settings.SeekBarPreference
|
||||
android:defaultValue="100"
|
||||
android:key="touchscreenScale"
|
||||
android:title="@string/touchscreenScale_title"
|
||||
project64:maximumValue="400"
|
||||
project64:minimumValue="50"
|
||||
project64:stepSize="1"
|
||||
project64:units="%"
|
||||
/>
|
||||
|
||||
<emu.project64.settings.TwoLinesListPreference
|
||||
android:defaultValue="0"
|
||||
android:key="touchscreenLayout"
|
||||
android:title="@string/touchscreenLayout_title"
|
||||
android:dialogTitle="@string/touchscreenLayout_title" />
|
||||
|
||||
</PreferenceScreen>
|
|
@ -25,6 +25,7 @@ import emu.project64.util.Utility;
|
|||
import tv.ouya.console.api.OuyaFacade;
|
||||
import android.os.Build;
|
||||
import android.os.Environment;
|
||||
import android.view.KeyEvent;
|
||||
|
||||
public class AndroidDevice
|
||||
{
|
||||
|
@ -46,6 +47,9 @@ public class AndroidDevice
|
|||
/** True if device is running KitKat or later (19 - Android 4.4.x) */
|
||||
public static final boolean IS_KITKAT = Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT;
|
||||
|
||||
/** True if device is running Lollipop or later (21 - Android 5.0.x) */
|
||||
public static final boolean IS_LOLLIPOP = Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP;
|
||||
|
||||
/** True if device is an OUYA. */
|
||||
public static final boolean IS_OUYA_HARDWARE = OuyaFacade.getInstance().isRunningOnOUYAHardware();
|
||||
|
||||
|
@ -55,6 +59,9 @@ public class AndroidDevice
|
|||
public static final boolean IS_ACTION_BAR_AVAILABLE = AndroidDevice.IS_HONEYCOMB && !AndroidDevice.IS_OUYA_HARDWARE;
|
||||
|
||||
final static boolean isTv;
|
||||
|
||||
public static boolean MapVolumeKeys = false;
|
||||
|
||||
static
|
||||
{
|
||||
isTv = Project64Application.getAppContext().getPackageManager().hasSystemFeature("android.software.leanback");
|
||||
|
@ -65,6 +72,24 @@ public class AndroidDevice
|
|||
return isTv;
|
||||
}
|
||||
|
||||
public static List<Integer> getUnmappableKeyCodes ()
|
||||
{
|
||||
List<Integer> unmappables = new ArrayList<Integer>();
|
||||
unmappables.add( KeyEvent.KEYCODE_MENU );
|
||||
if( IS_HONEYCOMB )
|
||||
{
|
||||
// Back key is needed to show/hide the action bar in HC+
|
||||
unmappables.add( KeyEvent.KEYCODE_BACK );
|
||||
}
|
||||
if( !MapVolumeKeys )
|
||||
{
|
||||
unmappables.add( KeyEvent.KEYCODE_VOLUME_UP );
|
||||
unmappables.add( KeyEvent.KEYCODE_VOLUME_DOWN );
|
||||
unmappables.add( KeyEvent.KEYCODE_VOLUME_MUTE );
|
||||
}
|
||||
return unmappables;
|
||||
}
|
||||
|
||||
public static ArrayList<String> getStorageDirectories()
|
||||
{
|
||||
BufferedReader bufReader = null;
|
||||
|
|
|
@ -15,6 +15,7 @@ import emu.project64.jni.SystemEvent;
|
|||
import android.app.Activity;
|
||||
import android.app.AlertDialog;
|
||||
import android.content.DialogInterface;
|
||||
import android.content.Intent;
|
||||
import android.os.Bundle;
|
||||
|
||||
public class GameActivity extends Activity
|
||||
|
@ -22,16 +23,16 @@ public class GameActivity extends Activity
|
|||
private GameLifecycleHandler mLifecycleHandler;
|
||||
@SuppressWarnings("unused")
|
||||
private GameMenuHandler mMenuHandler;
|
||||
|
||||
|
||||
@Override
|
||||
public void onWindowFocusChanged( boolean hasFocus )
|
||||
{
|
||||
super.onWindowFocusChanged( hasFocus );
|
||||
mLifecycleHandler.onWindowFocusChanged( hasFocus );
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void onBackPressed()
|
||||
public void onBackPressed()
|
||||
{
|
||||
NativeExports.ExternalEvent( SystemEvent.SysEvent_PauseCPU_AppLostActive.getValue());
|
||||
AlertDialog.Builder builder = new AlertDialog.Builder(this);
|
||||
|
@ -49,50 +50,59 @@ public class GameActivity extends Activity
|
|||
})
|
||||
.show();
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
protected void onCreate( Bundle savedInstanceState )
|
||||
{
|
||||
{
|
||||
mLifecycleHandler = new GameLifecycleHandler( this );
|
||||
mLifecycleHandler.onCreateBegin( savedInstanceState );
|
||||
super.onCreate( savedInstanceState );
|
||||
mLifecycleHandler.onCreateEnd( savedInstanceState );
|
||||
|
||||
|
||||
mMenuHandler = new GameMenuHandler( this, mLifecycleHandler );
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
protected void onStart()
|
||||
{
|
||||
super.onStart();
|
||||
mLifecycleHandler.onStart();
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
protected void onResume()
|
||||
{
|
||||
super.onResume();
|
||||
mLifecycleHandler.onResume();
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
protected void onPause()
|
||||
{
|
||||
super.onPause();
|
||||
mLifecycleHandler.onPause();
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
protected void onStop()
|
||||
{
|
||||
super.onStop();
|
||||
mLifecycleHandler.onStop();
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
protected void onDestroy()
|
||||
{
|
||||
super.onDestroy();
|
||||
mLifecycleHandler.onDestroy();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onActivityResult(int requestCode, int resultCode, Intent data)
|
||||
{
|
||||
if (requestCode == GameLifecycleHandler.RC_SETTINGS)
|
||||
{
|
||||
mLifecycleHandler.onSettingDone();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -15,46 +15,65 @@ import java.lang.ref.WeakReference;
|
|||
import javax.microedition.khronos.egl.EGLConfig;
|
||||
import javax.microedition.khronos.opengles.GL10;
|
||||
|
||||
import com.bda.controller.Controller;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Set;
|
||||
|
||||
import emu.project64.AndroidDevice;
|
||||
import emu.project64.R;
|
||||
import emu.project64.hack.MogaHack;
|
||||
import emu.project64.input.AbstractController;
|
||||
import emu.project64.input.PeripheralController;
|
||||
import emu.project64.input.TouchController;
|
||||
import emu.project64.input.map.InputMap;
|
||||
import emu.project64.input.map.VisibleTouchMap;
|
||||
import emu.project64.input.provider.AbstractProvider;
|
||||
import emu.project64.input.provider.AxisProvider;
|
||||
import emu.project64.input.provider.KeyProvider;
|
||||
import emu.project64.input.provider.KeyProvider.ImeFormula;
|
||||
import emu.project64.input.provider.MogaProvider;
|
||||
import emu.project64.jni.NativeExports;
|
||||
import emu.project64.jni.NativeXperiaTouchpad;
|
||||
import emu.project64.jni.SettingsID;
|
||||
import emu.project64.jni.SystemEvent;
|
||||
import emu.project64.jni.UISettingID;
|
||||
import emu.project64.persistent.ConfigFile;
|
||||
import emu.project64.persistent.ConfigFile.ConfigSection;
|
||||
import emu.project64.profile.Profile;
|
||||
import android.annotation.SuppressLint;
|
||||
import android.annotation.TargetApi;
|
||||
import android.app.Activity;
|
||||
import android.app.ActivityManager;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.graphics.Color;
|
||||
import android.graphics.drawable.ColorDrawable;
|
||||
import android.os.Bundle;
|
||||
import android.os.Vibrator;
|
||||
import android.util.Log;
|
||||
import android.view.KeyEvent;
|
||||
import android.view.SurfaceHolder;
|
||||
import android.view.View;
|
||||
import android.view.Window;
|
||||
import android.view.WindowManager.LayoutParams;
|
||||
|
||||
public class GameLifecycleHandler implements SurfaceHolder.Callback, GameSurface.SurfaceInfo
|
||||
public class GameLifecycleHandler implements View.OnKeyListener, SurfaceHolder.Callback, GameSurface.SurfaceInfo
|
||||
{
|
||||
private final static boolean LOG_GAMELIFECYCLEHANDLER = false;
|
||||
public final static int RC_SETTINGS = 10005;
|
||||
|
||||
// Activity and views
|
||||
private Activity mActivity;
|
||||
private GameSurface mSurface;
|
||||
private GameOverlay mOverlay;
|
||||
|
||||
// Input resources
|
||||
private final ArrayList<AbstractController> mControllers;
|
||||
private VisibleTouchMap mTouchscreenMap;
|
||||
private KeyProvider mKeyProvider;
|
||||
private Controller mMogaController;
|
||||
|
||||
// Internal flags
|
||||
private final boolean mIsXperiaPlay;
|
||||
private boolean mStarted = false;
|
||||
|
@ -64,22 +83,31 @@ public class GameLifecycleHandler implements SurfaceHolder.Callback, GameSurface
|
|||
private boolean mIsFocused = false; // true if the window is focused
|
||||
private boolean mIsResumed = false; // true if the activity is resumed
|
||||
private boolean mIsSurface = false; // true if the surface is available
|
||||
|
||||
public GameLifecycleHandler(Activity activity)
|
||||
|
||||
private float mtouchscreenScale = ((float)NativeExports.UISettingsLoadDword(UISettingID.TouchScreen_ButtonScale.getValue())) / 100.0f;
|
||||
private String mlayout = NativeExports.UISettingsLoadString(UISettingID.TouchScreen_Layout.getValue());
|
||||
|
||||
public GameLifecycleHandler(Activity activity)
|
||||
{
|
||||
mActivity = activity;
|
||||
mControllers = new ArrayList<AbstractController>();
|
||||
mIsXperiaPlay = !(activity instanceof GameActivity);
|
||||
mMogaController = Controller.getInstance( mActivity );
|
||||
}
|
||||
|
||||
@TargetApi(11)
|
||||
public void onCreateBegin(Bundle savedInstanceState)
|
||||
public void onCreateBegin(Bundle savedInstanceState)
|
||||
{
|
||||
if (LOG_GAMELIFECYCLEHANDLER)
|
||||
if (LOG_GAMELIFECYCLEHANDLER)
|
||||
{
|
||||
Log.i("GameLifecycleHandler", "onCreateBegin");
|
||||
}
|
||||
|
||||
// Initialize MOGA controller API
|
||||
// TODO: Remove hack after MOGA SDK is fixed
|
||||
// mMogaController.init();
|
||||
MogaHack.init( mMogaController, mActivity );
|
||||
|
||||
|
||||
// For Honeycomb, let the action bar overlay the rendered view (rather
|
||||
// than squeezing it)
|
||||
// For earlier APIs, remove the title bar to yield more space
|
||||
|
@ -97,9 +125,9 @@ public class GameLifecycleHandler implements SurfaceHolder.Callback, GameSurface
|
|||
}
|
||||
|
||||
@TargetApi(11)
|
||||
public void onCreateEnd(Bundle savedInstanceState)
|
||||
public void onCreateEnd(Bundle savedInstanceState)
|
||||
{
|
||||
if (LOG_GAMELIFECYCLEHANDLER)
|
||||
if (LOG_GAMELIFECYCLEHANDLER)
|
||||
{
|
||||
Log.i("GameLifecycleHandler", "onCreateEnd");
|
||||
}
|
||||
|
@ -114,13 +142,13 @@ public class GameLifecycleHandler implements SurfaceHolder.Callback, GameSurface
|
|||
mActivity.setContentView(R.layout.game_activity);
|
||||
mSurface = (GameSurface) mActivity.findViewById( R.id.gameSurface );
|
||||
mOverlay = (GameOverlay) mActivity.findViewById(R.id.gameOverlay);
|
||||
|
||||
|
||||
// Listen to game surface events (created, changed, destroyed)
|
||||
mSurface.getHolder().addCallback( this );
|
||||
mSurface.createGLContext((ActivityManager)mActivity.getSystemService(Context.ACTIVITY_SERVICE));
|
||||
|
||||
// Configure the action bar introduced in higher Android versions
|
||||
if (AndroidDevice.IS_ACTION_BAR_AVAILABLE)
|
||||
if (AndroidDevice.IS_ACTION_BAR_AVAILABLE)
|
||||
{
|
||||
mActivity.getActionBar().hide();
|
||||
ColorDrawable color = new ColorDrawable(Color.parseColor("#303030"));
|
||||
|
@ -128,62 +156,49 @@ public class GameLifecycleHandler implements SurfaceHolder.Callback, GameSurface
|
|||
mActivity.getActionBar().setBackgroundDrawable(color);
|
||||
}
|
||||
|
||||
boolean isFpsEnabled = false; //mGlobalPrefs.isFpsEnabled
|
||||
boolean isTouchscreenAnimated = false; //mGlobalPrefs.isTouchscreenAnimated
|
||||
boolean isTouchscreenHidden = false; //!isTouchscreenEnabled || globalPrefs.touchscreenTransparency == 0;
|
||||
String profilesDir = AndroidDevice.PACKAGE_DIRECTORY + "/profiles";
|
||||
String touchscreenProfiles_cfg = profilesDir + "/touchscreen.cfg";
|
||||
ConfigFile touchscreenConfigFile = new ConfigFile( touchscreenProfiles_cfg );
|
||||
//SharedPreferences mPreferences = context.getSharedPreferences( sharedPrefsName, Context.MODE_PRIVATE );
|
||||
Profile touchscreenProfile = new Profile( true, touchscreenConfigFile.get( "Analog")); //loadProfile( /*mPreferences*/ null, "touchscreenProfile", "Analog", touchscreenProfiles_cfg,touchscreenProfiles_cfg );
|
||||
int touchscreenTransparency = 100;
|
||||
String touchscreenSkinsDir = AndroidDevice.PACKAGE_DIRECTORY + "/skins/touchscreen";
|
||||
String touchscreenSkin = touchscreenSkinsDir + "/Outline";
|
||||
float touchscreenScale = 1.0f; //( (float) mPreferences.getInt( "touchscreenScale", 100 ) ) / 100.0f;
|
||||
|
||||
// The touch map and overlay are needed to display frame rate and/or controls
|
||||
mTouchscreenMap = new VisibleTouchMap( mActivity.getResources() );
|
||||
mTouchscreenMap.load(touchscreenSkin, touchscreenProfile,
|
||||
isTouchscreenAnimated, isFpsEnabled,
|
||||
touchscreenScale, touchscreenTransparency );
|
||||
mOverlay.initialize( mTouchscreenMap, !isTouchscreenHidden, isFpsEnabled, isTouchscreenAnimated );
|
||||
|
||||
CreateTouchScreenControls();
|
||||
|
||||
// Initialize user interface devices
|
||||
View inputSource = mIsXperiaPlay ? new NativeXperiaTouchpad(mActivity) : mOverlay;
|
||||
initControllers(inputSource);
|
||||
|
||||
// Override the peripheral controllers' key provider, to add some extra functionality
|
||||
inputSource.setOnKeyListener( this );
|
||||
}
|
||||
|
||||
public void onStart()
|
||||
public void onStart()
|
||||
{
|
||||
if (LOG_GAMELIFECYCLEHANDLER)
|
||||
if (LOG_GAMELIFECYCLEHANDLER)
|
||||
{
|
||||
Log.i("GameLifecycleHandler", "onStart");
|
||||
}
|
||||
}
|
||||
|
||||
public void onResume()
|
||||
public void onResume()
|
||||
{
|
||||
if (LOG_GAMELIFECYCLEHANDLER)
|
||||
if (LOG_GAMELIFECYCLEHANDLER)
|
||||
{
|
||||
Log.i("GameLifecycleHandler", "onResume");
|
||||
}
|
||||
mIsResumed = true;
|
||||
tryRunning();
|
||||
|
||||
mMogaController.onResume();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void surfaceCreated(SurfaceHolder holder)
|
||||
public void surfaceCreated(SurfaceHolder holder)
|
||||
{
|
||||
if (LOG_GAMELIFECYCLEHANDLER)
|
||||
if (LOG_GAMELIFECYCLEHANDLER)
|
||||
{
|
||||
Log.i("GameLifecycleHandler", "surfaceCreated");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height)
|
||||
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height)
|
||||
{
|
||||
if (LOG_GAMELIFECYCLEHANDLER)
|
||||
if (LOG_GAMELIFECYCLEHANDLER)
|
||||
{
|
||||
Log.i("GameLifecycleHandler", "surfaceChanged");
|
||||
}
|
||||
|
@ -191,24 +206,24 @@ public class GameLifecycleHandler implements SurfaceHolder.Callback, GameSurface
|
|||
tryRunning();
|
||||
}
|
||||
|
||||
public void onWindowFocusChanged(boolean hasFocus)
|
||||
public void onWindowFocusChanged(boolean hasFocus)
|
||||
{
|
||||
if (LOG_GAMELIFECYCLEHANDLER)
|
||||
if (LOG_GAMELIFECYCLEHANDLER)
|
||||
{
|
||||
Log.i("GameLifecycleHandler", "onWindowFocusChanged: " + hasFocus);
|
||||
}
|
||||
// Only try to run; don't try to pause. User may just be touching the
|
||||
// in-game menu.
|
||||
mIsFocused = hasFocus;
|
||||
if (hasFocus)
|
||||
if (hasFocus)
|
||||
{
|
||||
tryRunning();
|
||||
tryRunning();
|
||||
}
|
||||
}
|
||||
|
||||
public void AutoSave()
|
||||
{
|
||||
if (LOG_GAMELIFECYCLEHANDLER)
|
||||
if (LOG_GAMELIFECYCLEHANDLER)
|
||||
{
|
||||
Log.i("GameLifecycleHandler", "OnAutoSave");
|
||||
}
|
||||
|
@ -229,7 +244,7 @@ public class GameLifecycleHandler implements SurfaceHolder.Callback, GameSurface
|
|||
for (int i = 0; i < 100; i++)
|
||||
{
|
||||
int LastSaveTime = NativeExports.SettingsLoadDword(SettingsID.Game_LastSaveTime.getValue());
|
||||
if (LOG_GAMELIFECYCLEHANDLER)
|
||||
if (LOG_GAMELIFECYCLEHANDLER)
|
||||
{
|
||||
Log.i("GameLifecycleHandler", "LastSaveTime = " + LastSaveTime + " OriginalSaveTime = " + OriginalSaveTime);
|
||||
}
|
||||
|
@ -237,9 +252,9 @@ public class GameLifecycleHandler implements SurfaceHolder.Callback, GameSurface
|
|||
{
|
||||
break;
|
||||
}
|
||||
try
|
||||
try
|
||||
{
|
||||
Thread.sleep(100);
|
||||
Thread.sleep(100);
|
||||
}
|
||||
catch(InterruptedException ex)
|
||||
{
|
||||
|
@ -255,18 +270,18 @@ public class GameLifecycleHandler implements SurfaceHolder.Callback, GameSurface
|
|||
}
|
||||
else if (LOG_GAMELIFECYCLEHANDLER)
|
||||
{
|
||||
Log.i("GameLifecycleHandler", "CPU not running, not doing anything");
|
||||
Log.i("GameLifecycleHandler", "CPU not running, not doing anything");
|
||||
}
|
||||
|
||||
if (LOG_GAMELIFECYCLEHANDLER)
|
||||
|
||||
if (LOG_GAMELIFECYCLEHANDLER)
|
||||
{
|
||||
Log.i("GameLifecycleHandler", "OnAutoSave Done");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public void onPause()
|
||||
{
|
||||
if (LOG_GAMELIFECYCLEHANDLER)
|
||||
if (LOG_GAMELIFECYCLEHANDLER)
|
||||
{
|
||||
Log.i("GameLifecycleHandler", "onPause");
|
||||
}
|
||||
|
@ -275,7 +290,8 @@ public class GameLifecycleHandler implements SurfaceHolder.Callback, GameSurface
|
|||
{
|
||||
AutoSave();
|
||||
}
|
||||
if (LOG_GAMELIFECYCLEHANDLER)
|
||||
mMogaController.onPause();
|
||||
if (LOG_GAMELIFECYCLEHANDLER)
|
||||
{
|
||||
Log.i("GameLifecycleHandler", "onPause - done");
|
||||
}
|
||||
|
@ -284,7 +300,7 @@ public class GameLifecycleHandler implements SurfaceHolder.Callback, GameSurface
|
|||
@Override
|
||||
public void surfaceDestroyed(SurfaceHolder holder)
|
||||
{
|
||||
if (LOG_GAMELIFECYCLEHANDLER)
|
||||
if (LOG_GAMELIFECYCLEHANDLER)
|
||||
{
|
||||
Log.i("GameLifecycleHandler", "surfaceDestroyed");
|
||||
}
|
||||
|
@ -293,7 +309,7 @@ public class GameLifecycleHandler implements SurfaceHolder.Callback, GameSurface
|
|||
|
||||
public void onStop()
|
||||
{
|
||||
if (LOG_GAMELIFECYCLEHANDLER)
|
||||
if (LOG_GAMELIFECYCLEHANDLER)
|
||||
{
|
||||
Log.i("GameLifecycleHandler", "onStop");
|
||||
}
|
||||
|
@ -301,10 +317,71 @@ public class GameLifecycleHandler implements SurfaceHolder.Callback, GameSurface
|
|||
|
||||
public void onDestroy()
|
||||
{
|
||||
if (LOG_GAMELIFECYCLEHANDLER)
|
||||
if (LOG_GAMELIFECYCLEHANDLER)
|
||||
{
|
||||
Log.i("GameLifecycleHandler", "onDestroy");
|
||||
}
|
||||
mMogaController.exit();
|
||||
}
|
||||
|
||||
public void onSettingDone ()
|
||||
{
|
||||
float touchscreenScale = ((float)NativeExports.UISettingsLoadDword(UISettingID.TouchScreen_ButtonScale.getValue())) / 100.0f;
|
||||
boolean recreateTouchScreenControls = false;
|
||||
if (touchscreenScale != mtouchscreenScale)
|
||||
{
|
||||
mtouchscreenScale = touchscreenScale;
|
||||
recreateTouchScreenControls = true;
|
||||
}
|
||||
|
||||
String layout = NativeExports.UISettingsLoadString(UISettingID.TouchScreen_Layout.getValue());
|
||||
if (layout != mlayout)
|
||||
{
|
||||
mlayout = layout;
|
||||
recreateTouchScreenControls = true;
|
||||
}
|
||||
|
||||
if (recreateTouchScreenControls)
|
||||
{
|
||||
CreateTouchScreenControls();
|
||||
}
|
||||
}
|
||||
|
||||
private void CreateTouchScreenControls()
|
||||
{
|
||||
boolean isTouchscreenAnimated = false; //mGlobalPrefs.isTouchscreenAnimated
|
||||
boolean isTouchscreenHidden = false; //!isTouchscreenEnabled || globalPrefs.touchscreenTransparency == 0;
|
||||
String profilesDir = AndroidDevice.PACKAGE_DIRECTORY + "/profiles";
|
||||
String touchscreenProfiles_cfg = profilesDir + "/touchscreen.cfg";
|
||||
ConfigFile touchscreenConfigFile = new ConfigFile( touchscreenProfiles_cfg );
|
||||
ConfigSection section = touchscreenConfigFile.get(mlayout);
|
||||
if (section == null)
|
||||
{
|
||||
mlayout = "Analog";
|
||||
section = touchscreenConfigFile.get(mlayout);
|
||||
}
|
||||
Profile touchscreenProfile = new Profile( true, section);
|
||||
int touchscreenTransparency = 100;
|
||||
String touchscreenSkinsDir = AndroidDevice.PACKAGE_DIRECTORY + "/skins/touchscreen";
|
||||
String touchscreenSkin = touchscreenSkinsDir + "/Outline";
|
||||
|
||||
// The touch map and overlay are needed to display frame rate and/or controls
|
||||
mTouchscreenMap = new VisibleTouchMap( mActivity.getResources() );
|
||||
mTouchscreenMap.load(touchscreenSkin, touchscreenProfile, isTouchscreenAnimated, mtouchscreenScale, touchscreenTransparency );
|
||||
mOverlay.initialize( mTouchscreenMap, !isTouchscreenHidden, isTouchscreenAnimated );
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onKey( View view, int keyCode, KeyEvent event )
|
||||
{
|
||||
// If PeripheralControllers exist and handle the event,
|
||||
// they return true. Else they return false, signaling
|
||||
// Android to handle the event (menu button, vol keys).
|
||||
if( mKeyProvider != null )
|
||||
{
|
||||
return mKeyProvider.onKey( view, keyCode, event );
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@SuppressLint("InlinedApi")
|
||||
|
@ -315,15 +392,32 @@ public class GameLifecycleHandler implements SurfaceHolder.Callback, GameSurface
|
|||
int touchscreenAutoHold = 0;
|
||||
boolean isTouchscreenFeedbackEnabled = false;
|
||||
Set<Integer> autoHoldableButtons = null;
|
||||
|
||||
|
||||
// Create the touchscreen controller
|
||||
TouchController touchscreenController = new TouchController( mTouchscreenMap,
|
||||
inputSource, mOverlay, vibrator, touchscreenAutoHold,
|
||||
isTouchscreenFeedbackEnabled, autoHoldableButtons );
|
||||
mControllers.add( touchscreenController );
|
||||
|
||||
// Create the input providers shared among all peripheral controllers
|
||||
String profile_name = NativeExports.UISettingsLoadString(UISettingID.Controller_CurrentProfile.getValue());
|
||||
ConfigFile ControllerConfigFile = new ConfigFile(NativeExports.UISettingsLoadString(UISettingID.Controller_ConfigFile.getValue()));
|
||||
ConfigSection section = ControllerConfigFile.get( profile_name );
|
||||
if (section != null)
|
||||
{
|
||||
Profile ControllerProfile = new Profile( false, section );
|
||||
InputMap map = new InputMap( ControllerProfile.get( "map" ) );
|
||||
|
||||
mKeyProvider = new KeyProvider( inputSource, ImeFormula.DEFAULT, AndroidDevice.getUnmappableKeyCodes() );
|
||||
MogaProvider mogaProvider = new MogaProvider( mMogaController );
|
||||
AbstractProvider axisProvider = AndroidDevice.IS_HONEYCOMB_MR1 ? new AxisProvider( inputSource ) : null;
|
||||
int Deadzone = NativeExports.UISettingsLoadDword(UISettingID.Controller_Deadzone.getValue());
|
||||
int Sensitivity = NativeExports.UISettingsLoadDword(UISettingID.Controller_Sensitivity.getValue());
|
||||
mControllers.add( new PeripheralController( 1, map, Deadzone, Sensitivity, mKeyProvider, axisProvider, mogaProvider ) );
|
||||
}
|
||||
}
|
||||
|
||||
private void tryRunning()
|
||||
private void tryRunning()
|
||||
{
|
||||
if (mIsFocused && mIsResumed && mIsSurface && mStopped)
|
||||
{
|
||||
|
@ -338,21 +432,21 @@ public class GameLifecycleHandler implements SurfaceHolder.Callback, GameSurface
|
|||
NativeExports.StartGame(mActivity, new GameSurface.GLThread(new WeakReference<GameSurface>(mSurface), handler));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void onSurfaceCreated(GL10 gl, EGLConfig config)
|
||||
{
|
||||
if (LOG_GAMELIFECYCLEHANDLER)
|
||||
if (LOG_GAMELIFECYCLEHANDLER)
|
||||
{
|
||||
Log.i("GameLifecycleHandler", "onSurfaceCreated");
|
||||
}
|
||||
NativeExports.onSurfaceCreated();
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void onSurfaceChanged(GL10 gl, int width, int height)
|
||||
{
|
||||
if (LOG_GAMELIFECYCLEHANDLER)
|
||||
if (LOG_GAMELIFECYCLEHANDLER)
|
||||
{
|
||||
Log.i("GameLifecycleHandler", "onSurfaceChanged");
|
||||
}
|
||||
|
|
|
@ -146,7 +146,7 @@ public class GameMenuHandler implements PopupMenu.OnMenuItemClickListener, Popup
|
|||
break;
|
||||
case R.id.menuItem_settings:
|
||||
Intent SettingsIntent = new Intent(mActivity, SettingsActivity.class);
|
||||
mActivity.startActivity( SettingsIntent );
|
||||
mActivity.startActivityForResult( SettingsIntent, GameLifecycleHandler.RC_SETTINGS );
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
|
|
|
@ -44,7 +44,7 @@ public class GameOverlay extends View implements TouchController.OnStateChangedL
|
|||
requestFocus();
|
||||
}
|
||||
|
||||
public void initialize( VisibleTouchMap touchMap, boolean drawingEnabled, boolean fpsEnabled, boolean joystickAnimated )
|
||||
public void initialize( VisibleTouchMap touchMap, boolean drawingEnabled, boolean joystickAnimated )
|
||||
{
|
||||
mTouchMap = touchMap;
|
||||
mDrawingEnabled = drawingEnabled;
|
||||
|
|
|
@ -0,0 +1,110 @@
|
|||
/****************************************************************************
|
||||
* *
|
||||
* Project64 - A Nintendo 64 emulator. *
|
||||
* http://www.pj64-emu.com/ *
|
||||
* Copyright (C) 2016 Project64. All rights reserved. *
|
||||
* Copyright (C) 2013 Paul Lamb
|
||||
* *
|
||||
* License: *
|
||||
* GNU/GPLv2 http://www.gnu.org/licenses/gpl-2.0.html *
|
||||
* *
|
||||
****************************************************************************/
|
||||
package emu.project64.hack;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import android.content.ComponentName;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.pm.ResolveInfo;
|
||||
import android.content.pm.ServiceInfo;
|
||||
import android.util.Log;
|
||||
|
||||
import com.bda.controller.Controller;
|
||||
import com.bda.controller.IControllerService;
|
||||
|
||||
import emu.project64.AndroidDevice;
|
||||
|
||||
/**
|
||||
* Temporary hack for crash in MOGA library on Lollipop. This hack can be removed once MOGA fixes
|
||||
* their library. The actual issue is caused by the use of implicit service intents, which are
|
||||
* illegal in Lollipop, as seen in the logcat message below.
|
||||
*
|
||||
* <pre>
|
||||
* {@code Service Intent must be explicit: Intent { act=com.bda.controller.IControllerService } }
|
||||
* </pre>
|
||||
*
|
||||
* @see <a href="http://www.mogaanywhere.com/developers/">MOGA developer site</a>
|
||||
* @see <a href="http://commonsware.com/blog/2014/06/29/dealing-deprecations-bindservice.html">
|
||||
* Discussion on explicit intents</a>
|
||||
*/
|
||||
public class MogaHack
|
||||
{
|
||||
public static void init( Controller controller, Context context )
|
||||
{
|
||||
if( AndroidDevice.IS_LOLLIPOP )
|
||||
{
|
||||
boolean mIsBound = false;
|
||||
java.lang.reflect.Field fIsBound = null;
|
||||
android.content.ServiceConnection mServiceConnection = null;
|
||||
java.lang.reflect.Field fServiceConnection = null;
|
||||
try
|
||||
{
|
||||
Class<?> cMogaController = controller.getClass();
|
||||
fIsBound = cMogaController.getDeclaredField( "mIsBound" );
|
||||
fIsBound.setAccessible( true );
|
||||
mIsBound = fIsBound.getBoolean( controller );
|
||||
fServiceConnection = cMogaController.getDeclaredField( "mServiceConnection" );
|
||||
fServiceConnection.setAccessible( true );
|
||||
mServiceConnection = ( android.content.ServiceConnection ) fServiceConnection.get( controller );
|
||||
}
|
||||
catch( NoSuchFieldException e )
|
||||
{
|
||||
Log.e( "MogaHack", "MOGA Lollipop Hack NoSuchFieldException (get)", e );
|
||||
}
|
||||
catch( IllegalAccessException e )
|
||||
{
|
||||
Log.e( "MogaHack", "MOGA Lollipop Hack IllegalAccessException (get)", e );
|
||||
}
|
||||
catch( IllegalArgumentException e )
|
||||
{
|
||||
Log.e( "MogaHack", "MOGA Lollipop Hack IllegalArgumentException (get)", e );
|
||||
}
|
||||
if( ( !mIsBound ) && ( mServiceConnection != null ) )
|
||||
{
|
||||
// Convert implicit intent to explicit intent, see http://stackoverflow.com/a/26318757
|
||||
Intent intent = new Intent( IControllerService.class.getName() );
|
||||
List<ResolveInfo> resolveInfos = context.getPackageManager().queryIntentServices( intent, 0 );
|
||||
if( resolveInfos == null || resolveInfos.size() != 1 )
|
||||
{
|
||||
Log.e( "MogaHack", "Somebody is trying to intercept our intent. Disabling MOGA controller for security." );
|
||||
return;
|
||||
}
|
||||
ServiceInfo serviceInfo = resolveInfos.get( 0 ).serviceInfo;
|
||||
String packageName = serviceInfo.packageName;
|
||||
String className = serviceInfo.name;
|
||||
intent.setComponent( new ComponentName( packageName, className ) );
|
||||
|
||||
// Start the service explicitly
|
||||
context.startService( intent );
|
||||
context.bindService( intent, mServiceConnection, 1 );
|
||||
try
|
||||
{
|
||||
fIsBound.setBoolean( controller, true );
|
||||
}
|
||||
catch( IllegalAccessException e )
|
||||
{
|
||||
Log.e( "MogaHack", "MOGA Lollipop Hack IllegalAccessException (set)", e );
|
||||
}
|
||||
catch( IllegalArgumentException e )
|
||||
{
|
||||
Log.e( "MogaHack", "MOGA Lollipop Hack IllegalArgumentException (set)", e );
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
controller.init();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -12,6 +12,7 @@ package emu.project64.input;
|
|||
|
||||
import java.util.ArrayList;
|
||||
|
||||
import android.util.Log;
|
||||
import emu.project64.jni.NativeInput;
|
||||
|
||||
/**
|
||||
|
@ -32,19 +33,21 @@ import emu.project64.jni.NativeInput;
|
|||
* from the subclass. For best performance, subclasses should only call notifyChanged() when the
|
||||
* input state has actually changed, and should bundle the protected field modifications before
|
||||
* calling notifyChanged(). For example,
|
||||
*
|
||||
*
|
||||
* <pre>
|
||||
* {@code
|
||||
* buttons[0] = true; notifyChanged(); buttons[1] = false; notifyChanged(); // Inefficient
|
||||
* buttons[0] = true; buttons[1] = false; notifyChanged(); // Better
|
||||
* }
|
||||
* </pre>
|
||||
*
|
||||
*
|
||||
* @see PeripheralController
|
||||
* @see TouchController
|
||||
*/
|
||||
public abstract class AbstractController
|
||||
{
|
||||
protected final static boolean LOG_CONTROLLER = false;
|
||||
|
||||
/**
|
||||
* A small class that encapsulates controller state.
|
||||
*/
|
||||
|
@ -52,58 +55,58 @@ public abstract class AbstractController
|
|||
{
|
||||
/** The pressed state of each controller button. */
|
||||
public boolean[] buttons = new boolean[NUM_N64_BUTTONS];
|
||||
|
||||
|
||||
/** The fractional value of the analog-x axis, between -1 and 1, inclusive. */
|
||||
public float axisFractionX = 0;
|
||||
|
||||
|
||||
/** The fractional value of the analog-y axis, between -1 and 1, inclusive. */
|
||||
public float axisFractionY = 0;
|
||||
}
|
||||
|
||||
|
||||
// Constants must match EButton listing in plugin.h! (input-sdl plug-in)
|
||||
|
||||
|
||||
/** N64 button: dpad-right. */
|
||||
public static final int DPD_R = 0;
|
||||
|
||||
|
||||
/** N64 button: dpad-left. */
|
||||
public static final int DPD_L = 1;
|
||||
|
||||
|
||||
/** N64 button: dpad-down. */
|
||||
public static final int DPD_D = 2;
|
||||
|
||||
|
||||
/** N64 button: dpad-up. */
|
||||
public static final int DPD_U = 3;
|
||||
|
||||
|
||||
/** N64 button: start. */
|
||||
public static final int START = 4;
|
||||
|
||||
|
||||
/** N64 button: trigger-z. */
|
||||
public static final int BTN_Z = 5;
|
||||
|
||||
|
||||
/** N64 button: b. */
|
||||
public static final int BTN_B = 6;
|
||||
|
||||
|
||||
/** N64 button: a. */
|
||||
public static final int BTN_A = 7;
|
||||
|
||||
|
||||
/** N64 button: cpad-right. */
|
||||
public static final int CPD_R = 8;
|
||||
|
||||
|
||||
/** N64 button: cpad-left. */
|
||||
public static final int CPD_L = 9;
|
||||
|
||||
|
||||
/** N64 button: cpad-down. */
|
||||
public static final int CPD_D = 10;
|
||||
|
||||
|
||||
/** N64 button: cpad-up. */
|
||||
public static final int CPD_U = 11;
|
||||
|
||||
|
||||
/** N64 button: shoulder-r. */
|
||||
public static final int BTN_R = 12;
|
||||
|
||||
|
||||
/** N64 button: shoulder-l. */
|
||||
public static final int BTN_L = 13;
|
||||
|
||||
|
||||
/** N64 button: reserved-1. */
|
||||
public static final int BTN_RESERVED1 = 14;
|
||||
|
||||
|
@ -112,19 +115,19 @@ public abstract class AbstractController
|
|||
|
||||
/** Total number of N64 buttons. */
|
||||
public static final int NUM_N64_BUTTONS = 16;
|
||||
|
||||
|
||||
/** The state of all four player controllers. */
|
||||
private static final ArrayList<State> sStates = new ArrayList<State>();
|
||||
|
||||
|
||||
/** The state of this controller. */
|
||||
protected State mState;
|
||||
|
||||
|
||||
/** The player number, between 1 and 4, inclusive. */
|
||||
protected int mPlayerNumber = 1;
|
||||
|
||||
|
||||
/** The factor by which the axis fractions are scaled before going to the core. */
|
||||
private static final float AXIS_SCALE = 80;
|
||||
|
||||
|
||||
static
|
||||
{
|
||||
sStates.add( new State() );
|
||||
|
@ -132,7 +135,7 @@ public abstract class AbstractController
|
|||
sStates.add( new State() );
|
||||
sStates.add( new State() );
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Instantiates a new abstract controller.
|
||||
*/
|
||||
|
@ -140,30 +143,35 @@ public abstract class AbstractController
|
|||
{
|
||||
mState = sStates.get( 0 );
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Notifies the core that the N64 controller state has changed.
|
||||
*/
|
||||
protected void notifyChanged()
|
||||
{
|
||||
|
||||
int axisX = Math.round( AXIS_SCALE * mState.axisFractionX );
|
||||
int axisY = Math.round( AXIS_SCALE * mState.axisFractionY );
|
||||
if (LOG_CONTROLLER)
|
||||
{
|
||||
Log.i("Controller", "notifyChanged: axisX=" + axisX + " axisY=" + axisY);
|
||||
}
|
||||
NativeInput.setState( mPlayerNumber - 1, mState.buttons, axisX, axisY );
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Gets the player number.
|
||||
*
|
||||
*
|
||||
* @return The player number, between 1 and 4, inclusive.
|
||||
*/
|
||||
public int getPlayerNumber()
|
||||
{
|
||||
return mPlayerNumber;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Sets the player number.
|
||||
*
|
||||
*
|
||||
* @param player The new player number, between 1 and 4, inclusive.
|
||||
*/
|
||||
public void setPlayerNumber( int player )
|
||||
|
|
|
@ -0,0 +1,173 @@
|
|||
/****************************************************************************
|
||||
* *
|
||||
* Project 64 - A Nintendo 64 emulator. *
|
||||
* http://www.pj64-emu.com/ *
|
||||
* Copyright (C) 2016 Project64. All rights reserved. *
|
||||
* Copyright (C) 2013 Paul Lamb, littleguy77 *
|
||||
* *
|
||||
* License: *
|
||||
* GNU/GPLv2 http://www.gnu.org/licenses/gpl-2.0.html *
|
||||
* *
|
||||
****************************************************************************/
|
||||
package emu.project64.input;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
import emu.project64.input.map.InputMap;
|
||||
import emu.project64.input.provider.AbstractProvider;
|
||||
import emu.project64.util.Utility;
|
||||
import android.annotation.TargetApi;
|
||||
import android.util.FloatMath;
|
||||
import android.util.Log;
|
||||
import android.view.InputDevice;
|
||||
import android.view.KeyEvent;
|
||||
|
||||
/**
|
||||
* A class for generating N64 controller commands from peripheral hardware (gamepads, joysticks,
|
||||
* keyboards, mice, etc.).
|
||||
*/
|
||||
public class PeripheralController extends AbstractController implements
|
||||
AbstractProvider.OnInputListener
|
||||
{
|
||||
/** The map from input codes to commands. */
|
||||
private final InputMap mInputMap;
|
||||
|
||||
/** The analog deadzone, between 0 and 1, inclusive. */
|
||||
private final float mDeadzoneFraction;
|
||||
|
||||
/** The analog sensitivity, the amount by which to scale stick values, nominally 1. */
|
||||
private final float mSensitivityFraction;
|
||||
|
||||
/** The user input providers. */
|
||||
private final ArrayList<AbstractProvider> mProviders;
|
||||
|
||||
/** The positive analog-x strength, between 0 and 1, inclusive. */
|
||||
private float mStrengthXpos;
|
||||
|
||||
/** The negative analog-x strength, between 0 and 1, inclusive. */
|
||||
private float mStrengthXneg;
|
||||
|
||||
/** The positive analog-y strength, between 0 and 1, inclusive. */
|
||||
private float mStrengthYpos;
|
||||
|
||||
/** The negative analogy-y strength, between 0 and 1, inclusive. */
|
||||
private float mStrengthYneg;
|
||||
|
||||
/**
|
||||
* Instantiates a new peripheral controller.
|
||||
*
|
||||
* @param player The player number, between 1 and 4, inclusive.
|
||||
* @param inputMap The map from input codes to commands.
|
||||
* @param inputDeadzone The analog deadzone in percent.
|
||||
* @param inputSensitivity The analog sensitivity in percent.
|
||||
* @param providers The user input providers. Null elements are safe.
|
||||
*/
|
||||
public PeripheralController( int player, InputMap inputMap, int inputDeadzone, int inputSensitivity, AbstractProvider... providers )
|
||||
{
|
||||
setPlayerNumber( player );
|
||||
|
||||
// Assign the maps
|
||||
mInputMap = inputMap;
|
||||
mDeadzoneFraction = ( (float) inputDeadzone ) / 100f;
|
||||
mSensitivityFraction = ( (float) inputSensitivity ) / 100f;
|
||||
|
||||
// Assign the non-null input providers
|
||||
mProviders = new ArrayList<AbstractProvider>();
|
||||
for( AbstractProvider provider : providers )
|
||||
{
|
||||
if( provider != null )
|
||||
{
|
||||
mProviders.add( provider );
|
||||
provider.registerListener( this );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@TargetApi( 16 )
|
||||
@Override
|
||||
public void onInput( int inputCode, float strength, int hardwareId )
|
||||
{
|
||||
// Apply user changes to the controller state
|
||||
apply( inputCode, strength );
|
||||
|
||||
// Notify the core that controller state has changed
|
||||
notifyChanged();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onInput( int[] inputCodes, float[] strengths, int hardwareId )
|
||||
{
|
||||
// Apply user changes to the controller state
|
||||
for( int i = 0; i < inputCodes.length; i++ )
|
||||
apply( inputCodes[i], strengths[i] );
|
||||
|
||||
// Notify the core that controller state has changed
|
||||
notifyChanged();
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply user input to the N64 controller state.
|
||||
*
|
||||
* @param inputCode The universal input code that was dispatched.
|
||||
* @param strength The input strength, between 0 and 1, inclusive.
|
||||
*
|
||||
* @return True, if controller state changed.
|
||||
*/
|
||||
private boolean apply( int inputCode, float strength )
|
||||
{
|
||||
boolean keyDown = strength > AbstractProvider.STRENGTH_THRESHOLD;
|
||||
int n64Index = mInputMap.get( inputCode );
|
||||
|
||||
if( n64Index >= 0 && n64Index < NUM_N64_BUTTONS )
|
||||
{
|
||||
mState.buttons[n64Index] = keyDown;
|
||||
return true;
|
||||
}
|
||||
else if( n64Index < InputMap.NUM_N64_CONTROLS )
|
||||
{
|
||||
switch( n64Index )
|
||||
{
|
||||
case InputMap.AXIS_R:
|
||||
mStrengthXpos = strength;
|
||||
break;
|
||||
case InputMap.AXIS_L:
|
||||
mStrengthXneg = strength;
|
||||
break;
|
||||
case InputMap.AXIS_D:
|
||||
mStrengthYneg = strength;
|
||||
break;
|
||||
case InputMap.AXIS_U:
|
||||
mStrengthYpos = strength;
|
||||
break;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
|
||||
// Calculate the net position of the analog stick
|
||||
float rawX = mSensitivityFraction * ( mStrengthXpos - mStrengthXneg );
|
||||
float rawY = mSensitivityFraction * ( mStrengthYpos - mStrengthYneg );
|
||||
float magnitude = (float) Math.sqrt( ( rawX * rawX ) + ( rawY * rawY ) );
|
||||
|
||||
// Update controller state
|
||||
if( magnitude > mDeadzoneFraction )
|
||||
{
|
||||
// Normalize the vector
|
||||
float normalizedX = rawX / magnitude;
|
||||
float normalizedY = rawY / magnitude;
|
||||
|
||||
// Rescale strength to account for deadzone
|
||||
magnitude = ( magnitude - mDeadzoneFraction ) / ( 1f - mDeadzoneFraction );
|
||||
magnitude = Utility.clamp( magnitude, 0f, 1f );
|
||||
mState.axisFractionX = normalizedX * magnitude;
|
||||
mState.axisFractionY = normalizedY * magnitude;
|
||||
}
|
||||
else
|
||||
{
|
||||
// In the deadzone
|
||||
mState.axisFractionX = 0;
|
||||
mState.axisFractionY = 0;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
|
@ -26,7 +26,6 @@ import android.view.View.OnTouchListener;
|
|||
/**
|
||||
* A class for generating N64 controller commands from a touchscreen.
|
||||
*/
|
||||
@SuppressWarnings("deprecation")
|
||||
public class TouchController extends AbstractController implements OnTouchListener
|
||||
{
|
||||
public interface OnStateChangedListener
|
||||
|
|
|
@ -0,0 +1,135 @@
|
|||
/****************************************************************************
|
||||
* *
|
||||
* Project64 - A Nintendo 64 emulator. *
|
||||
* http://www.pj64-emu.com/ *
|
||||
* Copyright (C) 2012 Project64. All rights reserved. *
|
||||
* *
|
||||
* License: *
|
||||
* GNU/GPLv2 http://www.gnu.org/licenses/gpl-2.0.html *
|
||||
* *
|
||||
****************************************************************************/
|
||||
package emu.project64.input.map;
|
||||
|
||||
import emu.project64.input.AbstractController;
|
||||
import emu.project64.input.provider.AbstractProvider;
|
||||
|
||||
/**
|
||||
* A class for mapping arbitrary user inputs to N64 buttons/axes.
|
||||
*
|
||||
* @see AbstractProvider
|
||||
* @see PeripheralController
|
||||
* @see ControllerProfileActivity
|
||||
*/
|
||||
public class InputMap extends SerializableMap
|
||||
{
|
||||
/** Map flag: Input code is not mapped. */
|
||||
public static final int UNMAPPED = -1;
|
||||
|
||||
/** Map offset: N64 non-button controls. */
|
||||
public static final int OFFSET_EXTRAS = AbstractController.NUM_N64_BUTTONS;
|
||||
|
||||
/** N64 control: analog-right. */
|
||||
public static final int AXIS_R = OFFSET_EXTRAS;
|
||||
|
||||
/** N64 control: analog-left. */
|
||||
public static final int AXIS_L = OFFSET_EXTRAS + 1;
|
||||
|
||||
/** N64 control: analog-down. */
|
||||
public static final int AXIS_D = OFFSET_EXTRAS + 2;
|
||||
|
||||
/** N64 control: analog-up. */
|
||||
public static final int AXIS_U = OFFSET_EXTRAS + 3;
|
||||
|
||||
/** Total number of N64 controls. */
|
||||
public static final int NUM_N64_CONTROLS = OFFSET_EXTRAS + 4;
|
||||
/** Total number of mappable controls/functions. */
|
||||
public static final int NUM_MAPPABLES = NUM_N64_CONTROLS;
|
||||
|
||||
public InputMap( String serializedMap )
|
||||
{
|
||||
super( serializedMap );
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the command mapped to a given input code.
|
||||
*
|
||||
* @param inputCode The standardized input code.
|
||||
*
|
||||
* @return The command the code is mapped to, or UNMAPPED.
|
||||
*
|
||||
* @see AbstractProvider
|
||||
* @see InputMap#UNMAPPED
|
||||
*/
|
||||
public int get( int inputCode )
|
||||
{
|
||||
return mMap.get( inputCode, UNMAPPED );
|
||||
}
|
||||
|
||||
/**
|
||||
* Maps an input code to a command.
|
||||
*
|
||||
* @param inputCode The standardized input code to be mapped.
|
||||
* @param command The index to the N64/Mupen command.
|
||||
*/
|
||||
public void map( int inputCode, int command )
|
||||
{
|
||||
// Map the input if a valid index was given
|
||||
if( command >= 0 && command < NUM_MAPPABLES && inputCode != 0 )
|
||||
{
|
||||
if( inputCode < 0 )
|
||||
{
|
||||
// If an analog input is mapped, it should be the only thing mapped to this command
|
||||
unmapCommand( command );
|
||||
}
|
||||
else
|
||||
{
|
||||
// If a digital input is mapped, no analog inputs can be mapped to this command
|
||||
for( int i = mMap.size() - 1; i >= 0; i-- )
|
||||
{
|
||||
if( mMap.valueAt( i ) == command && mMap.keyAt( i ) < 0 )
|
||||
mMap.removeAt( i );
|
||||
}
|
||||
}
|
||||
mMap.put( inputCode, command );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Unmaps a command.
|
||||
*
|
||||
* @param command The index to the command.
|
||||
*/
|
||||
public void unmapCommand( int command )
|
||||
{
|
||||
// Remove any matching key-value pairs (count down to accommodate removal)
|
||||
for( int i = mMap.size() - 1; i >= 0; i-- )
|
||||
{
|
||||
if( mMap.valueAt( i ) == command )
|
||||
mMap.removeAt( i );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if a command is mapped to at least one input code.
|
||||
*
|
||||
* @param command The index to the command.
|
||||
*
|
||||
* @return True, if the mapping exists.
|
||||
*/
|
||||
public boolean isMapped( int command )
|
||||
{
|
||||
return mMap.indexOfValue( command ) >= 0;
|
||||
}
|
||||
public String getMappedCodeInfo( int command )
|
||||
{
|
||||
String result = "";
|
||||
for( int i = 0; i < mMap.size(); i++ )
|
||||
{
|
||||
if( mMap.valueAt( i ) == command )
|
||||
{
|
||||
result += AbstractProvider.getInputName( mMap.keyAt( i ) ) + "\n";
|
||||
}
|
||||
}
|
||||
return result.trim();
|
||||
}
|
||||
}
|
|
@ -33,7 +33,6 @@ import android.util.SparseArray;
|
|||
* @see TouchController
|
||||
*/
|
||||
@SuppressLint("FloatMath")
|
||||
@SuppressWarnings("deprecation")
|
||||
public class TouchMap
|
||||
{
|
||||
/** Map flag: Touch location is not mapped. */
|
||||
|
|
|
@ -30,34 +30,7 @@ import android.util.Log;
|
|||
* @see GameOverlay
|
||||
*/
|
||||
public class VisibleTouchMap extends TouchMap
|
||||
{
|
||||
/** FPS frame image. */
|
||||
private Image mFpsFrame;
|
||||
|
||||
/** X-coordinate of the FPS frame, in percent. */
|
||||
private int mFpsFrameX;
|
||||
|
||||
/** Y-coordinate of the FPS frame, in percent. */
|
||||
private int mFpsFrameY;
|
||||
|
||||
/** X-coordinate of the FPS text centroid, in percent. */
|
||||
private int mFpsTextX;
|
||||
|
||||
/** Y-coordinate of the FPS text centroid, in percent. */
|
||||
private int mFpsTextY;
|
||||
|
||||
/** The current FPS value. */
|
||||
private int mFpsValue;
|
||||
|
||||
/** The minimum size of the FPS indicator in pixels. */
|
||||
private float mFpsMinPixels;
|
||||
|
||||
/** The minimum size to scale the FPS indicator. */
|
||||
private float mFpsMinScale;
|
||||
|
||||
/** True if the FPS indicator should be drawn. */
|
||||
private boolean mFpsEnabled;
|
||||
|
||||
{
|
||||
/** The factor to scale images by. */
|
||||
private float mScalingFactor = 1.0f;
|
||||
|
||||
|
@ -71,20 +44,14 @@ public class VisibleTouchMap extends TouchMap
|
|||
private int mReferenceHeight = 0;
|
||||
|
||||
/** The last width passed to {@link #resize(int, int, DisplayMetrics)}. */
|
||||
private int cacheWidth = 0;
|
||||
private static int cacheWidth = 0;
|
||||
|
||||
/** The last height passed to {@link #resize(int, int, DisplayMetrics)}. */
|
||||
private int cacheHeight = 0;
|
||||
private static int cacheHeight = 0;
|
||||
|
||||
/** The last height passed to {@link #resize(int, int, DisplayMetrics)}. */
|
||||
private DisplayMetrics cacheMetrics;
|
||||
|
||||
/** The set of images representing the FPS string. */
|
||||
private final CopyOnWriteArrayList<Image> mFpsDigits;
|
||||
|
||||
/** The set of images representing the numerals 0, 1, 2, ..., 9. */
|
||||
private final Image[] mNumerals;
|
||||
|
||||
/** Auto-hold overlay images. */
|
||||
public final Image[] autoHoldImages;
|
||||
|
||||
|
@ -102,8 +69,6 @@ public class VisibleTouchMap extends TouchMap
|
|||
public VisibleTouchMap( Resources resources )
|
||||
{
|
||||
super( resources );
|
||||
mFpsDigits = new CopyOnWriteArrayList<Image>();
|
||||
mNumerals = new Image[10];
|
||||
autoHoldImages = new Image[NUM_N64_PSEUDOBUTTONS];
|
||||
autoHoldX = new int[NUM_N64_PSEUDOBUTTONS];
|
||||
autoHoldY = new int[NUM_N64_PSEUDOBUTTONS];
|
||||
|
@ -118,13 +83,6 @@ public class VisibleTouchMap extends TouchMap
|
|||
public void clear()
|
||||
{
|
||||
super.clear();
|
||||
mFpsFrame = null;
|
||||
mFpsFrameX = mFpsFrameY = 0;
|
||||
mFpsTextX = mFpsTextY = 50;
|
||||
mFpsValue = 0;
|
||||
mFpsDigits.clear();
|
||||
for( int i = 0; i < mNumerals.length; i++ )
|
||||
mNumerals[i] = null;
|
||||
for( int i = 0; i < autoHoldImages.length; i++ )
|
||||
autoHoldImages[i] = null;
|
||||
for( int i = 0; i < autoHoldX.length; i++ )
|
||||
|
@ -195,25 +153,6 @@ public class VisibleTouchMap extends TouchMap
|
|||
autoHoldImages[i].fitPercent( autoHoldX[i], autoHoldY[i], w, h );
|
||||
}
|
||||
}
|
||||
|
||||
// Compute FPS frame location
|
||||
float fpsScale = scale;
|
||||
if( mFpsMinScale > scale )
|
||||
fpsScale = mFpsMinScale;
|
||||
if( mFpsFrame != null )
|
||||
{
|
||||
mFpsFrame.setScale( fpsScale );
|
||||
mFpsFrame.fitPercent( mFpsFrameX, mFpsFrameY, w, h );
|
||||
}
|
||||
for( int i = 0; i < mNumerals.length; i++ )
|
||||
{
|
||||
if( mNumerals[i] != null )
|
||||
mNumerals[i].setScale( fpsScale );
|
||||
}
|
||||
|
||||
// Compute the FPS digit locations
|
||||
refreshFpsImages();
|
||||
refreshFpsPositions();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -267,25 +206,6 @@ public class VisibleTouchMap extends TouchMap
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Draws the FPS indicator.
|
||||
*
|
||||
* @param canvas The canvas on which to draw.
|
||||
*/
|
||||
public void drawFps( Canvas canvas )
|
||||
{
|
||||
if( canvas == null )
|
||||
return;
|
||||
|
||||
// Redraw the FPS indicator
|
||||
if( mFpsFrame != null )
|
||||
mFpsFrame.draw( canvas );
|
||||
|
||||
// Draw each digit of the FPS number
|
||||
for( Image digit : mFpsDigits )
|
||||
digit.draw( canvas );
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the analog stick assets to reflect a new position.
|
||||
*
|
||||
|
@ -318,32 +238,6 @@ public class VisibleTouchMap extends TouchMap
|
|||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the FPS indicator assets to reflect a new value.
|
||||
*
|
||||
* @param fps The new FPS value.
|
||||
*
|
||||
* @return True if the FPS assets changed.
|
||||
*/
|
||||
public boolean updateFps( int fps )
|
||||
{
|
||||
// Clamp to positive, four digits max [0 - 9999]
|
||||
fps = Utility.clamp( fps, 0, 9999 );
|
||||
|
||||
// Quick return if user has disabled FPS or it hasn't changed
|
||||
if( !mFpsEnabled || mFpsValue == fps )
|
||||
return false;
|
||||
|
||||
// Store the new value
|
||||
mFpsValue = fps;
|
||||
|
||||
// Refresh the FPS digits
|
||||
refreshFpsImages();
|
||||
refreshFpsPositions();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the auto-hold assets to reflect a new value.
|
||||
*
|
||||
|
@ -364,73 +258,18 @@ public class VisibleTouchMap extends TouchMap
|
|||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Refreshes the images used to draw the FPS string.
|
||||
*/
|
||||
private void refreshFpsImages()
|
||||
{
|
||||
// Refresh the list of FPS digits
|
||||
String fpsString = Integer.toString( mFpsValue );
|
||||
mFpsDigits.clear();
|
||||
for( int i = 0; i < 4; i++ )
|
||||
{
|
||||
// Create a new sequence of numeral images
|
||||
if( i < fpsString.length() )
|
||||
{
|
||||
int numeral = SafeMethods.toInt( fpsString.substring( i, i + 1 ), -1 );
|
||||
if( numeral > -1 && numeral < 10 )
|
||||
{
|
||||
// Clone the numeral from the font images and move to next digit
|
||||
mFpsDigits.add( new Image( mResources, mNumerals[numeral] ) );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Refreshes the positions of the FPS images.
|
||||
*/
|
||||
private void refreshFpsPositions()
|
||||
{
|
||||
// Compute the centroid of the FPS text
|
||||
int x = 0;
|
||||
int y = 0;
|
||||
if( mFpsFrame != null )
|
||||
{
|
||||
x = mFpsFrame.x + (int) ( ( mFpsFrame.width * mFpsFrame.scale ) * ( mFpsTextX / 100f ) );
|
||||
y = mFpsFrame.y + (int) ( ( mFpsFrame.height * mFpsFrame.scale ) * ( mFpsTextY / 100f ) );
|
||||
}
|
||||
|
||||
// Compute the width of the FPS text
|
||||
int totalWidth = 0;
|
||||
for( Image digit : mFpsDigits )
|
||||
totalWidth += (int) ( digit.width * digit.scale );
|
||||
|
||||
// Compute the starting position of the FPS text
|
||||
x -= (int) ( totalWidth / 2f );
|
||||
|
||||
// Compute the position of each digit
|
||||
for( Image digit : mFpsDigits )
|
||||
{
|
||||
digit.setPos( x, y - (int) ( digit.hHeight * digit.scale ) );
|
||||
x += (int) ( digit.width * digit.scale );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Loads all touch map data from the filesystem.
|
||||
*
|
||||
* @param skinDir The directory containing the skin.ini and image files.
|
||||
* @param profile The name of the touchscreen profile.
|
||||
* @param animated True to load the analog assets in two parts for animation.
|
||||
* @param fpsEnabled True to display the FPS indicator.
|
||||
* @param scale The factor to scale images by.
|
||||
* @param alpha The opacity of the visible elements.
|
||||
*/
|
||||
public void load( String skinDir, Profile profile, boolean animated, boolean fpsEnabled, float scale, int alpha )
|
||||
public void load( String skinDir, Profile profile, boolean animated, float scale, int alpha )
|
||||
{
|
||||
mFpsEnabled = fpsEnabled;
|
||||
mScalingFactor = scale;
|
||||
mTouchscreenTransparency = alpha;
|
||||
|
||||
|
@ -438,9 +277,6 @@ public class VisibleTouchMap extends TouchMap
|
|||
ConfigFile skin_ini = new ConfigFile( skinFolder + "/skin.ini" );
|
||||
mReferenceWidth = SafeMethods.toInt( skin_ini.get( "INFO", "referenceScreenWidth" ), 0 );
|
||||
mReferenceHeight = SafeMethods.toInt( skin_ini.get( "INFO", "referenceScreenHeight" ), 0 );
|
||||
mFpsTextX = SafeMethods.toInt( skin_ini.get( "INFO", "fps-numx" ), 50 );
|
||||
mFpsTextY = SafeMethods.toInt( skin_ini.get( "INFO", "fps-numy" ), 50 );
|
||||
mFpsMinPixels = SafeMethods.toInt( skin_ini.get( "INFO", "fps-minPixels" ), 0 );
|
||||
|
||||
// Scale the assets to the last screensize used
|
||||
resize( cacheWidth, cacheHeight, cacheMetrics );
|
||||
|
@ -472,10 +308,9 @@ public class VisibleTouchMap extends TouchMap
|
|||
analogForeImage.setAlpha( mTouchscreenTransparency );
|
||||
}
|
||||
|
||||
// Load the FPS and autohold images
|
||||
// Load the autohold images
|
||||
if( profile != null )
|
||||
{
|
||||
loadFpsIndicator( profile );
|
||||
loadAutoHoldImages( profile, "groupAB-holdA" );
|
||||
loadAutoHoldImages( profile, "groupAB-holdB" );
|
||||
loadAutoHoldImages( profile, "groupC-holdCu" );
|
||||
|
@ -489,48 +324,6 @@ public class VisibleTouchMap extends TouchMap
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads FPS indicator assets and properties from the filesystem.
|
||||
*
|
||||
* @param profile The touchscreen profile containing the FPS properties.
|
||||
*/
|
||||
private void loadFpsIndicator( Profile profile )
|
||||
{
|
||||
int x = profile.getInt( "fps-x", -1 );
|
||||
int y = profile.getInt( "fps-y", -1 );
|
||||
|
||||
if( x >= 0 && y >= 0 )
|
||||
{
|
||||
// Position (percentages of the screen dimensions)
|
||||
mFpsFrameX = x;
|
||||
mFpsFrameY = y;
|
||||
|
||||
// Load frame image
|
||||
mFpsFrame = new Image( mResources, skinFolder + "/fps.png" );
|
||||
|
||||
// Minimum factor the FPS indicator can be scaled by
|
||||
mFpsMinScale = mFpsMinPixels / (float) mFpsFrame.width;
|
||||
|
||||
// Load numeral images
|
||||
String filename = "";
|
||||
try
|
||||
{
|
||||
// Make sure we can load them (they might not even exist)
|
||||
for( int i = 0; i < mNumerals.length; i++ )
|
||||
{
|
||||
filename = skinFolder + "/fps-" + i + ".png";
|
||||
mNumerals[i] = new Image( mResources, filename );
|
||||
}
|
||||
}
|
||||
catch( Exception e )
|
||||
{
|
||||
// Problem, let the user know
|
||||
Log.e( "VisibleTouchMap", "Problem loading fps numeral '" + filename
|
||||
+ "', error message: " + e.getMessage() );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads auto-hold assets and properties from the filesystem.
|
||||
*
|
||||
|
|
|
@ -0,0 +1,391 @@
|
|||
/****************************************************************************
|
||||
* *
|
||||
* Project64 - A Nintendo 64 emulator. *
|
||||
* http://www.pj64-emu.com/ *
|
||||
* Copyright (C) 2016 Project64. All rights reserved. *
|
||||
* Copyright (C) 2013 Paul Lamb
|
||||
* *
|
||||
* License: *
|
||||
* GNU/GPLv2 http://www.gnu.org/licenses/gpl-2.0.html *
|
||||
* *
|
||||
****************************************************************************/
|
||||
package emu.project64.input.provider;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
import emu.project64.AndroidDevice;
|
||||
import tv.ouya.console.api.OuyaController;
|
||||
import android.annotation.TargetApi;
|
||||
import android.view.InputDevice;
|
||||
import android.view.KeyEvent;
|
||||
import android.view.MotionEvent;
|
||||
|
||||
/**
|
||||
* The base class for transforming arbitrary input data into a common format.
|
||||
*
|
||||
* @see KeyProvider
|
||||
* @see AxisProvider
|
||||
* @see SensorProvider
|
||||
* @see InputMap
|
||||
*/
|
||||
public abstract class AbstractProvider
|
||||
{
|
||||
/** The offset used to construct the hardware ID for a MOGA controller. */
|
||||
private static final int HARDWARE_ID_MOGA_OFFSET = 1000;
|
||||
|
||||
/** The maximum possible hardware ID for a MOGA controller, inclusive. */
|
||||
private static final int HARDWARE_ID_MOGA_MAX = 1010;
|
||||
|
||||
/**
|
||||
* The interface for listening to a provider.
|
||||
*/
|
||||
public interface OnInputListener
|
||||
{
|
||||
/**
|
||||
* Called when a single input has been dispatched.
|
||||
*
|
||||
* @param inputCode The universal input code that was dispatched.
|
||||
* @param strength The input strength, between 0 and 1, inclusive.
|
||||
* @param hardwareId The identifier of the source device.
|
||||
*/
|
||||
public void onInput( int inputCode, float strength, int hardwareId );
|
||||
|
||||
/**
|
||||
* Called when multiple inputs have been dispatched simultaneously.
|
||||
*
|
||||
* @param inputCodes The universal input codes that were dispatched.
|
||||
* @param strengths The input strengths, between 0 and 1, inclusive.
|
||||
* @param hardwareId The identifier of the source device.
|
||||
*/
|
||||
public void onInput( int[] inputCodes, float[] strengths, int hardwareId );
|
||||
}
|
||||
|
||||
/** The strength threshold above which an input is said to be "on". */
|
||||
public static final float STRENGTH_THRESHOLD = 0.5f;
|
||||
|
||||
/** Listener management. */
|
||||
private final ArrayList<AbstractProvider.OnInputListener> mPublisher;
|
||||
|
||||
/**
|
||||
* Instantiates a new abstract provider.
|
||||
*/
|
||||
protected AbstractProvider()
|
||||
{
|
||||
mPublisher = new ArrayList<AbstractProvider.OnInputListener>();
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers a listener to start receiving input notifications.
|
||||
*
|
||||
* @param listener The listener to register. Null values are safe.
|
||||
*/
|
||||
public void registerListener( AbstractProvider.OnInputListener listener )
|
||||
{
|
||||
if( ( listener != null ) && !mPublisher.contains( listener ) )
|
||||
{
|
||||
mPublisher.add( listener );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Unregisters a listener to stop receiving input notifications.
|
||||
*
|
||||
* @param listener The listener to unregister. Null values are safe.
|
||||
*/
|
||||
public void unregisterListener( AbstractProvider.OnInputListener listener )
|
||||
{
|
||||
if( listener != null )
|
||||
{
|
||||
mPublisher.remove( listener );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Unregisters all listeners.
|
||||
*/
|
||||
public void unregisterAllListeners()
|
||||
{
|
||||
mPublisher.clear();
|
||||
}
|
||||
|
||||
/**
|
||||
* Obtains unique hardware id from an input event.
|
||||
*
|
||||
* @param event The event generated by the hardware.
|
||||
*
|
||||
* @return The unique identifier of the hardware.
|
||||
*/
|
||||
public static int getHardwareId( KeyEvent event )
|
||||
{
|
||||
// This might be replaced by something else in the future... so we abstract it
|
||||
return event.getDeviceId();
|
||||
}
|
||||
|
||||
/**
|
||||
* Obtains unique hardware id from an input event.
|
||||
*
|
||||
* @param event The event generated by the hardware.
|
||||
*
|
||||
* @return The unique identifier of the hardware.
|
||||
*/
|
||||
public static int getHardwareId( MotionEvent event )
|
||||
{
|
||||
// This might be replaced by something else in the future... so we abstract it
|
||||
return event.getDeviceId();
|
||||
}
|
||||
|
||||
/**
|
||||
* Obtains unique hardware id from an input event.
|
||||
*
|
||||
* @param event The event generated by the hardware.
|
||||
*
|
||||
* @return The unique identifier of the hardware.
|
||||
*/
|
||||
public static int getHardwareId( com.bda.controller.KeyEvent event )
|
||||
{
|
||||
// This might be replaced by something else in the future... so we abstract it
|
||||
return event.getControllerId() + HARDWARE_ID_MOGA_OFFSET;
|
||||
}
|
||||
|
||||
/**
|
||||
* Obtains unique hardware id from an input event.
|
||||
*
|
||||
* @param event The event generated by the hardware.
|
||||
*
|
||||
* @return The unique identifier of the hardware.
|
||||
*/
|
||||
public static int getHardwareId( com.bda.controller.MotionEvent event )
|
||||
{
|
||||
// This might be replaced by something else in the future... so we abstract it
|
||||
return event.getControllerId() + HARDWARE_ID_MOGA_OFFSET;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines whether the hardware is available. This is a conservative test in that it will
|
||||
* return true if the availability cannot be conclusively determined.
|
||||
*
|
||||
* @param id The unique hardware identifier.
|
||||
*
|
||||
* @return True if the associated hardware is available or indeterminate.
|
||||
*/
|
||||
@TargetApi( 9 )
|
||||
public static boolean isHardwareAvailable( int id )
|
||||
{
|
||||
// This might be replaced by something else in the future... so we abstract it
|
||||
if( id > HARDWARE_ID_MOGA_OFFSET && id <= HARDWARE_ID_MOGA_MAX )
|
||||
{
|
||||
return true;
|
||||
}
|
||||
else if( AndroidDevice.IS_GINGERBREAD )
|
||||
{
|
||||
InputDevice device = InputDevice.getDevice( id );
|
||||
return device != null;
|
||||
}
|
||||
else
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the human-readable name of the hardware.
|
||||
*
|
||||
* @param id The unique hardware identifier.
|
||||
*
|
||||
* @return The name of the hardware, or null if hardware not found.
|
||||
*/
|
||||
@TargetApi( 9 )
|
||||
public static String getHardwareName( int id )
|
||||
{
|
||||
if( id > HARDWARE_ID_MOGA_OFFSET && id <= HARDWARE_ID_MOGA_MAX )
|
||||
{
|
||||
return "moga-" + ( id - HARDWARE_ID_MOGA_OFFSET );
|
||||
}
|
||||
else if( AndroidDevice.IS_GINGERBREAD )
|
||||
{
|
||||
InputDevice device = InputDevice.getDevice( id );
|
||||
return device == null ? null : device.getName();
|
||||
}
|
||||
else
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the human-readable name of the input.
|
||||
*
|
||||
* @param inputCode The universal input code.
|
||||
*
|
||||
* @return The name of the input.
|
||||
*/
|
||||
@SuppressWarnings( "deprecation" )
|
||||
@TargetApi( 12 )
|
||||
public static String getInputName( int inputCode )
|
||||
{
|
||||
if( inputCode > 0 )
|
||||
{
|
||||
if( AndroidDevice.IS_HONEYCOMB_MR1 )
|
||||
{
|
||||
String name = null;
|
||||
if( inputCode != -1 && AndroidDevice.IS_OUYA_HARDWARE )
|
||||
{
|
||||
if( inputCode == OuyaController.BUTTON_A )
|
||||
name = "OUYA BUTTON_A";
|
||||
else if( inputCode == OuyaController.BUTTON_DPAD_DOWN )
|
||||
name = "OUYA BUTTON_DPAD_DOWN";
|
||||
else if( inputCode == OuyaController.BUTTON_DPAD_LEFT )
|
||||
name = "OUYA BUTTON_DPAD_LEFT";
|
||||
else if( inputCode == OuyaController.BUTTON_DPAD_RIGHT )
|
||||
name = "OUYA BUTTON_DPAD_RIGHT";
|
||||
else if( inputCode == OuyaController.BUTTON_DPAD_UP )
|
||||
name = "OUYA BUTTON_DPAD_UP";
|
||||
else if( inputCode == OuyaController.BUTTON_L1 )
|
||||
name = "OUYA BUTTON_L1";
|
||||
else if( inputCode == OuyaController.BUTTON_L2 )
|
||||
name = "OUYA BUTTON_L2";
|
||||
else if( inputCode == OuyaController.BUTTON_L3 )
|
||||
name = "OUYA BUTTON_L3";
|
||||
else if( inputCode == OuyaController.BUTTON_MENU )
|
||||
name = "OUYA BUTTON_MENU";
|
||||
else if( inputCode == OuyaController.BUTTON_O )
|
||||
name = "OUYA BUTTON_O";
|
||||
else if( inputCode == OuyaController.BUTTON_R1 )
|
||||
name = "OUYA BUTTON_R1";
|
||||
else if( inputCode == OuyaController.BUTTON_R2 )
|
||||
name = "OUYA BUTTON_R2";
|
||||
else if( inputCode == OuyaController.BUTTON_R3 )
|
||||
name = "OUYA BUTTON_R3";
|
||||
else if( inputCode == OuyaController.BUTTON_U )
|
||||
name = "OUYA BUTTON_U";
|
||||
else if( inputCode == OuyaController.BUTTON_Y )
|
||||
name = "OUYA BUTTON_Y";
|
||||
}
|
||||
if( name == null )
|
||||
return KeyEvent.keyCodeToString( inputCode );
|
||||
else
|
||||
return name + " (" + KeyEvent.keyCodeToString( inputCode ) + ")";
|
||||
}
|
||||
else
|
||||
return "KEYCODE_" + inputCode;
|
||||
}
|
||||
else if( inputCode < 0 )
|
||||
{
|
||||
int axis = inputToAxisCode( inputCode );
|
||||
String direction = inputToAxisDirection( inputCode ) ? " (+)" : " (-)";
|
||||
if( AndroidDevice.IS_HONEYCOMB_MR1 )
|
||||
{
|
||||
String name = null;
|
||||
if( axis != -1 && AndroidDevice.IS_OUYA_HARDWARE )
|
||||
{
|
||||
if( axis == OuyaController.AXIS_L2 )
|
||||
name = "OUYA AXIS_L2";
|
||||
else if( axis == OuyaController.AXIS_LS_X )
|
||||
name = "OUYA AXIS_LS_X";
|
||||
else if( axis == OuyaController.AXIS_LS_Y )
|
||||
name = "OUYA AXIS_LS_Y";
|
||||
else if( axis == OuyaController.AXIS_R2 )
|
||||
name = "OUYA AXIS_R2";
|
||||
else if( axis == OuyaController.AXIS_RS_X )
|
||||
name = "OUYA AXIS_RS_X";
|
||||
else if( axis == OuyaController.AXIS_RS_Y )
|
||||
name = "OUYA AXIS_RS_Y";
|
||||
}
|
||||
if( name == null )
|
||||
return MotionEvent.axisToString( axis ) + direction;
|
||||
else
|
||||
return name + " (" + MotionEvent.axisToString( axis ) + ")" + direction;
|
||||
}
|
||||
else
|
||||
return "AXIS_" + axis + direction;
|
||||
}
|
||||
else
|
||||
return "NULL";
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the human-readable name of the input, appended with strength information.
|
||||
*
|
||||
* @param inputCode The universal input code.
|
||||
* @param strength The input strength, between 0 and 1, inclusive.
|
||||
*
|
||||
* @return The name of the input.
|
||||
*/
|
||||
public static String getInputName( int inputCode, float strength )
|
||||
{
|
||||
return getInputName( inputCode ) + ( inputCode == 0
|
||||
? ""
|
||||
: String.format( " %4.2f", strength ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Utility for child classes. Converts an Android axis code to a universal input code.
|
||||
*
|
||||
* @param axisCode The Android axis code.
|
||||
* @param positiveDirection Set true for positive Android axis, false for negative Android axis.
|
||||
*
|
||||
* @return The corresponding universal input code.
|
||||
*/
|
||||
protected static int axisToInputCode( int axisCode, boolean positiveDirection )
|
||||
{
|
||||
// Axis codes are encoded to negative values (versus buttons which are positive). Axis codes
|
||||
// are bit shifted by one so that the lowest bit can encode axis direction.
|
||||
return -( ( axisCode ) * 2 + ( positiveDirection ? 1 : 2 ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Utility for child classes. Converts a universal input code to an Android axis code.
|
||||
*
|
||||
* @param inputCode The universal input code.
|
||||
*
|
||||
* @return The corresponding Android axis code.
|
||||
*/
|
||||
protected static int inputToAxisCode( int inputCode )
|
||||
{
|
||||
return ( -inputCode - 1 ) / 2;
|
||||
}
|
||||
|
||||
/**
|
||||
* Utility for child classes. Converts a universal input code to an Android axis direction.
|
||||
*
|
||||
* @param inputCode The universal input code.
|
||||
*
|
||||
* @return True if the input code represents positive Android axis direction, false otherwise.
|
||||
*/
|
||||
protected static boolean inputToAxisDirection( int inputCode )
|
||||
{
|
||||
return ( ( -inputCode ) % 2 ) == 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Notifies listeners that a single input was dispatched. Subclasses should invoke this method
|
||||
* to publish their input data.
|
||||
*
|
||||
* @param inputCode The universal input code that was dispatched.
|
||||
* @param strength The input strength, between 0 and 1, inclusive.
|
||||
* @param hardwareId The identifier of the source device.
|
||||
*/
|
||||
protected void notifyListeners( int inputCode, float strength, int hardwareId )
|
||||
{
|
||||
for( OnInputListener listener : mPublisher )
|
||||
{
|
||||
listener.onInput( inputCode, strength, hardwareId );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Notifies listeners that multiple inputs were dispatched simultaneously. Subclasses should
|
||||
* invoke this method to publish their input data.
|
||||
*
|
||||
* @param inputCodes The universal input codes that were dispatched.
|
||||
* @param strengths The input strengths, between 0 and 1, inclusive.
|
||||
* @param hardwareId The identifier of the source device.
|
||||
*/
|
||||
protected void notifyListeners( int[] inputCodes, float[] strengths, int hardwareId )
|
||||
{
|
||||
for( OnInputListener listener : mPublisher )
|
||||
{
|
||||
listener.onInput( inputCodes.clone(), strengths.clone(), hardwareId );
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,193 @@
|
|||
/****************************************************************************
|
||||
* *
|
||||
* Project64 - A Nintendo 64 emulator. *
|
||||
* http://www.pj64-emu.com/ *
|
||||
* Copyright (C) 2016 Project64. All rights reserved. *
|
||||
* Copyright (C) 2013 Paul Lamb, littleguy77
|
||||
* *
|
||||
* License: *
|
||||
* GNU/GPLv2 http://www.gnu.org/licenses/gpl-2.0.html *
|
||||
* *
|
||||
****************************************************************************/
|
||||
package emu.project64.input.provider;
|
||||
|
||||
import emu.project64.AndroidDevice;
|
||||
import emu.project64.input.map.AxisMap;
|
||||
import android.annotation.TargetApi;
|
||||
import android.view.InputDevice;
|
||||
import android.view.InputDevice.MotionRange;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.View;
|
||||
|
||||
/**
|
||||
* A class for transforming Android MotionEvent inputs into a common format.
|
||||
*/
|
||||
public class AxisProvider extends AbstractProvider
|
||||
{
|
||||
/** The input codes to listen for. */
|
||||
private int[] mInputCodes;
|
||||
|
||||
/** The default number of input codes to listen for. */
|
||||
private static final int DEFAULT_NUM_INPUTS = 128;
|
||||
|
||||
/**
|
||||
* Instantiates a new axis provider.
|
||||
*/
|
||||
@TargetApi( 12 )
|
||||
public AxisProvider()
|
||||
{
|
||||
// By default, provide data from all possible axes
|
||||
mInputCodes = new int[DEFAULT_NUM_INPUTS];
|
||||
for( int i = 0; i < mInputCodes.length; i++ )
|
||||
mInputCodes[i] = -( i + 1 );
|
||||
}
|
||||
|
||||
/**
|
||||
* Instantiates a new axis provider.
|
||||
*
|
||||
* @param view The view receiving MotionEvent data.
|
||||
*/
|
||||
@TargetApi( 12 )
|
||||
public AxisProvider( View view )
|
||||
{
|
||||
this();
|
||||
|
||||
// Connect the input source
|
||||
view.setOnGenericMotionListener( new GenericMotionListener() );
|
||||
|
||||
// Request focus for proper listening
|
||||
view.requestFocus();
|
||||
}
|
||||
|
||||
/**
|
||||
* Restricts listening to a set of universal input codes.
|
||||
*
|
||||
* @param inputCodeFilter The new input codes to listen for.
|
||||
*/
|
||||
public void setInputCodeFilter( int[] inputCodeFilter )
|
||||
{
|
||||
mInputCodes = inputCodeFilter.clone();
|
||||
}
|
||||
|
||||
/**
|
||||
* Manually dispatches a MotionEvent through the provider's listening chain.
|
||||
*
|
||||
* @param event The MotionEvent object containing full information about the event.
|
||||
*
|
||||
* @return True if the listener has consumed the event, false otherwise.
|
||||
*/
|
||||
public boolean onGenericMotion( MotionEvent event )
|
||||
{
|
||||
if( AndroidDevice.IS_HONEYCOMB_MR1 )
|
||||
return new GenericMotionListener().onGenericMotion( null, event );
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Just an indirection class that eliminates some logcat chatter about benign errors. If we make
|
||||
* the parent class implement View.OnGenericMotionListener, then we get logcat error messages
|
||||
* <i>even if</i> we conditionally exclude calls to the class based on API. These errors are not
|
||||
* actually harmful, so the logcat messages are simply a nuisance during debugging.
|
||||
* <p/>
|
||||
* For a detailed explanation, see <a href=http://stackoverflow.com/questions/13103902/
|
||||
* android-recommended-way-of-safely-supporting-newer-apis-has-error-if-the-class-i>here</a>.
|
||||
*/
|
||||
@TargetApi( 12 )
|
||||
public class GenericMotionListener implements View.OnGenericMotionListener
|
||||
{
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see android.view.View.OnGenericMotionListener#onGenericMotion(android.view.View,
|
||||
* android.view.MotionEvent)
|
||||
*/
|
||||
@Override
|
||||
public boolean onGenericMotion( View v, MotionEvent event )
|
||||
{
|
||||
// Ignore motion events from non-joysticks (mice are a problem)
|
||||
if( event.getSource() != InputDevice.SOURCE_JOYSTICK )
|
||||
return false;
|
||||
|
||||
InputDevice device = event.getDevice();
|
||||
AxisMap axisInfo = AxisMap.getMap( device );
|
||||
|
||||
// Read all the requested axes
|
||||
float[] strengths = new float[mInputCodes.length];
|
||||
for( int i = 0; i < mInputCodes.length; i++ )
|
||||
{
|
||||
int inputCode = mInputCodes[i];
|
||||
|
||||
// Compute the axis code from the input code
|
||||
int axisCode = inputToAxisCode( inputCode );
|
||||
|
||||
// Get the analog value using the Android API
|
||||
float strength = event.getAxisValue( axisCode );
|
||||
|
||||
// Modify strength if necessary
|
||||
strength = normalizeStrength( strength, axisInfo, device, axisCode );
|
||||
|
||||
// If the strength points in the correct direction, record it
|
||||
boolean direction1 = inputToAxisDirection( inputCode );
|
||||
boolean direction2 = strength > 0;
|
||||
if( direction1 == direction2 )
|
||||
strengths[i] = Math.abs( strength );
|
||||
else
|
||||
strengths[i] = 0;
|
||||
}
|
||||
|
||||
// Notify listeners about new input data
|
||||
notifyListeners( mInputCodes, strengths, getHardwareId( event ) );
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private float normalizeStrength( float strength, AxisMap axisInfo, InputDevice device,
|
||||
int axisCode )
|
||||
{
|
||||
if( axisInfo != null )
|
||||
{
|
||||
int axisClass = axisInfo.getClass( axisCode );
|
||||
|
||||
if( axisClass == AxisMap.AXIS_CLASS_IGNORED )
|
||||
{
|
||||
// We should ignore this axis
|
||||
strength = 0;
|
||||
}
|
||||
else if( device != null )
|
||||
{
|
||||
// We should normalize this axis
|
||||
MotionRange motionRange = device.getMotionRange( axisCode, InputDevice.SOURCE_JOYSTICK );
|
||||
if( motionRange != null )
|
||||
{
|
||||
switch( axisClass )
|
||||
{
|
||||
case AxisMap.AXIS_CLASS_STICK:
|
||||
// Normalize to [-1,1]
|
||||
strength = ( strength - motionRange.getMin() ) / motionRange.getRange() * 2f - 1f;
|
||||
break;
|
||||
case AxisMap.AXIS_CLASS_TRIGGER:
|
||||
// Normalize to [0,1]
|
||||
strength = ( strength - motionRange.getMin() ) / motionRange.getRange();
|
||||
break;
|
||||
case AxisMap.AXIS_CLASS_N64_USB_STICK:
|
||||
// Normalize to [-1,1]
|
||||
// The Raphnet adapters through v2.x and some other USB adapters assume the N64
|
||||
// controller produces values in the range [-127,127]. However, the official N64 spec
|
||||
// says that raw values of +/- 80 indicate full strength. Therefore we rescale by
|
||||
// multiplying by 127/80 (dividing by 0.63).
|
||||
// http://naesten.dyndns.org:8080/psyq/man/os/osContGetReadData.html
|
||||
// http://raphnet-tech.com/products/gc_n64_usb_adapters/
|
||||
strength = strength / 0.63f;
|
||||
break;
|
||||
case AxisMap.AXIS_CLASS_UNKNOWN:
|
||||
default:
|
||||
// Do nothing
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return strength;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,172 @@
|
|||
/****************************************************************************
|
||||
* *
|
||||
* Project64 - A Nintendo 64 emulator. *
|
||||
* http://www.pj64-emu.com/ *
|
||||
* Copyright (C) 2016 Project64. All rights reserved. *
|
||||
* Copyright (C) 2013 Paul Lamb, littleguy77
|
||||
* *
|
||||
* License: *
|
||||
* GNU/GPLv2 http://www.gnu.org/licenses/gpl-2.0.html *
|
||||
* *
|
||||
****************************************************************************/
|
||||
package emu.project64.input.provider;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import android.app.AlertDialog.Builder;
|
||||
import android.content.DialogInterface;
|
||||
import android.view.KeyEvent;
|
||||
import android.view.View;
|
||||
|
||||
/**
|
||||
* A class for transforming Android KeyEvent inputs into a common format.
|
||||
*/
|
||||
public class KeyProvider extends AbstractProvider implements View.OnKeyListener,
|
||||
DialogInterface.OnKeyListener
|
||||
{
|
||||
/**
|
||||
* The formula for decoding KeyEvent data for specific Android IMEs.
|
||||
*/
|
||||
public enum ImeFormula
|
||||
{
|
||||
/** The default decoding formula. */
|
||||
DEFAULT,
|
||||
/** The formula for <i>USB/BT Joystick Center</i>, by Poke64738. */
|
||||
USB_BT_JOYSTICK_CENTER,
|
||||
/** The formula for <i>BT Controller</i>, by droidbean. */
|
||||
BT_CONTROLLER,
|
||||
/** An example decoding formula. */
|
||||
EXAMPLE_IME
|
||||
}
|
||||
|
||||
/** The IME formula for decoding KeyEvent data. */
|
||||
private final ImeFormula mImeFormula;
|
||||
|
||||
/** The list of key codes that should be ignored. */
|
||||
private final List<Integer> mIgnoredCodes;
|
||||
|
||||
/**
|
||||
* Instantiates a new key provider.
|
||||
*
|
||||
* @param formula The decoding formula to be used.
|
||||
* @param ignoredCodes List of key codes that should be ignored.
|
||||
*/
|
||||
public KeyProvider( ImeFormula formula, List<Integer> ignoredCodes )
|
||||
{
|
||||
// Assign the fields
|
||||
mImeFormula = formula;
|
||||
mIgnoredCodes = ignoredCodes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Instantiates a new key provider.
|
||||
*
|
||||
* @param view The view receiving KeyEvent data.
|
||||
* @param formula The decoding formula to be used.
|
||||
* @param ignoredCodes List of key codes that should be ignored.
|
||||
*/
|
||||
public KeyProvider( View view, ImeFormula formula, List<Integer> ignoredCodes )
|
||||
{
|
||||
this( formula, ignoredCodes );
|
||||
|
||||
// Connect the input source
|
||||
view.setOnKeyListener( this );
|
||||
|
||||
// Request focus for proper listening
|
||||
view.requestFocus();
|
||||
}
|
||||
|
||||
/**
|
||||
* Instantiates a new key provider.
|
||||
*
|
||||
* @param builder The builder for the dialog receiving KeyEvent data.
|
||||
* @param formula The decoding formula to be used.
|
||||
* @param ignoredCodes List of key codes that should be ignored.
|
||||
*/
|
||||
public KeyProvider( Builder builder, ImeFormula formula, List<Integer> ignoredCodes )
|
||||
{
|
||||
this( formula, ignoredCodes );
|
||||
|
||||
// Connect the input source
|
||||
builder.setOnKeyListener( this );
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see android.view.View.OnKeyListener#onKey(android.view.View, int, android.view.KeyEvent)
|
||||
*/
|
||||
@Override
|
||||
public boolean onKey( View v, int keyCode, KeyEvent event )
|
||||
{
|
||||
return onKey( keyCode, event );
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see android.content.DialogInterface.OnKeyListener#onKey(android.content.DialogInterface,
|
||||
* int, android.view.KeyEvent)
|
||||
*/
|
||||
@Override
|
||||
public boolean onKey( DialogInterface dialog, int keyCode, KeyEvent event )
|
||||
{
|
||||
return onKey( keyCode, event );
|
||||
}
|
||||
|
||||
/**
|
||||
* Manually dispatches a KeyEvent through the provider's listening chain.
|
||||
*
|
||||
* @param keyCode The code for the physical key that was pressed.
|
||||
* @param event The KeyEvent object containing full information about the event.
|
||||
*
|
||||
* @return True if the listener has consumed the event, false otherwise.
|
||||
*/
|
||||
public boolean onKey( int keyCode, KeyEvent event )
|
||||
{
|
||||
// Ignore specified key codes
|
||||
if( mIgnoredCodes != null && mIgnoredCodes.contains( keyCode ) )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Translate input code and analog strength (ranges between 0.0 and 1.0)
|
||||
int inputCode;
|
||||
float strength;
|
||||
if( keyCode <= 0xFF )
|
||||
{
|
||||
// Ordinary key/button changed state
|
||||
inputCode = keyCode;
|
||||
strength = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Analog axis changed state, decode using IME-specific formula
|
||||
switch( mImeFormula )
|
||||
{
|
||||
case DEFAULT:
|
||||
case USB_BT_JOYSTICK_CENTER:
|
||||
case BT_CONTROLLER:
|
||||
default:
|
||||
// Formula defined between paulscode and poke64738
|
||||
inputCode = keyCode / 100;
|
||||
strength = ( (float) keyCode % 100 ) / 64f;
|
||||
break;
|
||||
case EXAMPLE_IME:
|
||||
// Low byte stores input code, high byte stores strength
|
||||
inputCode = keyCode & 0xFF;
|
||||
strength = ( (float) ( keyCode >> 8 ) ) / 0xFF;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Strength is zero when the button/axis is released
|
||||
if( event.getAction() == KeyEvent.ACTION_UP )
|
||||
strength = 0;
|
||||
|
||||
// Notify listeners about new input data
|
||||
notifyListeners( inputCode, strength, getHardwareId( event ) );
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,97 @@
|
|||
/****************************************************************************
|
||||
* *
|
||||
* Project64 - A Nintendo 64 emulator. *
|
||||
* http://www.pj64-emu.com/ *
|
||||
* Copyright (C) 2016 Project64. All rights reserved. *
|
||||
* Copyright (C) 2013 Paul Lamb, littleguy77
|
||||
* *
|
||||
* License: *
|
||||
* GNU/GPLv2 http://www.gnu.org/licenses/gpl-2.0.html *
|
||||
* *
|
||||
****************************************************************************/
|
||||
package emu.project64.input.provider;
|
||||
|
||||
import android.os.Handler;
|
||||
|
||||
import com.bda.controller.Controller;
|
||||
import com.bda.controller.ControllerListener;
|
||||
import com.bda.controller.KeyEvent;
|
||||
import com.bda.controller.MotionEvent;
|
||||
import com.bda.controller.StateEvent;
|
||||
|
||||
/**
|
||||
* A class for transforming MOGA input events into a common format.
|
||||
*/
|
||||
public class MogaProvider extends AbstractProvider implements ControllerListener
|
||||
{
|
||||
private final Controller mController;
|
||||
private final int[] mInputCodes;
|
||||
|
||||
/**
|
||||
* Instantiates a new MOGA provider.
|
||||
*/
|
||||
public MogaProvider( Controller controller )
|
||||
{
|
||||
mController = controller;
|
||||
mController.setListener( this, new Handler() );
|
||||
|
||||
mInputCodes = new int[10];
|
||||
//@formatter:off
|
||||
mInputCodes[0] = axisToInputCode( MotionEvent.AXIS_X, true );
|
||||
mInputCodes[1] = axisToInputCode( MotionEvent.AXIS_X, false );
|
||||
mInputCodes[2] = axisToInputCode( MotionEvent.AXIS_Y, true );
|
||||
mInputCodes[3] = axisToInputCode( MotionEvent.AXIS_Y, false );
|
||||
mInputCodes[4] = axisToInputCode( MotionEvent.AXIS_Z, true );
|
||||
mInputCodes[5] = axisToInputCode( MotionEvent.AXIS_Z, false );
|
||||
mInputCodes[6] = axisToInputCode( MotionEvent.AXIS_RZ, true );
|
||||
mInputCodes[7] = axisToInputCode( MotionEvent.AXIS_RZ, false );
|
||||
mInputCodes[8] = axisToInputCode( MotionEvent.AXIS_LTRIGGER, true );
|
||||
mInputCodes[9] = axisToInputCode( MotionEvent.AXIS_RTRIGGER, true );
|
||||
//@formatter:on
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onKeyEvent( KeyEvent event )
|
||||
{
|
||||
int inputCode = event.getKeyCode();
|
||||
float strength = event.getAction() == KeyEvent.ACTION_DOWN ? 1 : 0;
|
||||
int hardwareId = getHardwareId( event );
|
||||
|
||||
// Notify listeners about new input data
|
||||
notifyListeners( inputCode, strength, hardwareId );
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onMotionEvent( MotionEvent event )
|
||||
{
|
||||
// Read all the requested axes
|
||||
float[] strengths = new float[mInputCodes.length];
|
||||
for( int i = 0; i < mInputCodes.length; i++ )
|
||||
{
|
||||
int inputCode = mInputCodes[i];
|
||||
|
||||
// Compute the axis code from the input code
|
||||
int axisCode = inputToAxisCode( inputCode );
|
||||
|
||||
// Get the analog value using the MOGA API
|
||||
float strength = event.getAxisValue( axisCode );
|
||||
|
||||
// If the strength points in the correct direction, record it
|
||||
boolean direction1 = inputToAxisDirection( inputCode );
|
||||
boolean direction2 = strength > 0;
|
||||
if( direction1 == direction2 )
|
||||
strengths[i] = Math.abs( strength );
|
||||
else
|
||||
strengths[i] = 0;
|
||||
}
|
||||
int hardwareId = getHardwareId( event );
|
||||
|
||||
// Notify listeners about new input data
|
||||
notifyListeners( mInputCodes, strengths, hardwareId );
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStateEvent( StateEvent arg0 )
|
||||
{
|
||||
}
|
||||
}
|
|
@ -26,6 +26,8 @@ public enum UISettingID
|
|||
//Controller Config
|
||||
Controller_ConfigFile,
|
||||
Controller_CurrentProfile,
|
||||
Controller_Deadzone,
|
||||
Controller_Sensitivity,
|
||||
;
|
||||
|
||||
private int value;
|
||||
|
|
|
@ -0,0 +1,510 @@
|
|||
/****************************************************************************
|
||||
* *
|
||||
* Project64 - A Nintendo 64 emulator. *
|
||||
* http://www.pj64-emu.com/ *
|
||||
* Copyright (C) 2012 Project64. All rights reserved. *
|
||||
* *
|
||||
* License: *
|
||||
* GNU/GPLv2 http://www.gnu.org/licenses/gpl-2.0.html *
|
||||
* *
|
||||
****************************************************************************/
|
||||
package emu.project64.profile;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import emu.project64.AndroidDevice;
|
||||
import emu.project64.R;
|
||||
import emu.project64.hack.MogaHack;
|
||||
import emu.project64.input.AbstractController;
|
||||
import emu.project64.input.map.InputMap;
|
||||
import emu.project64.input.provider.AbstractProvider;
|
||||
import emu.project64.input.provider.AbstractProvider.OnInputListener;
|
||||
import emu.project64.input.provider.AxisProvider;
|
||||
import emu.project64.input.provider.KeyProvider;
|
||||
import emu.project64.input.provider.KeyProvider.ImeFormula;
|
||||
import emu.project64.input.provider.MogaProvider;
|
||||
import emu.project64.jni.NativeExports;
|
||||
import emu.project64.jni.UISettingID;
|
||||
import emu.project64.persistent.ConfigFile;
|
||||
import emu.project64.persistent.ConfigFile.ConfigSection;
|
||||
import android.app.AlertDialog;
|
||||
import android.app.AlertDialog.Builder;
|
||||
import android.annotation.TargetApi;
|
||||
import android.content.Context;
|
||||
import android.content.DialogInterface;
|
||||
import android.graphics.PorterDuff;
|
||||
import android.os.Bundle;
|
||||
import android.support.v7.app.ActionBar;
|
||||
import android.support.v7.app.AppCompatActivity;
|
||||
import android.support.v7.widget.Toolbar;
|
||||
import android.text.TextUtils;
|
||||
import android.util.DisplayMetrics;
|
||||
import android.util.Log;
|
||||
import android.view.KeyEvent;
|
||||
import android.view.MenuItem;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.View;
|
||||
import android.view.View.OnClickListener;
|
||||
import android.view.WindowManager;
|
||||
import android.widget.Button;
|
||||
import android.widget.EditText;
|
||||
import android.widget.FrameLayout;
|
||||
import android.widget.TextView;
|
||||
|
||||
import com.bda.controller.Controller;
|
||||
|
||||
public class ControllerProfileActivity extends AppCompatActivity implements OnInputListener,
|
||||
OnClickListener
|
||||
{
|
||||
// Visual settings
|
||||
private static final float UNMAPPED_BUTTON_ALPHA = 0.2f;
|
||||
private static final int UNMAPPED_BUTTON_FILTER = 0x66FFFFFF;
|
||||
private static final int MIN_LAYOUT_WIDTH_DP = 480;
|
||||
|
||||
// Controller profile objects
|
||||
private ConfigFile mConfigFile;
|
||||
private Profile mProfile;
|
||||
|
||||
// Input listening
|
||||
private KeyProvider mKeyProvider;
|
||||
private MogaProvider mMogaProvider;
|
||||
private AxisProvider mAxisProvider;
|
||||
private Controller mMogaController = Controller.getInstance( this );
|
||||
|
||||
// Widgets
|
||||
private final Button[] mN64Buttons = new Button[InputMap.NUM_MAPPABLES];
|
||||
private TextView mFeedbackText;
|
||||
|
||||
@Override
|
||||
public void onCreate( Bundle savedInstanceState )
|
||||
{
|
||||
super.onCreate( savedInstanceState );
|
||||
|
||||
// Initialize MOGA controller API
|
||||
// TODO: Remove hack after MOGA SDK is fixed
|
||||
// mMogaController.init();
|
||||
MogaHack.init( mMogaController, this );
|
||||
|
||||
// Load the profile; fail fast if there are any programmer usage errors
|
||||
String name = NativeExports.UISettingsLoadString(UISettingID.Controller_CurrentProfile.getValue());
|
||||
if( TextUtils.isEmpty( name ) )
|
||||
throw new Error( "Invalid usage: profile name cannot be null or empty" );
|
||||
mConfigFile = new ConfigFile(NativeExports.UISettingsLoadString(UISettingID.Controller_ConfigFile.getValue()));
|
||||
ConfigSection section = mConfigFile.get( name );
|
||||
if( section == null )
|
||||
{
|
||||
//profile not found create it
|
||||
mConfigFile.put(name, "map", "-");
|
||||
section = mConfigFile.get( name );
|
||||
if( section == null )
|
||||
{
|
||||
throw new Error( "Invalid usage: profile name not found in config file" );
|
||||
}
|
||||
}
|
||||
mProfile = new Profile( false, section );
|
||||
|
||||
// Set up input listeners
|
||||
mKeyProvider = new KeyProvider( ImeFormula.DEFAULT, AndroidDevice.getUnmappableKeyCodes() );
|
||||
mKeyProvider.registerListener( this );
|
||||
mMogaProvider = new MogaProvider( mMogaController );
|
||||
mMogaProvider.registerListener( this );
|
||||
if( AndroidDevice.IS_HONEYCOMB_MR1 )
|
||||
{
|
||||
mAxisProvider = new AxisProvider();
|
||||
mAxisProvider.registerListener( this );
|
||||
}
|
||||
|
||||
// Initialize the layout
|
||||
initLayoutDefault();
|
||||
|
||||
// Refresh everything
|
||||
refreshAllButtons();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onResume()
|
||||
{
|
||||
super.onResume();
|
||||
mMogaController.onResume();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPause()
|
||||
{
|
||||
super.onPause();
|
||||
mMogaController.onPause();
|
||||
|
||||
// Lazily persist the profile data; only need to do it on pause
|
||||
mProfile.writeTo( mConfigFile );
|
||||
mConfigFile.save();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroy()
|
||||
{
|
||||
super.onDestroy();
|
||||
mMogaController.exit();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onOptionsItemSelected(MenuItem item)
|
||||
{
|
||||
switch (item.getItemId())
|
||||
{
|
||||
case android.R.id.home:
|
||||
finish();
|
||||
return true;
|
||||
}
|
||||
return super.onOptionsItemSelected(item);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBackPressed()
|
||||
{
|
||||
finish();
|
||||
}
|
||||
|
||||
private void initLayoutDefault()
|
||||
{
|
||||
WindowManager manager = (WindowManager) getSystemService( Context.WINDOW_SERVICE );
|
||||
DisplayMetrics metrics = new DisplayMetrics();
|
||||
manager.getDefaultDisplay().getMetrics( metrics );
|
||||
float scalefactor = (float) DisplayMetrics.DENSITY_DEFAULT / (float) metrics.densityDpi;
|
||||
int widthDp = Math.round( metrics.widthPixels * scalefactor );
|
||||
|
||||
// For narrow screens, use an alternate layout
|
||||
if( widthDp < MIN_LAYOUT_WIDTH_DP )
|
||||
{
|
||||
setContentView( R.layout.controller_profile_activity_port );
|
||||
}
|
||||
else
|
||||
{
|
||||
setContentView( R.layout.controller_profile_activity );
|
||||
}
|
||||
|
||||
// Add the tool bar to the activity (which supports the fancy menu/arrow animation)
|
||||
Toolbar toolbar = (Toolbar) findViewById( R.id.toolbar );
|
||||
toolbar.setTitle( getString(R.string.gamepad_title) );
|
||||
setSupportActionBar( toolbar );
|
||||
ActionBar actionbar = getSupportActionBar();
|
||||
|
||||
if (AndroidDevice.IS_ICE_CREAM_SANDWICH)
|
||||
{
|
||||
actionbar.setHomeButtonEnabled(true);
|
||||
actionbar.setDisplayHomeAsUpEnabled(true);
|
||||
}
|
||||
|
||||
// Initialize and refresh the widgets
|
||||
initWidgets();
|
||||
}
|
||||
|
||||
private void initWidgets()
|
||||
{
|
||||
// Get the text view object
|
||||
mFeedbackText = (TextView) findViewById( R.id.textFeedback );
|
||||
mFeedbackText.setText( "" );
|
||||
|
||||
// Create a button list to simplify highlighting and mapping
|
||||
setupButton( R.id.buttonDR, AbstractController.DPD_R );
|
||||
setupButton( R.id.buttonDL, AbstractController.DPD_L );
|
||||
setupButton( R.id.buttonDD, AbstractController.DPD_D );
|
||||
setupButton( R.id.buttonDU, AbstractController.DPD_U );
|
||||
setupButton( R.id.buttonS, AbstractController.START );
|
||||
setupButton( R.id.buttonZ, AbstractController.BTN_Z );
|
||||
setupButton( R.id.buttonB, AbstractController.BTN_B );
|
||||
setupButton( R.id.buttonA, AbstractController.BTN_A );
|
||||
setupButton( R.id.buttonCR, AbstractController.CPD_R );
|
||||
setupButton( R.id.buttonCL, AbstractController.CPD_L );
|
||||
setupButton( R.id.buttonCD, AbstractController.CPD_D );
|
||||
setupButton( R.id.buttonCU, AbstractController.CPD_U );
|
||||
setupButton( R.id.buttonR, AbstractController.BTN_R );
|
||||
setupButton( R.id.buttonL, AbstractController.BTN_L );
|
||||
setupButton( R.id.buttonAR, InputMap.AXIS_R );
|
||||
setupButton( R.id.buttonAL, InputMap.AXIS_L );
|
||||
setupButton( R.id.buttonAD, InputMap.AXIS_D );
|
||||
setupButton( R.id.buttonAU, InputMap.AXIS_U );
|
||||
}
|
||||
|
||||
private void setupButton( int resId, int index )
|
||||
{
|
||||
mN64Buttons[index] = (Button) findViewById( resId );
|
||||
if( mN64Buttons[index] != null )
|
||||
{
|
||||
mN64Buttons[index].setOnClickListener( this );
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onClick(View view)
|
||||
{
|
||||
// Handle button clicks in the mapping screen
|
||||
for( int i = 0; i < mN64Buttons.length; i++ )
|
||||
{
|
||||
// Find the button that was pressed
|
||||
if( view.equals( mN64Buttons[i] ) )
|
||||
{
|
||||
// Popup a dialog to listen to input codes from user
|
||||
Button button = (Button) view;
|
||||
popupListener( button.getText(), i );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private interface PromptInputCodeListener
|
||||
{
|
||||
public void onDialogClosed( int inputCode, int hardwareId, int which );
|
||||
}
|
||||
|
||||
/**
|
||||
* Open a dialog to prompt the user for an input code.
|
||||
*
|
||||
* @param context The activity context.
|
||||
* @param moga The MOGA controller interface.
|
||||
* @param title The title of the dialog.
|
||||
* @param message The message to be shown inside the dialog.
|
||||
* @param neutralButtonText The text to be shown on the neutral button, or null.
|
||||
* @param ignoredKeyCodes The key codes to ignore.
|
||||
* @param listener The listener to process the input code, when provided.
|
||||
*/
|
||||
public static void promptInputCode( Context context, Controller moga, CharSequence title, CharSequence message,
|
||||
CharSequence neutralButtonText, final PromptInputCodeListener listener )
|
||||
{
|
||||
final ArrayList<AbstractProvider> providers = new ArrayList<AbstractProvider>();
|
||||
|
||||
// Create a widget to dispatch key/motion event data
|
||||
FrameLayout view = new FrameLayout( context );
|
||||
EditText dummyImeListener = new EditText( context );
|
||||
dummyImeListener.setVisibility( View.INVISIBLE );
|
||||
dummyImeListener.setHeight( 0 );
|
||||
view.addView( dummyImeListener );
|
||||
|
||||
// Set the focus parameters of the view so that it will dispatch events
|
||||
view.setFocusable( true );
|
||||
view.setFocusableInTouchMode( true );
|
||||
view.requestFocus();
|
||||
|
||||
// Create the input event providers
|
||||
providers.add( new KeyProvider( view, ImeFormula.DEFAULT, AndroidDevice.getUnmappableKeyCodes() ) );
|
||||
providers.add( new MogaProvider( moga ) );
|
||||
if( AndroidDevice.IS_HONEYCOMB_MR1 )
|
||||
{
|
||||
providers.add( new AxisProvider( view ) );
|
||||
}
|
||||
|
||||
// Notify the client when the user clicks the dialog's positive button
|
||||
DialogInterface.OnClickListener clickListener = new DialogInterface.OnClickListener()
|
||||
{
|
||||
@Override
|
||||
public void onClick( DialogInterface dialog, int which )
|
||||
{
|
||||
for( AbstractProvider provider : providers )
|
||||
provider.unregisterAllListeners();
|
||||
listener.onDialogClosed( 0, 0, which );
|
||||
}
|
||||
};
|
||||
|
||||
final AlertDialog dialog = new Builder( context ).setTitle( title ).setMessage( message ).setCancelable( false )
|
||||
.setNegativeButton( context.getString( android.R.string.cancel ), clickListener )
|
||||
.setPositiveButton( context.getString( android.R.string.ok ), clickListener )
|
||||
.setNeutralButton( neutralButtonText, clickListener ).setPositiveButton( null, null )
|
||||
.setView( view ).create();
|
||||
|
||||
OnInputListener inputListener = new OnInputListener()
|
||||
{
|
||||
@Override
|
||||
public void onInput( int[] inputCodes, float[] strengths, int hardwareId )
|
||||
{
|
||||
if( inputCodes == null || strengths == null )
|
||||
return;
|
||||
|
||||
// Find the strongest input
|
||||
float maxStrength = 0;
|
||||
int strongestInputCode = 0;
|
||||
for( int i = 0; i < inputCodes.length; i++ )
|
||||
{
|
||||
// Identify the strongest input
|
||||
float strength = strengths[i];
|
||||
if( strength > maxStrength )
|
||||
{
|
||||
maxStrength = strength;
|
||||
strongestInputCode = inputCodes[i];
|
||||
}
|
||||
}
|
||||
|
||||
// Call the overloaded method with the strongest found
|
||||
onInput( strongestInputCode, maxStrength, hardwareId );
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onInput( int inputCode, float strength, int hardwareId )
|
||||
{
|
||||
if( inputCode != 0 && strength > AbstractProvider.STRENGTH_THRESHOLD )
|
||||
{
|
||||
for( AbstractProvider provider : providers )
|
||||
provider.unregisterAllListeners();
|
||||
listener.onDialogClosed( inputCode, hardwareId, DialogInterface.BUTTON_POSITIVE );
|
||||
dialog.dismiss();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Connect the upstream event listeners
|
||||
for( AbstractProvider provider : providers )
|
||||
{
|
||||
provider.registerListener( inputListener );
|
||||
}
|
||||
|
||||
// Launch the dialog
|
||||
dialog.show();
|
||||
}
|
||||
|
||||
private void popupListener( CharSequence title, final int index )
|
||||
{
|
||||
final InputMap map = new InputMap( mProfile.get( "map" ) );
|
||||
String message = getString( R.string.inputMapActivity_popupMessage,map.getMappedCodeInfo( index ) );
|
||||
String btnText = getString( R.string.inputMapActivity_popupUnmap );
|
||||
|
||||
PromptInputCodeListener listener = new PromptInputCodeListener()
|
||||
{
|
||||
@Override
|
||||
public void onDialogClosed( int inputCode, int hardwareId, int which )
|
||||
{
|
||||
if( which != DialogInterface.BUTTON_NEGATIVE )
|
||||
{
|
||||
if( which == DialogInterface.BUTTON_POSITIVE )
|
||||
{
|
||||
map.map( inputCode, index );
|
||||
}
|
||||
else
|
||||
{
|
||||
map.unmapCommand( index );
|
||||
}
|
||||
mProfile.put( "map", map.serialize() );
|
||||
refreshAllButtons();
|
||||
}
|
||||
|
||||
// Refresh our MOGA provider since the prompt disconnected it
|
||||
mMogaProvider = new MogaProvider( mMogaController );
|
||||
mMogaProvider.registerListener( ControllerProfileActivity.this );
|
||||
}
|
||||
};
|
||||
promptInputCode( this, mMogaController, title, message, btnText, listener);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onKeyDown( int keyCode, KeyEvent event )
|
||||
{
|
||||
return mKeyProvider.onKey( keyCode, event ) || super.onKeyDown( keyCode, event );
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onKeyUp( int keyCode, KeyEvent event )
|
||||
{
|
||||
return mKeyProvider.onKey( keyCode, event ) || super.onKeyUp( keyCode, event );
|
||||
}
|
||||
|
||||
@TargetApi( 12 )
|
||||
@Override
|
||||
public boolean onGenericMotionEvent( MotionEvent event )
|
||||
{
|
||||
if( !AndroidDevice.IS_HONEYCOMB_MR1 )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return mAxisProvider.onGenericMotion( event ) || super.onGenericMotionEvent( event );
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onInput(int inputCode, float strength, int hardwareId)
|
||||
{
|
||||
refreshButton( inputCode, strength );
|
||||
refreshFeedbackText( inputCode, strength );
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onInput(int[] inputCodes, float[] strengths, int hardwareId)
|
||||
{
|
||||
float maxStrength = AbstractProvider.STRENGTH_THRESHOLD;
|
||||
int strongestInputCode = 0;
|
||||
for( int i = 0; i < inputCodes.length; i++ )
|
||||
{
|
||||
int inputCode = inputCodes[i];
|
||||
float strength = strengths[i];
|
||||
|
||||
// Cache the strongest input
|
||||
if( strength > maxStrength )
|
||||
{
|
||||
maxStrength = strength;
|
||||
strongestInputCode = inputCode;
|
||||
}
|
||||
|
||||
refreshButton( inputCode, strength );
|
||||
}
|
||||
refreshFeedbackText( strongestInputCode, maxStrength );
|
||||
}
|
||||
|
||||
private void refreshFeedbackText( int inputCode, float strength )
|
||||
{
|
||||
// Update the feedback text (not all layouts include this, so check null)
|
||||
if( mFeedbackText != null )
|
||||
{
|
||||
mFeedbackText.setText( strength > AbstractProvider.STRENGTH_THRESHOLD ? AbstractProvider.getInputName( inputCode ) : "" );
|
||||
}
|
||||
}
|
||||
|
||||
private void refreshButton( int inputCode, float strength )
|
||||
{
|
||||
InputMap map = new InputMap( mProfile.get( "map" ) );
|
||||
int command = map.get( inputCode );
|
||||
if( command != InputMap.UNMAPPED )
|
||||
{
|
||||
Button button = mN64Buttons[command];
|
||||
refreshButton( button, strength, true );
|
||||
}
|
||||
}
|
||||
|
||||
@TargetApi( 11 )
|
||||
private void refreshButton( Button button, float strength, boolean isMapped )
|
||||
{
|
||||
if( button != null )
|
||||
{
|
||||
button.setPressed( strength > AbstractProvider.STRENGTH_THRESHOLD );
|
||||
|
||||
// Fade any buttons that aren't mapped
|
||||
if( AndroidDevice.IS_HONEYCOMB )
|
||||
{
|
||||
if( isMapped )
|
||||
{
|
||||
button.setAlpha( 1 );
|
||||
}
|
||||
else
|
||||
{
|
||||
button.setAlpha( UNMAPPED_BUTTON_ALPHA );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// For older APIs try something similar (not quite the same)
|
||||
if( isMapped )
|
||||
{
|
||||
button.getBackground().clearColorFilter();
|
||||
}
|
||||
else
|
||||
{
|
||||
button.getBackground().setColorFilter( UNMAPPED_BUTTON_FILTER, PorterDuff.Mode.MULTIPLY );
|
||||
}
|
||||
button.invalidate();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void refreshAllButtons()
|
||||
{
|
||||
final InputMap map = new InputMap( mProfile.get( "map" ) );
|
||||
for( int i = 0; i < mN64Buttons.length; i++ )
|
||||
{
|
||||
refreshButton( mN64Buttons[i], 0, map.isMapped( i ) );
|
||||
}
|
||||
}
|
||||
}
|
|
@ -26,6 +26,7 @@ package emu.project64.settings;
|
|||
import emu.project64.R;
|
||||
import emu.project64.SplashActivity;
|
||||
import emu.project64.jni.NativeExports;
|
||||
import emu.project64.profile.ControllerProfileActivity;
|
||||
import android.content.DialogInterface;
|
||||
import android.content.Intent;
|
||||
import android.os.Bundle;
|
||||
|
@ -38,6 +39,8 @@ import android.support.v7.preference.PreferenceFragmentCompat;
|
|||
|
||||
public abstract class BaseSettingsFragment extends PreferenceFragmentCompat
|
||||
{
|
||||
private static final String DIALOG_FRAGMENT_TAG = "android.support.v7.preference.PreferenceFragment.DIALOG";
|
||||
|
||||
protected abstract int getXml();
|
||||
protected abstract int getTitleId();
|
||||
|
||||
|
@ -68,19 +71,45 @@ public abstract class BaseSettingsFragment extends PreferenceFragmentCompat
|
|||
@Override
|
||||
public void onDisplayPreferenceDialog(Preference preference)
|
||||
{
|
||||
/*if (AndroidUtil.isHoneycombOrLater() && preference instanceof MultiSelectListPreference) {
|
||||
DialogFragment dialogFragment = MultiSelectListPreferenceDialogFragmentCompat.newInstance(preference.getKey());
|
||||
DialogFragment dialogFragment = null;
|
||||
if (preference instanceof SeekBarPreference)
|
||||
{
|
||||
dialogFragment = SeekBarPreferencePreferenceDialogFragmentCompat.newInstance(preference.getKey());
|
||||
}
|
||||
else if (preference instanceof TwoLinesListPreference)
|
||||
{
|
||||
dialogFragment = TwoLinesListPreferenceDialogFragmentCompat.newInstance(preference.getKey());
|
||||
}
|
||||
else
|
||||
{
|
||||
super.onDisplayPreferenceDialog(preference);
|
||||
}
|
||||
|
||||
if (dialogFragment != null)
|
||||
{
|
||||
dialogFragment.setTargetFragment(this, 0);
|
||||
dialogFragment.show(getFragmentManager(), DIALOG_FRAGMENT_TAG);
|
||||
return;
|
||||
}*/
|
||||
super.onDisplayPreferenceDialog(preference);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onPreferenceTreeClick(Preference preference)
|
||||
{
|
||||
if (preference.getKey().equals("settings_video"))
|
||||
if (preference.getKey().equals("settings_input"))
|
||||
{
|
||||
loadFragment(new InputFragment());
|
||||
}
|
||||
else if (preference.getKey().equals("settings_touch_screen"))
|
||||
{
|
||||
loadFragment(new TouchScreenFragment());
|
||||
}
|
||||
else if (preference.getKey().equals("settings_gamepad_screen"))
|
||||
{
|
||||
final AppCompatActivity activity = (AppCompatActivity)getActivity();
|
||||
Intent intent = new Intent( activity, ControllerProfileActivity.class );
|
||||
activity.startActivity( intent );
|
||||
}
|
||||
else if (preference.getKey().equals("settings_video"))
|
||||
{
|
||||
loadFragment(new VideoFragment());
|
||||
}
|
||||
|
|
|
@ -11,14 +11,6 @@
|
|||
package emu.project64.settings;
|
||||
|
||||
import emu.project64.R;
|
||||
import emu.project64.jni.NativeExports;
|
||||
import emu.project64.jni.SettingsID;
|
||||
import android.content.Intent;
|
||||
import android.content.SharedPreferences;
|
||||
import android.os.Bundle;
|
||||
import android.support.v7.preference.ListPreference;
|
||||
import android.support.v7.preference.Preference;
|
||||
import android.support.v7.preference.PreferenceManager;
|
||||
|
||||
public class GameSettingsFragment extends BaseSettingsFragment
|
||||
{
|
||||
|
|
|
@ -0,0 +1,57 @@
|
|||
/****************************************************************************
|
||||
* *
|
||||
* Project64 - A Nintendo 64 emulator. *
|
||||
* http://www.pj64-emu.com/ *
|
||||
* Copyright (C) 2012 Project64. All rights reserved. *
|
||||
* *
|
||||
* License: *
|
||||
* GNU/GPLv2 http://www.gnu.org/licenses/gpl-2.0.html *
|
||||
* *
|
||||
****************************************************************************/
|
||||
package emu.project64.settings;
|
||||
|
||||
import android.os.Bundle;
|
||||
import emu.project64.R;
|
||||
|
||||
public class GamepadScreenFragment extends BaseSettingsFragment
|
||||
{
|
||||
@Override
|
||||
protected int getXml()
|
||||
{
|
||||
return R.xml.setting_gamepad;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int getTitleId()
|
||||
{
|
||||
return R.string.gamepad_title;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreatePreferences(Bundle bundle, String s)
|
||||
{
|
||||
super.onCreatePreferences(bundle, s);
|
||||
|
||||
/*String profilesDir = AndroidDevice.PACKAGE_DIRECTORY + "/profiles";
|
||||
String touchscreenProfiles_cfg = profilesDir + "/touchscreen.cfg";
|
||||
ConfigFile touchscreenProfiles = new ConfigFile( touchscreenProfiles_cfg );
|
||||
Set<String> layoutsKeySet = touchscreenProfiles.keySet();
|
||||
String[] layouts = layoutsKeySet.toArray(new String[layoutsKeySet.size()]);
|
||||
|
||||
CharSequence[] entries = new CharSequence[layouts.length];
|
||||
String[] entryValues = new String[layouts.length];
|
||||
String[] entrySubtitles = new String[layouts.length];
|
||||
|
||||
for( int i = 0; i < layouts.length; i++ )
|
||||
{
|
||||
entries[i] = layouts[i];
|
||||
entryValues[i] = layouts[i];
|
||||
entrySubtitles[i] = touchscreenProfiles.get(layouts[i]).get("comment");
|
||||
}
|
||||
|
||||
final TwoLinesListPreference listPreference = (TwoLinesListPreference) findPreference("touchscreenLayout");
|
||||
listPreference.setEntries(entries);
|
||||
listPreference.setEntryValues(entryValues);
|
||||
listPreference.setEntriesSubtitles(entrySubtitles);*/
|
||||
}
|
||||
}
|
|
@ -0,0 +1,28 @@
|
|||
/****************************************************************************
|
||||
* *
|
||||
* Project64 - A Nintendo 64 emulator. *
|
||||
* http://www.pj64-emu.com/ *
|
||||
* Copyright (C) 2012 Project64. All rights reserved. *
|
||||
* *
|
||||
* License: *
|
||||
* GNU/GPLv2 http://www.gnu.org/licenses/gpl-2.0.html *
|
||||
* *
|
||||
****************************************************************************/
|
||||
package emu.project64.settings;
|
||||
|
||||
import emu.project64.R;
|
||||
|
||||
public class InputFragment extends SettingsFragment
|
||||
{
|
||||
@Override
|
||||
protected int getXml()
|
||||
{
|
||||
return R.xml.setting_input;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int getTitleId()
|
||||
{
|
||||
return R.string.input_screen_title;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,222 @@
|
|||
/****************************************************************************
|
||||
* *
|
||||
* Project64 - A Nintendo 64 emulator. *
|
||||
* http://www.pj64-emu.com/ *
|
||||
* Copyright (C) 2012 Project64. All rights reserved. *
|
||||
* *
|
||||
* License: *
|
||||
* GNU/GPLv2 http://www.gnu.org/licenses/gpl-2.0.html *
|
||||
* *
|
||||
****************************************************************************/
|
||||
package emu.project64.settings;
|
||||
|
||||
import emu.project64.R;
|
||||
import android.content.Context;
|
||||
import android.content.res.TypedArray;
|
||||
import android.support.v7.preference.DialogPreference;
|
||||
import android.util.AttributeSet;
|
||||
import android.widget.SeekBar;
|
||||
|
||||
/**
|
||||
* A type of {@link DialogPreference} that uses a {@link SeekBar} as a means of selecting a desired option.
|
||||
*/
|
||||
public class SeekBarPreference extends DialogPreference
|
||||
{
|
||||
private static final int DEFAULT_VALUE = 50;
|
||||
private static final int DEFAULT_MIN = 0;
|
||||
private static final int DEFAULT_MAX = 100;
|
||||
private static final int DEFAULT_STEP = 10;
|
||||
private static final String DEFAULT_UNITS = "%";
|
||||
|
||||
private int mValue = DEFAULT_VALUE;
|
||||
private int mMinValue = DEFAULT_MIN;
|
||||
private int mMaxValue = DEFAULT_MAX;
|
||||
private int mStepSize = DEFAULT_STEP;
|
||||
private String mUnits = DEFAULT_UNITS;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param context The {@link Context} this SeekBarPreference is being used in.
|
||||
* @param attrs A collection of attributes, as found associated with a tag in an XML document.
|
||||
*/
|
||||
public SeekBarPreference( Context context, AttributeSet attrs )
|
||||
{
|
||||
super( context, attrs );
|
||||
|
||||
// Get the attributes from the XML file, if provided
|
||||
TypedArray a = context.obtainStyledAttributes( attrs, R.styleable.SeekBarPreference );
|
||||
setMinValue( a.getInteger( R.styleable.SeekBarPreference_minimumValue, DEFAULT_MIN ) );
|
||||
setMaxValue( a.getInteger( R.styleable.SeekBarPreference_maximumValue, DEFAULT_MAX ) );
|
||||
setStepSize( a.getInteger( R.styleable.SeekBarPreference_stepSize, DEFAULT_STEP ) );
|
||||
setUnits( a.getString( R.styleable.SeekBarPreference_units ) );
|
||||
if (getUnits() == null)
|
||||
{
|
||||
setUnits(DEFAULT_UNITS);
|
||||
}
|
||||
a.recycle();
|
||||
|
||||
// Setup the layout
|
||||
setDialogLayoutResource( R.layout.seek_bar_preference );
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param context The {@link Context} this SeekBarPreference will be used in.
|
||||
*/
|
||||
public SeekBarPreference( Context context )
|
||||
{
|
||||
this( context, null );
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets this SeekBarPreference to a specified value.
|
||||
*
|
||||
* @param value The value to set the SeekBarPreference to.
|
||||
*/
|
||||
public void setValue( int value )
|
||||
{
|
||||
mValue = validate( value );
|
||||
if( shouldPersist() )
|
||||
persistInt( mValue );
|
||||
setSummary( getValueString( mValue ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the minimum value this SeekBarPreference may have.
|
||||
*
|
||||
* @param minValue The minimum value for this SeekBarPreference.
|
||||
*/
|
||||
public void setMinValue( int minValue )
|
||||
{
|
||||
mMinValue = minValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the maximum value this SeekBarPreference may have.
|
||||
*
|
||||
* @param maxValue The maximum value for this SeekBarPreference.
|
||||
*/
|
||||
public void setMaxValue( int maxValue )
|
||||
{
|
||||
mMaxValue = maxValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the size of each increment in this SeekBarPreference.
|
||||
*
|
||||
* @param stepSize The size of each increment.
|
||||
*/
|
||||
public void setStepSize( int stepSize )
|
||||
{
|
||||
mStepSize = stepSize;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the type of units this SeekBarPreference uses (e.g. "%").
|
||||
*
|
||||
* @param units The unit type for this SeekBarPreference to use.
|
||||
*/
|
||||
public void setUnits( String units )
|
||||
{
|
||||
mUnits = units;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the currently set value.
|
||||
*
|
||||
* @return The currently set value in this SeekBarPreference.
|
||||
*/
|
||||
public int getValue()
|
||||
{
|
||||
return mValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the currently set minimum value.
|
||||
*
|
||||
* @return The currently set minimum value for this SeekBarPreference.
|
||||
*/
|
||||
public int getMinValue()
|
||||
{
|
||||
return mMinValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the currently set maximum value.
|
||||
*
|
||||
* @return The currently set maximum value for this SeekBarPreference.
|
||||
*/
|
||||
public int getMaxValue()
|
||||
{
|
||||
return mMaxValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the currently set increment step size.
|
||||
*
|
||||
* @return The currently set increment step size for this SeekBarPreference.
|
||||
*/
|
||||
public int getStepSize()
|
||||
{
|
||||
return mStepSize;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the currently set units.
|
||||
*
|
||||
* @return The currently set unit type this SeekBarPreference uses.
|
||||
*/
|
||||
public String getUnits()
|
||||
{
|
||||
return mUnits;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the value as a string with units appended.
|
||||
*
|
||||
* @param value The value to use in the string.
|
||||
*
|
||||
* @return The value as a String.
|
||||
*/
|
||||
public String getValueString( int value )
|
||||
{
|
||||
return getContext().getString( R.string.seekBarPreference_summary, value, mUnits );
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Object onGetDefaultValue( TypedArray a, int index )
|
||||
{
|
||||
return a.getInteger( index, DEFAULT_VALUE );
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onSetInitialValue( boolean restorePersistedValue, Object defaultValue )
|
||||
{
|
||||
setValue( restorePersistedValue ? getPersistedInt( mValue ) : (Integer) defaultValue );
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAttached()
|
||||
{
|
||||
setSummary( getValueString( mValue ) );
|
||||
super.onAttached();
|
||||
}
|
||||
|
||||
public int validate( int value )
|
||||
{
|
||||
// Round to nearest integer multiple of mStepSize
|
||||
int newValue = Math.round( value / (float)getStepSize() ) * getStepSize();
|
||||
|
||||
// Address issues when mStepSize is not an integral factor of mMaxValue
|
||||
// e.g. mMaxValue = 100, mMinValue = 0, mStepSize = 9, progress = 100 --> newValue = 99 (should be 100)
|
||||
// e.g. mMaxValue = 100, mMinValue = 0, mStepSize = 6, progress = 99 --> newValue = 102 (should be 100)
|
||||
if( value == getMinValue() || newValue < getMinValue() )
|
||||
newValue = getMinValue();
|
||||
if( value == getMaxValue() || newValue > getMaxValue() )
|
||||
newValue = getMaxValue();
|
||||
|
||||
return newValue;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,102 @@
|
|||
/****************************************************************************
|
||||
* *
|
||||
* Project64 - A Nintendo 64 emulator. *
|
||||
* http://www.pj64-emu.com/ *
|
||||
* Copyright (C) 2012 Project64. All rights reserved. *
|
||||
* *
|
||||
* License: *
|
||||
* GNU/GPLv2 http://www.gnu.org/licenses/gpl-2.0.html *
|
||||
* *
|
||||
****************************************************************************/
|
||||
package emu.project64.settings;
|
||||
|
||||
import emu.project64.R;
|
||||
import android.os.Bundle;
|
||||
import android.support.v7.app.AlertDialog;
|
||||
import android.support.v7.preference.DialogPreference;
|
||||
import android.support.v7.preference.Preference;
|
||||
import android.support.v7.preference.PreferenceDialogFragmentCompat;
|
||||
import android.view.View;
|
||||
import android.widget.SeekBar;
|
||||
import android.widget.TextView;
|
||||
import android.widget.SeekBar.OnSeekBarChangeListener;
|
||||
|
||||
public class SeekBarPreferencePreferenceDialogFragmentCompat extends PreferenceDialogFragmentCompat
|
||||
implements DialogPreference.TargetFragment, OnSeekBarChangeListener
|
||||
{
|
||||
private TextView mTextView;
|
||||
private SeekBar mSeekBar;
|
||||
|
||||
public void onDialogClosed(boolean positiveResult)
|
||||
{
|
||||
if( positiveResult )
|
||||
{
|
||||
final SeekBarPreference preference = getSeekBarPreference();
|
||||
int value = mSeekBar.getProgress() + preference.getMinValue();
|
||||
preference.setValue( value );
|
||||
}
|
||||
}
|
||||
|
||||
public static SeekBarPreferencePreferenceDialogFragmentCompat newInstance(String key)
|
||||
{
|
||||
SeekBarPreferencePreferenceDialogFragmentCompat fragment = new SeekBarPreferencePreferenceDialogFragmentCompat();
|
||||
Bundle b = new Bundle(1);
|
||||
b.putString("key", key);
|
||||
fragment.setArguments(b);
|
||||
return fragment;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Preference findPreference(CharSequence charSequence)
|
||||
{
|
||||
return getPreference();
|
||||
}
|
||||
|
||||
private SeekBarPreference getSeekBarPreference()
|
||||
{
|
||||
return (SeekBarPreference)this.getPreference();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onBindDialogView(View view)
|
||||
{
|
||||
super.onBindDialogView(view);
|
||||
|
||||
// Grab the widget references
|
||||
mTextView = (TextView) view.findViewById( R.id.textFeedback );
|
||||
mSeekBar = (SeekBar) view.findViewById( R.id.seekbar );
|
||||
}
|
||||
|
||||
protected void onPrepareDialogBuilder(AlertDialog.Builder builder)
|
||||
{
|
||||
super.onPrepareDialogBuilder(builder);
|
||||
final SeekBarPreference preference = getSeekBarPreference();
|
||||
|
||||
// Initialize and refresh the widgets
|
||||
mSeekBar.setMax( preference.getMaxValue() - preference.getMinValue());
|
||||
mSeekBar.setOnSeekBarChangeListener( this );
|
||||
mSeekBar.setProgress( preference.getValue() - preference.getMinValue() );
|
||||
mTextView.setText( preference.getValueString( preference.getValue() ) );
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStartTrackingTouch( SeekBar seekBar )
|
||||
{
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStopTrackingTouch( SeekBar seekBar )
|
||||
{
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onProgressChanged( SeekBar seekBar, int progress, boolean fromUser )
|
||||
{
|
||||
final SeekBarPreference preference = getSeekBarPreference();
|
||||
|
||||
int value = preference.validate( progress + preference.getMinValue() );
|
||||
if( value != ( progress + preference.getMinValue() ) )
|
||||
seekBar.setProgress( value - preference.getMinValue() );
|
||||
mTextView.setText( preference.getValueString( value ) );
|
||||
}
|
||||
}
|
|
@ -0,0 +1,61 @@
|
|||
/****************************************************************************
|
||||
* *
|
||||
* Project64 - A Nintendo 64 emulator. *
|
||||
* http://www.pj64-emu.com/ *
|
||||
* Copyright (C) 2012 Project64. All rights reserved. *
|
||||
* *
|
||||
* License: *
|
||||
* GNU/GPLv2 http://www.gnu.org/licenses/gpl-2.0.html *
|
||||
* *
|
||||
****************************************************************************/
|
||||
package emu.project64.settings;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
import android.os.Bundle;
|
||||
import emu.project64.AndroidDevice;
|
||||
import emu.project64.R;
|
||||
import emu.project64.persistent.ConfigFile;
|
||||
|
||||
public class TouchScreenFragment extends BaseSettingsFragment
|
||||
{
|
||||
@Override
|
||||
protected int getXml()
|
||||
{
|
||||
return R.xml.setting_touch_screen;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int getTitleId()
|
||||
{
|
||||
return R.string.touch_screen_title;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreatePreferences(Bundle bundle, String s)
|
||||
{
|
||||
super.onCreatePreferences(bundle, s);
|
||||
|
||||
String profilesDir = AndroidDevice.PACKAGE_DIRECTORY + "/profiles";
|
||||
String touchscreenProfiles_cfg = profilesDir + "/touchscreen.cfg";
|
||||
ConfigFile touchscreenProfiles = new ConfigFile( touchscreenProfiles_cfg );
|
||||
Set<String> layoutsKeySet = touchscreenProfiles.keySet();
|
||||
String[] layouts = layoutsKeySet.toArray(new String[layoutsKeySet.size()]);
|
||||
|
||||
CharSequence[] entries = new CharSequence[layouts.length];
|
||||
String[] entryValues = new String[layouts.length];
|
||||
String[] entrySubtitles = new String[layouts.length];
|
||||
|
||||
for( int i = 0; i < layouts.length; i++ )
|
||||
{
|
||||
entries[i] = layouts[i];
|
||||
entryValues[i] = layouts[i];
|
||||
entrySubtitles[i] = touchscreenProfiles.get(layouts[i]).get("comment");
|
||||
}
|
||||
|
||||
final TwoLinesListPreference listPreference = (TwoLinesListPreference) findPreference("touchscreenLayout");
|
||||
listPreference.setEntries(entries);
|
||||
listPreference.setEntryValues(entryValues);
|
||||
listPreference.setEntriesSubtitles(entrySubtitles);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,119 @@
|
|||
/****************************************************************************
|
||||
* *
|
||||
* Project64 - A Nintendo 64 emulator. *
|
||||
* http://www.pj64-emu.com/ *
|
||||
* Copyright (C) 2012 Project64. All rights reserved. *
|
||||
* *
|
||||
* License: *
|
||||
* GNU/GPLv2 http://www.gnu.org/licenses/gpl-2.0.html *
|
||||
* *
|
||||
****************************************************************************/
|
||||
package emu.project64.settings;
|
||||
|
||||
import emu.project64.R;
|
||||
import android.content.Context;
|
||||
import android.content.res.TypedArray;
|
||||
import android.support.v7.preference.ListPreference;
|
||||
import android.util.AttributeSet;
|
||||
|
||||
public class TwoLinesListPreference extends ListPreference
|
||||
{
|
||||
private CharSequence[] mEntriesSubtitles;
|
||||
private int mValueIndex;
|
||||
|
||||
public TwoLinesListPreference(Context context, AttributeSet attrs)
|
||||
{
|
||||
super(context, attrs);
|
||||
TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.TwoLinesListPreference);
|
||||
mEntriesSubtitles = a.getTextArray(R.styleable.TwoLinesListPreference_entriesSubtitles);
|
||||
a.recycle();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onSetInitialValue(boolean restoreValue, Object defaultValue)
|
||||
{
|
||||
super.onSetInitialValue(restoreValue, defaultValue);
|
||||
|
||||
mEntriesSubtitles = getEntriesSubtitles();
|
||||
mValueIndex = getValueIndex();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setValue(String value)
|
||||
{
|
||||
super.setValue(value);
|
||||
mValueIndex = getValueIndex();
|
||||
updateSummary();
|
||||
}
|
||||
/**
|
||||
* Returns the index of the given value (in the entry values array).
|
||||
*
|
||||
* @param value The value whose index should be returned.
|
||||
* @return The index of the value, or -1 if not found.
|
||||
*/
|
||||
public int findIndexOfValue(String value)
|
||||
{
|
||||
CharSequence[] EntryValues = getEntryValues();
|
||||
if (value != null && EntryValues != null)
|
||||
{
|
||||
for (int i = EntryValues.length - 1; i >= 0; i--)
|
||||
{
|
||||
if (EntryValues[i].equals(value))
|
||||
{
|
||||
return i;
|
||||
}
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
public int getValueIndex()
|
||||
{
|
||||
return findIndexOfValue(getValue());
|
||||
}
|
||||
|
||||
public CharSequence[] getEntriesSubtitles()
|
||||
{
|
||||
return mEntriesSubtitles;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setEntries(CharSequence[] Entries)
|
||||
{
|
||||
super.setEntries(Entries);
|
||||
updateSummary();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setEntryValues(CharSequence[] EntryValues)
|
||||
{
|
||||
super.setEntryValues(EntryValues);
|
||||
mValueIndex = getValueIndex();
|
||||
updateSummary();
|
||||
}
|
||||
|
||||
public void setEntriesSubtitles(CharSequence[] mEntriesSubtitles)
|
||||
{
|
||||
this.mEntriesSubtitles = mEntriesSubtitles;
|
||||
updateSummary();
|
||||
}
|
||||
|
||||
private void updateSummary()
|
||||
{
|
||||
if (mValueIndex < 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
CharSequence[] Entries = getEntries();
|
||||
String summary = Entries[mValueIndex].toString();
|
||||
if (mEntriesSubtitles != null && mEntriesSubtitles.length > mValueIndex)
|
||||
{
|
||||
String subtitle = mEntriesSubtitles[mValueIndex].toString();
|
||||
if (summary.length() > 0 && subtitle.length() > 0)
|
||||
{
|
||||
summary += " - " + subtitle;
|
||||
}
|
||||
}
|
||||
setSummary( summary );
|
||||
}
|
||||
}
|
|
@ -0,0 +1,133 @@
|
|||
/****************************************************************************
|
||||
* *
|
||||
* Project64 - A Nintendo 64 emulator. *
|
||||
* http://www.pj64-emu.com/ *
|
||||
* Copyright (C) 2012 Project64. All rights reserved. *
|
||||
* *
|
||||
* License: *
|
||||
* GNU/GPLv2 http://www.gnu.org/licenses/gpl-2.0.html *
|
||||
* *
|
||||
****************************************************************************/
|
||||
package emu.project64.settings;
|
||||
|
||||
import emu.project64.R;
|
||||
import android.content.Context;
|
||||
import android.content.DialogInterface;
|
||||
import android.os.Bundle;
|
||||
import android.support.v7.app.AlertDialog;
|
||||
import android.support.v7.preference.PreferenceDialogFragmentCompat;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.View.OnClickListener;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.ArrayAdapter;
|
||||
import android.widget.ListAdapter;
|
||||
import android.widget.RadioButton;
|
||||
import android.widget.TextView;
|
||||
|
||||
public class TwoLinesListPreferenceDialogFragmentCompat extends PreferenceDialogFragmentCompat
|
||||
{
|
||||
private TwoLinesListPreference getTwoLinesListPreference()
|
||||
{
|
||||
return (TwoLinesListPreference)this.getPreference();
|
||||
}
|
||||
|
||||
class YourAdapter extends ArrayAdapter<String>
|
||||
{
|
||||
public YourAdapter(Context context, String[] values)
|
||||
{
|
||||
super(context, R.layout.two_lines_list_preference_row, values);
|
||||
}
|
||||
|
||||
class ViewHolder
|
||||
{
|
||||
TextView title;
|
||||
TextView subTitle;
|
||||
RadioButton radioBtn;
|
||||
}
|
||||
|
||||
ViewHolder holder;
|
||||
ViewHolderClickListener listener = new ViewHolderClickListener();
|
||||
|
||||
@Override
|
||||
public View getView(int position, View convertView, ViewGroup parent)
|
||||
{
|
||||
final LayoutInflater inflater = (LayoutInflater) getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
|
||||
if (convertView == null)
|
||||
{
|
||||
convertView = inflater.inflate(R.layout.two_lines_list_preference_row, null);
|
||||
holder = new ViewHolder();
|
||||
holder.title = (TextView) convertView.findViewById(R.id.two_lines_list_view_row_text);
|
||||
holder.subTitle = (TextView) convertView.findViewById(R.id.two_lines_list_view_row_subtext);
|
||||
holder.radioBtn = (RadioButton) convertView.findViewById(R.id.two_lines_list_view_row_radiobtn);
|
||||
convertView.setTag(holder);
|
||||
|
||||
holder.title.setOnClickListener(listener);
|
||||
holder.subTitle.setOnClickListener(listener);
|
||||
holder.radioBtn.setOnClickListener(listener);
|
||||
}
|
||||
else
|
||||
{
|
||||
holder = (ViewHolder) convertView.getTag();
|
||||
}
|
||||
final TwoLinesListPreference preference = getTwoLinesListPreference();
|
||||
|
||||
holder.title.setText(preference.getEntries()[position]);
|
||||
holder.title.setTag(position);
|
||||
holder.subTitle.setText(preference.getEntriesSubtitles()[position]);
|
||||
holder.subTitle.setTag(position);
|
||||
|
||||
holder.radioBtn.setChecked(preference.getValueIndex() == position);
|
||||
holder.radioBtn.setTag(position);
|
||||
|
||||
return convertView;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPrepareDialogBuilder(AlertDialog.Builder builder)
|
||||
{
|
||||
super.onPrepareDialogBuilder(builder);
|
||||
final TwoLinesListPreference preference = getTwoLinesListPreference();
|
||||
CharSequence[] entries = preference.getEntries();
|
||||
String[] values = new String[ entries.length ];
|
||||
for (int i = 0; i < entries.length; i ++)
|
||||
{
|
||||
values[i] = entries[i].toString();
|
||||
}
|
||||
|
||||
ListAdapter adapter = new YourAdapter(builder.getContext(), values);
|
||||
builder.setAdapter(adapter, new DialogInterface.OnClickListener()
|
||||
{
|
||||
@Override
|
||||
public void onClick(DialogInterface dialog, int which)
|
||||
{
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public static TwoLinesListPreferenceDialogFragmentCompat newInstance(String key)
|
||||
{
|
||||
TwoLinesListPreferenceDialogFragmentCompat fragment = new TwoLinesListPreferenceDialogFragmentCompat();
|
||||
Bundle b = new Bundle(1);
|
||||
b.putString("key", key);
|
||||
fragment.setArguments(b);
|
||||
return fragment;
|
||||
}
|
||||
|
||||
public void onDialogClosed(boolean positiveResult)
|
||||
{
|
||||
}
|
||||
|
||||
private class ViewHolderClickListener implements OnClickListener
|
||||
{
|
||||
@Override
|
||||
public void onClick(View v)
|
||||
{
|
||||
final TwoLinesListPreference preference = getTwoLinesListPreference();
|
||||
int EntryIndex = (Integer) v.getTag();
|
||||
preference.setValue(preference.getEntries()[EntryIndex].toString());
|
||||
TwoLinesListPreferenceDialogFragmentCompat.this.getDialog().dismiss();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -24,6 +24,8 @@ void RegisterUISettings(void)
|
|||
g_Settings->AddHandler((SettingID)(FirstUISettings + TouchScreen_Layout), new CSettingTypeApplication("Touch Screen", "Layout", "Analog"));
|
||||
g_Settings->AddHandler((SettingID)(FirstUISettings + Controller_ConfigFile), new CSettingTypeRelativePath("Config", "Controller.cfg"));
|
||||
g_Settings->AddHandler((SettingID)(FirstUISettings + Controller_CurrentProfile), new CSettingTypeApplication("Controller", "Profile", "User"));
|
||||
g_Settings->AddHandler((SettingID)(FirstUISettings + Controller_Deadzone), new CSettingTypeApplication("Controller", "Deadzone", (uint32_t)0));
|
||||
g_Settings->AddHandler((SettingID)(FirstUISettings + Controller_Sensitivity), new CSettingTypeApplication("Controller", "Sensitivity", (uint32_t)100));
|
||||
}
|
||||
|
||||
void UISettingsSaveBool(UISettingID Type, bool Value)
|
||||
|
|
|
@ -28,6 +28,8 @@ enum UISettingID
|
|||
//Controller Config
|
||||
Controller_ConfigFile,
|
||||
Controller_CurrentProfile,
|
||||
Controller_Deadzone,
|
||||
Controller_Sensitivity,
|
||||
};
|
||||
|
||||
void RegisterUISettings(void);
|
||||
|
|