Merge pull request #378 from reicast/native
Integrate an inline messaging system using Android GCM
|
@ -70,22 +70,6 @@
|
|||
-pre-clean
|
||||
-->
|
||||
<import file="custom_rules.xml" optional="true" />
|
||||
<property name="ndkbuildopt" value=""/>
|
||||
<target name="-pre-build">
|
||||
<exec executable="${ndk.dir}/ndk-build" failonerror="true">
|
||||
<arg line="-j4 ${ndkbuildopt}"/>
|
||||
</exec>
|
||||
<copy todir="${jar.libs.dir}/armeabi-v7a">
|
||||
<fileset
|
||||
dir="xperia/libs/armeabi-v7a"
|
||||
includes="libsexplay.so" />
|
||||
</copy>
|
||||
</target>
|
||||
<target name="clean" depends="android_rules.clean">
|
||||
<exec executable="${ndk.dir}/ndk-build" failonerror="true">
|
||||
<arg value="clean"/>
|
||||
</exec>
|
||||
</target>
|
||||
|
||||
<!-- Import the actual build file.
|
||||
|
||||
|
|
|
@ -0,0 +1,30 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project name="reicast-touchpad" default="help">
|
||||
<property name="ndkbuildopt" value=""/>
|
||||
<target name="-pre-build">
|
||||
<exec executable="${ndk.dir}/ndk-build.cmd" failonerror="true" osfamily="windows">
|
||||
<arg line="-j8 ${ndkbuildopt}"/>
|
||||
</exec>
|
||||
<exec executable="${ndk.dir}/ndk-build" failonerror="true" osfamily="unix">
|
||||
<arg line="-j8 ${ndkbuildopt}"/>
|
||||
</exec>
|
||||
<copy todir="${jar.libs.dir}/armeabi-v7a">
|
||||
<fileset
|
||||
dir="xperia/libs/armeabi-v7a"
|
||||
includes="libsexplay.so" />
|
||||
</copy>
|
||||
<copy todir="${jar.libs.dir}/x86">
|
||||
<fileset
|
||||
dir="xperia/libs/x86"
|
||||
includes="libsexplay.so" />
|
||||
</copy>
|
||||
</target>
|
||||
<target name="clean" depends="android_rules.clean">
|
||||
<exec executable="${ndk.dir}/ndk-build.cmd" failonerror="true" osfamily="windows">
|
||||
<arg value="clean"/>
|
||||
</exec>
|
||||
<exec executable="${ndk.dir}/ndk-build" failonerror="true" osfamily="unix">
|
||||
<arg value="clean"/>
|
||||
</exec>
|
||||
</target>
|
||||
</project>
|
|
@ -3,8 +3,8 @@
|
|||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="vertical"
|
||||
android:background="@drawable/background" >
|
||||
android:background="@drawable/background"
|
||||
android:orientation="vertical" >
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
|
@ -16,12 +16,12 @@
|
|||
android:id="@+id/browser_menu"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="10dp"
|
||||
android:gravity="center_vertical|left"
|
||||
android:layout_marginLeft="4dp"
|
||||
android:orientation="horizontal"
|
||||
android:layout_marginTop="10dp"
|
||||
android:background="@drawable/game_selector"
|
||||
android:focusable="true"
|
||||
android:background="@drawable/game_selector" >
|
||||
android:gravity="center_vertical|left"
|
||||
android:orientation="horizontal" >
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/browser"
|
||||
|
@ -34,25 +34,25 @@
|
|||
|
||||
<TextView
|
||||
android:id="@+id/browser_title"
|
||||
style="@android:style/TextAppearance.Large"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="center_vertical"
|
||||
style="@android:style/TextAppearance.Large"
|
||||
android:text="@string/browser"
|
||||
android:textSize="28dp"
|
||||
android:textAppearance="@style/BaseText" />
|
||||
android:textAppearance="@style/BaseText"
|
||||
android:textSize="28dp" />
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/settings_menu"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="10dp"
|
||||
android:gravity="center_vertical|left"
|
||||
android:layout_marginLeft="26dp"
|
||||
android:orientation="horizontal"
|
||||
android:layout_marginTop="10dp"
|
||||
android:background="@drawable/game_selector"
|
||||
android:focusable="true"
|
||||
android:background="@drawable/game_selector" >
|
||||
android:gravity="center_vertical|left"
|
||||
android:orientation="horizontal" >
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/config"
|
||||
|
@ -65,25 +65,25 @@
|
|||
|
||||
<TextView
|
||||
android:id="@+id/config_title"
|
||||
style="@android:style/TextAppearance.Large"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="center_vertical"
|
||||
style="@android:style/TextAppearance.Large"
|
||||
android:text="@string/settings"
|
||||
android:textSize="28dp"
|
||||
android:textAppearance="@style/BaseText" />
|
||||
android:textAppearance="@style/BaseText"
|
||||
android:textSize="28dp" />
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/paths_menu"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="10dp"
|
||||
android:gravity="center_vertical|left"
|
||||
android:layout_marginLeft="4dp"
|
||||
android:orientation="horizontal"
|
||||
android:layout_marginTop="10dp"
|
||||
android:background="@drawable/game_selector"
|
||||
android:focusable="true"
|
||||
android:background="@drawable/game_selector" >
|
||||
android:gravity="center_vertical|left"
|
||||
android:orientation="horizontal" >
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/options"
|
||||
|
@ -96,25 +96,25 @@
|
|||
|
||||
<TextView
|
||||
android:id="@+id/options_title"
|
||||
style="@android:style/TextAppearance.Large"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="center_vertical"
|
||||
style="@android:style/TextAppearance.Large"
|
||||
android:text="@string/paths"
|
||||
android:textSize="28dp"
|
||||
android:textAppearance="@style/BaseText" />
|
||||
android:textAppearance="@style/BaseText"
|
||||
android:textSize="28dp" />
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/input_menu"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="10dp"
|
||||
android:gravity="center_vertical|left"
|
||||
android:layout_marginLeft="26dp"
|
||||
android:orientation="horizontal"
|
||||
android:layout_marginTop="10dp"
|
||||
android:background="@drawable/game_selector"
|
||||
android:focusable="true"
|
||||
android:background="@drawable/game_selector" >
|
||||
android:gravity="center_vertical|left"
|
||||
android:orientation="horizontal" >
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/input"
|
||||
|
@ -127,25 +127,25 @@
|
|||
|
||||
<TextView
|
||||
android:id="@+id/input_title"
|
||||
style="@android:style/TextAppearance.Large"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="center_vertical"
|
||||
style="@android:style/TextAppearance.Large"
|
||||
android:text="@string/input"
|
||||
android:textSize="28dp"
|
||||
android:textAppearance="@style/BaseText" />
|
||||
android:textAppearance="@style/BaseText"
|
||||
android:textSize="28dp" />
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/about_menu"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="10dp"
|
||||
android:gravity="center_vertical|left"
|
||||
android:layout_marginLeft="4dp"
|
||||
android:orientation="horizontal"
|
||||
android:layout_marginTop="10dp"
|
||||
android:background="@drawable/game_selector"
|
||||
android:focusable="true"
|
||||
android:background="@drawable/game_selector" >
|
||||
android:gravity="center_vertical|left"
|
||||
android:orientation="horizontal" >
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/about"
|
||||
|
@ -158,25 +158,25 @@
|
|||
|
||||
<TextView
|
||||
android:id="@+id/about_title"
|
||||
style="@android:style/TextAppearance.Large"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="center_vertical"
|
||||
style="@android:style/TextAppearance.Large"
|
||||
android:text="@string/about"
|
||||
android:textSize="28dp"
|
||||
android:textAppearance="@style/BaseText" />
|
||||
android:textAppearance="@style/BaseText"
|
||||
android:textSize="28dp" />
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/rateme_menu"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="10dp"
|
||||
android:gravity="center_vertical|left"
|
||||
android:layout_marginLeft="26dp"
|
||||
android:orientation="horizontal"
|
||||
android:layout_marginTop="10dp"
|
||||
android:background="@drawable/game_selector"
|
||||
android:focusable="true"
|
||||
android:background="@drawable/game_selector" >
|
||||
android:gravity="center_vertical|left"
|
||||
android:orientation="horizontal" >
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/rate"
|
||||
|
@ -188,13 +188,44 @@
|
|||
|
||||
<TextView
|
||||
android:id="@+id/rateme_title"
|
||||
style="@android:style/TextAppearance.Large"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="center_vertical"
|
||||
style="@android:style/TextAppearance.Large"
|
||||
android:text="@string/rateme"
|
||||
android:textSize="28dp"
|
||||
android:textAppearance="@style/BaseText" />
|
||||
android:textAppearance="@style/BaseText"
|
||||
android:textSize="28dp" />
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/message_menu"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginLeft="4dp"
|
||||
android:layout_marginTop="10dp"
|
||||
android:background="@drawable/game_selector"
|
||||
android:focusable="true"
|
||||
android:gravity="center_vertical|left"
|
||||
android:orientation="horizontal" >
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/messages"
|
||||
android:layout_width="52dp"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_margin="2dp"
|
||||
android:gravity="center_vertical|left"
|
||||
android:scaleType="fitCenter"
|
||||
android:src="@drawable/ic_launcher" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/message_title"
|
||||
style="@android:style/TextAppearance.Large"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="center_vertical"
|
||||
android:text="@string/messages"
|
||||
android:textAppearance="@style/BaseText"
|
||||
android:textSize="28dp" />
|
||||
</LinearLayout>
|
||||
</LinearLayout>
|
||||
|
||||
|
|
|
@ -0,0 +1,32 @@
|
|||
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:id="@+id/toast_layout_root"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="fill_parent"
|
||||
android:background="#50FFFFFF"
|
||||
android:orientation="horizontal"
|
||||
android:padding="5dp"
|
||||
android:alpha="0.5">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/image"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_centerVertical="true"
|
||||
android:adjustViewBounds="true"
|
||||
android:maxHeight="80dip"
|
||||
android:maxWidth="80dip" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/text"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_centerVertical="true"
|
||||
android:layout_toRightOf="@+id/image"
|
||||
android:layout_marginLeft="5dip"
|
||||
android:includeFontPadding="false"
|
||||
android:lineSpacingMultiplier="1.0"
|
||||
android:textColor="#000000"
|
||||
android:textSize="18dip"
|
||||
android:typeface="sans" />
|
||||
|
||||
</RelativeLayout>
|
|
@ -15,6 +15,7 @@
|
|||
<string name="missing_flash">Flash Missing. The Dreamcast Flash is required for this emulator to work. Place the Flash file in %1$s/data/dc_flash.bin</string>
|
||||
<string name="require_bios">You have to provide the BIOS</string>
|
||||
<string name="data_folder">The data folder is assumed</string>
|
||||
<string name="git_broken">GitHub Native Unavailable!</string>
|
||||
|
||||
<string name="folder_bios">BOOT BIOS</string>
|
||||
<string name="folder_select">SELECT CURRENT FOLDER</string>
|
||||
|
@ -87,6 +88,7 @@
|
|||
<string name="paths">Paths</string>
|
||||
<string name="input">Input</string>
|
||||
<string name="about">About</string>
|
||||
<string name="messages">Messages</string>
|
||||
<string name="rateme">Rate Me</string>
|
||||
|
||||
<string name="textOn">ON</string>
|
||||
|
|
|
@ -93,6 +93,7 @@ import android.widget.SlidingDrawer.OnDrawerOpenListener;
|
|||
import android.widget.TextView;
|
||||
import android.widget.Toast;
|
||||
|
||||
import com.reicast.emulator.config.Config;
|
||||
import com.reicast.emulator.debug.GitAdapter;
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
|
@ -258,9 +259,7 @@ public class AboutFragment extends Fragment {
|
|||
} catch (JSONException e) {
|
||||
handler.post(new Runnable() {
|
||||
public void run() {
|
||||
Toast.makeText(parentActivity,
|
||||
"GitHub Native Unavailable!", Toast.LENGTH_LONG)
|
||||
.show();
|
||||
Config.customNotify(parentActivity, R.drawable.ic_github, R.string.git_broken);
|
||||
slidingGithub.close();
|
||||
}
|
||||
});
|
||||
|
@ -268,9 +267,7 @@ public class AboutFragment extends Fragment {
|
|||
} catch (Exception e) {
|
||||
handler.post(new Runnable() {
|
||||
public void run() {
|
||||
Toast.makeText(parentActivity,
|
||||
"GitHub Native Unavailable!", Toast.LENGTH_LONG)
|
||||
.show();
|
||||
Config.customNotify(parentActivity, R.drawable.ic_github, R.string.git_broken);
|
||||
slidingGithub.close();
|
||||
}
|
||||
});
|
||||
|
|
|
@ -53,6 +53,7 @@ public class MainActivity extends SlidingFragmentActivity implements
|
|||
|
||||
private UncaughtExceptionHandler mUEHandler;
|
||||
|
||||
private Intent debugger;
|
||||
public static boolean debugUser;
|
||||
|
||||
Gamepad pad = new Gamepad();
|
||||
|
@ -65,8 +66,8 @@ public class MainActivity extends SlidingFragmentActivity implements
|
|||
|
||||
mPrefs = PreferenceManager.getDefaultSharedPreferences(this);
|
||||
|
||||
Intent debugger = new Intent("com.reicast.emulator.debug.Debugger");
|
||||
debugger.setAction("com.reicast.emulator.DEBUG");
|
||||
debugger = new Intent("com.reicast.emulator.debug.Debug");
|
||||
debugger.setAction("reicast.emulator.DEBUG");
|
||||
if (isCallable(debugger)) {
|
||||
MainActivity.debugUser = true;
|
||||
}
|
||||
|
@ -283,6 +284,17 @@ public class MainActivity extends SlidingFragmentActivity implements
|
|||
}
|
||||
});
|
||||
}
|
||||
|
||||
View messages = findViewById(R.id.message_menu);
|
||||
if (MainActivity.debugUser) {
|
||||
messages.setOnClickListener(new OnClickListener() {
|
||||
public void onClick(View view) {
|
||||
startActivity(debugger);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
messages.setVisibility(View.GONE);
|
||||
}
|
||||
}
|
||||
});
|
||||
findViewById(R.id.header_list).setOnTouchListener(new OnTouchListener() {
|
||||
|
|
|
@ -1,10 +1,20 @@
|
|||
package com.reicast.emulator.config;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.Context;
|
||||
import android.content.SharedPreferences;
|
||||
import android.preference.PreferenceManager;
|
||||
import android.util.DisplayMetrics;
|
||||
import android.view.Gravity;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.TextView;
|
||||
import android.widget.Toast;
|
||||
|
||||
import com.android.util.DreamTime;
|
||||
import com.reicast.emulator.R;
|
||||
import com.reicast.emulator.emu.JNIdc;
|
||||
|
||||
public class Config {
|
||||
|
@ -75,4 +85,31 @@ public class Config {
|
|||
JNIdc.dreamtime(DreamTime.getDreamtime());
|
||||
}
|
||||
|
||||
public static void customNotify(Activity activity, int icon, int message) {
|
||||
LayoutInflater inflater = activity.getLayoutInflater();
|
||||
View layout = inflater.inflate(R.layout.toast_layout,
|
||||
(ViewGroup) activity.findViewById(R.id.toast_layout_root));
|
||||
|
||||
ImageView image = (ImageView) layout.findViewById(R.id.image);
|
||||
if (icon != -1) {
|
||||
image.setImageResource(icon);
|
||||
} else {
|
||||
image.setImageResource(R.drawable.ic_launcher);
|
||||
}
|
||||
|
||||
TextView text = (TextView) layout.findViewById(R.id.text);
|
||||
text.setText(activity.getString(message));
|
||||
|
||||
DisplayMetrics metrics = new DisplayMetrics();
|
||||
activity.getWindowManager().getDefaultDisplay().getMetrics(metrics);
|
||||
final float scale = activity.getResources().getDisplayMetrics().density;
|
||||
int toastPixels = (int) ((metrics.widthPixels * scale + 0.5f) / 14);
|
||||
|
||||
Toast toast = new Toast(activity);
|
||||
toast.setGravity(Gravity.BOTTOM, 0, toastPixels);
|
||||
toast.setDuration(Toast.LENGTH_SHORT);
|
||||
toast.setView(layout);
|
||||
toast.show();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -10,6 +10,7 @@ import android.content.SharedPreferences;
|
|||
import android.os.Build;
|
||||
import android.os.Handler;
|
||||
import android.preference.PreferenceManager;
|
||||
import android.util.Log;
|
||||
import android.widget.Toast;
|
||||
|
||||
import com.bda.controller.Controller;
|
||||
|
@ -37,7 +38,6 @@ public class MOGAInput
|
|||
static final int ACTION_VERSION_MOGAPRO = Controller.ACTION_VERSION_MOGAPRO;
|
||||
|
||||
public Controller mController = null;
|
||||
private Handler handler;
|
||||
private String notify;
|
||||
private Gamepad pad;
|
||||
|
||||
|
@ -81,7 +81,6 @@ public class MOGAInput
|
|||
|
||||
this.pad = pad;
|
||||
|
||||
handler = new Handler();
|
||||
prefs = PreferenceManager
|
||||
.getDefaultSharedPreferences(act.getApplicationContext());
|
||||
|
||||
|
@ -157,7 +156,6 @@ public class MOGAInput
|
|||
pad.custom[playerNum] = prefs.getBoolean("modified_key_layout" + id, false);
|
||||
pad.compat[playerNum] = prefs.getBoolean("controller_compat" + id, false);
|
||||
pad.joystick[playerNum] = prefs.getBoolean("separate_joystick" + id, false);
|
||||
pad.isActiveMoga[playerNum] = true;
|
||||
if (pad.compat[playerNum]) {
|
||||
getCompatibilityMap(playerNum, id);
|
||||
} else if (pad.custom[playerNum]) {
|
||||
|
@ -166,11 +164,6 @@ public class MOGAInput
|
|||
pad.map[playerNum] = pad.getMogaController();
|
||||
}
|
||||
initJoyStickLayout(playerNum);
|
||||
handler.post(new Runnable() {
|
||||
public void run() {
|
||||
Toast.makeText(act.getApplicationContext(), notify, Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void onStateEvent(StateEvent event)
|
||||
|
@ -191,12 +184,14 @@ public class MOGAInput
|
|||
int mControllerVersion = mController.getState(Controller.STATE_CURRENT_PRODUCT_VERSION);
|
||||
if (mControllerVersion == Controller.ACTION_VERSION_MOGAPRO) {
|
||||
pad.isMogaPro[playerNum] = true;
|
||||
notify = act.getApplicationContext().getString(R.string.moga_pro_connect);
|
||||
pad.isActiveMoga[playerNum] = true;
|
||||
Log.d("com.reicast.emulator", act.getApplicationContext().getString(R.string.moga_pro_connect));
|
||||
} else if (mControllerVersion == Controller.ACTION_VERSION_MOGA) {
|
||||
pad.isMogaPro[playerNum] = false;
|
||||
notify = act.getApplicationContext().getString(R.string.moga_connect);
|
||||
pad.isActiveMoga[playerNum] = true;
|
||||
Log.d("com.reicast.emulator", act.getApplicationContext().getString(R.string.moga_connect));
|
||||
}
|
||||
if (notify != null && !notify.equals(null)) {
|
||||
if (pad.isActiveMoga[playerNum]) {
|
||||
notifyMogaConnected(notify, playerNum);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
#APP_ABI := armeabi-v7a x86
|
||||
APP_ABI := armeabi-v7a
|
||||
APP_ABI := armeabi-v7a x86
|
||||
#APP_ABI := armeabi-v7a
|
||||
APP_PLATFORM := android-9
|
||||
NDK_TOOLCHAIN_VERSION := 4.8
|
|
@ -9,18 +9,38 @@
|
|||
android:targetSdkVersion="18" />
|
||||
|
||||
<uses-permission android:name="android.permission.READ_LOGS" />
|
||||
<uses-permission android:name="android.permission.GET_ACCOUNTS" />
|
||||
<uses-permission android:name="android.permission.WAKE_LOCK" />
|
||||
<uses-permission android:name="android.permission.VIBRATE" />
|
||||
<uses-permission android:name="com.google.android.c2dm.permission.RECEIVE" />
|
||||
<permission
|
||||
android:name="com.reicast.emulator.debug.permission.C2D_MESSAGE"
|
||||
android:protectionLevel="signature" />
|
||||
<uses-permission android:name="com.reicast.emulator.debug.permission.C2D_MESSAGE" />
|
||||
|
||||
<application
|
||||
android:allowBackup="true"
|
||||
android:icon="@drawable/ic_launcher"
|
||||
android:label="@string/app_name" >
|
||||
<activity android:name=".Debugger" >
|
||||
android:label="@string/app_name">
|
||||
<activity android:name=".Debug"
|
||||
android:excludeFromRecents="true"
|
||||
android:theme="@style/Theme.PanelTheme" >
|
||||
<intent-filter>
|
||||
<action android:name="com.reicast.emulator.DEBUG" />
|
||||
<action android:name="reicast.emulator.DEBUG" />
|
||||
<action android:name="reicast.emulator.NOTIFY" />
|
||||
|
||||
<category android:name="android.intent.category.DEFAULT" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
<receiver
|
||||
android:name=".GcmBroadcastReceiver"
|
||||
android:permission="com.google.android.c2dm.permission.SEND" >
|
||||
<intent-filter>
|
||||
<action android:name="com.google.android.c2dm.intent.RECEIVE" />
|
||||
|
||||
<category android:name="com.reicast.emulator.debug" />
|
||||
</intent-filter>
|
||||
</receiver>
|
||||
</application>
|
||||
|
||||
</manifest>
|
|
@ -1,5 +1,5 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project name="reicast-debugger" default="help">
|
||||
<project name="reicast-debug" default="help">
|
||||
|
||||
<!-- The local.properties file is created and updated by the 'android' tool.
|
||||
It contains the path to the SDK. It should *NOT* be checked into
|
||||
|
|
After Width: | Height: | Size: 249 B |
After Width: | Height: | Size: 269 B |
After Width: | Height: | Size: 182 B |
After Width: | Height: | Size: 549 B |
After Width: | Height: | Size: 535 B |
After Width: | Height: | Size: 559 B |
After Width: | Height: | Size: 566 B |
After Width: | Height: | Size: 206 B |
After Width: | Height: | Size: 216 B |
After Width: | Height: | Size: 163 B |
After Width: | Height: | Size: 351 B |
After Width: | Height: | Size: 350 B |
After Width: | Height: | Size: 362 B |
After Width: | Height: | Size: 362 B |
After Width: | Height: | Size: 255 B |
After Width: | Height: | Size: 285 B |
After Width: | Height: | Size: 229 B |
After Width: | Height: | Size: 653 B |
After Width: | Height: | Size: 633 B |
After Width: | Height: | Size: 687 B |
After Width: | Height: | Size: 660 B |
|
@ -0,0 +1,12 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:shape="rectangle" >
|
||||
|
||||
<!-- Gradient Bg for listrow -->
|
||||
<gradient
|
||||
android:angle="270"
|
||||
android:centerColor="#e7e7e8"
|
||||
android:endColor="#cfcfcf"
|
||||
android:startColor="#f1f1f2" />
|
||||
|
||||
</shape>
|
|
@ -0,0 +1,12 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:shape="rectangle" >
|
||||
|
||||
<!-- Gradient BgColor for listrow Selected -->
|
||||
<gradient
|
||||
android:angle="270"
|
||||
android:centerColor="#3f7fd7"
|
||||
android:endColor="#5699f6"
|
||||
android:startColor="#5699f6" />
|
||||
|
||||
</shape>
|
|
@ -0,0 +1,14 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<layer-list xmlns:android="http://schemas.android.com/apk/res/android" >
|
||||
|
||||
<item>
|
||||
<shape android:shape="rectangle" >
|
||||
<stroke
|
||||
android:width="1dp"
|
||||
android:color="#dbdbdc" />
|
||||
|
||||
<solid android:color="#FFFFFF" />
|
||||
</shape>
|
||||
</item>
|
||||
|
||||
</layer-list>
|
|
@ -0,0 +1,9 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<selector xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
|
||||
<!-- Selector style for listrow -->
|
||||
<item android:drawable="@drawable/gradient_bg" android:state_pressed="false" android:state_selected="false"/>
|
||||
<item android:drawable="@drawable/gradient_bg_hover" android:state_pressed="true"/>
|
||||
<item android:drawable="@drawable/gradient_bg_hover" android:state_pressed="false" android:state_selected="true"/>
|
||||
|
||||
</selector>
|
|
@ -0,0 +1,22 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Copyright (C) 2010 The Android Open Source Project
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
-->
|
||||
|
||||
<selector xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<item android:state_enabled="false" android:drawable="@drawable/switch_thumb_disabled_holo_dark" />
|
||||
<item android:state_pressed="true" android:drawable="@drawable/switch_thumb_pressed_holo_dark" />
|
||||
<item android:state_checked="true" android:drawable="@drawable/switch_thumb_activated_holo_dark" />
|
||||
<item android:drawable="@drawable/switch_thumb_holo_dark" />
|
||||
</selector>
|
|
@ -0,0 +1,20 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Copyright (C) 2010 The Android Open Source Project
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
-->
|
||||
|
||||
<selector xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<item android:state_focused="true" android:drawable="@drawable/switch_bg_focused_holo_dark" />
|
||||
<item android:drawable="@drawable/switch_bg_holo_dark" />
|
||||
</selector>
|
|
@ -0,0 +1,57 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="@drawable/list_selector"
|
||||
android:orientation="horizontal"
|
||||
android:padding="5dip" >
|
||||
|
||||
<!-- ListRow Left Thumbnail image -->
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/thumbnail"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_alignParentLeft="true"
|
||||
android:layout_marginRight="5dip"
|
||||
android:background="@drawable/image_bg"
|
||||
android:padding="3dip" >
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/list_image"
|
||||
android:contentDescription="@+id/icon"
|
||||
android:layout_width="60dip"
|
||||
android:layout_height="60dip"
|
||||
android:gravity="center"
|
||||
android:src="@drawable/ic_launcher" />
|
||||
</LinearLayout>
|
||||
<!-- Main Item Content -->
|
||||
|
||||
<TextView
|
||||
android:textIsSelectable="false"
|
||||
android:id="@+id/item_name"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_alignTop="@+id/thumbnail"
|
||||
android:layout_toRightOf="@+id/thumbnail"
|
||||
android:textColor="#040404"
|
||||
android:textSize="14sp"
|
||||
android:textStyle="bold"
|
||||
android:typeface="sans"
|
||||
android:paddingLeft="2dip" />
|
||||
|
||||
<TextView
|
||||
android:textIsSelectable="false"
|
||||
android:id="@+id/item_content"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_below="@+id/item_name"
|
||||
android:layout_marginTop="1dip"
|
||||
android:layout_toRightOf="@+id/thumbnail"
|
||||
android:textColor="#343434"
|
||||
android:textSize="12sp"
|
||||
android:textStyle="bold"
|
||||
android:paddingLeft="2dip" />
|
||||
|
||||
|
||||
</RelativeLayout>
|
|
@ -0,0 +1,73 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="fill_parent"
|
||||
android:fitsSystemWindows="true"
|
||||
android:orientation="vertical" >
|
||||
|
||||
<TableLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginLeft="6dp"
|
||||
android:stretchColumns="*" >
|
||||
|
||||
<TableRow
|
||||
android:layout_marginTop="10dp"
|
||||
android:gravity="center_vertical" >
|
||||
|
||||
<TextView
|
||||
android:id="@+id/notify_text"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="0.5"
|
||||
android:ems="10"
|
||||
android:gravity="center_vertical|left"
|
||||
android:text="@string/select_notify" />
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:gravity="right"
|
||||
android:orientation="vertical" >
|
||||
|
||||
<de.ankri.views.Switch
|
||||
android:id="@+id/notify_option"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:focusable="true" />
|
||||
</LinearLayout>
|
||||
</TableRow>
|
||||
</TableLayout>
|
||||
|
||||
<EditText
|
||||
android:id="@+id/txt_message"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="10dp"
|
||||
android:layout_weight="0.25"
|
||||
android:ems="10"
|
||||
android:hint="Enter Broadcast Message"
|
||||
android:inputType="textCapSentences"
|
||||
android:maxLines="4"
|
||||
android:minLines="2" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/txt_button"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="10dp"
|
||||
android:layout_weight="0.25"
|
||||
android:ems="10"
|
||||
android:text="Submit Message" />
|
||||
|
||||
<ListView
|
||||
android:id="@+id/dialog_list"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="10dp"
|
||||
android:layout_weight="1.0"
|
||||
android:divider="#b5b5b5"
|
||||
android:dividerHeight="1dp" />
|
||||
|
||||
</LinearLayout>
|
|
@ -0,0 +1,55 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<attr name="switchStyle" format="reference" />
|
||||
<declare-styleable name="Switch">
|
||||
<attr name="thumb" format="reference"/>
|
||||
<attr name="track" format="reference" />
|
||||
<attr name="textOn" format="string"/>
|
||||
<attr name="textOff" format="string"/>
|
||||
<attr name="thumbTextPadding" format="dimension" />
|
||||
<attr name="switchTextAppearance" format="reference" />
|
||||
<attr name="switchMinWidth" format="dimension" />
|
||||
<attr name="switchPadding" format="dimension" />
|
||||
<attr name="switchPreferenceStyle" format="reference" />
|
||||
</declare-styleable>
|
||||
|
||||
<attr name="textAppearance" format="reference" />
|
||||
<declare-styleable name="TextAppearanceSwitch" >
|
||||
<attr name="textColor" format="color" />
|
||||
<attr name="textSize" format="dimension"/>
|
||||
<attr name="textStyle" format="reference"/>
|
||||
<attr name="typeface" format="reference"/>
|
||||
<attr name="fontFamily" format="reference"/>
|
||||
<attr name="textColorHighlight" format="color"/>
|
||||
<attr name="textColorHint" format="color" />
|
||||
<attr name="textColorLink" format="color" />
|
||||
<attr name="textAllCaps" format="boolean" />
|
||||
</declare-styleable>
|
||||
|
||||
<declare-styleable name="SlidingMenu">
|
||||
<attr name="mode">
|
||||
<enum name="left" value="0" />
|
||||
<enum name="right" value="1" />
|
||||
</attr>
|
||||
<attr name="viewAbove" format="reference" />
|
||||
<attr name="viewBehind" format="reference" />
|
||||
<attr name="behindOffset" format="dimension" />
|
||||
<attr name="behindWidth" format="dimension" />
|
||||
<attr name="behindScrollScale" format="float" />
|
||||
<attr name="touchModeAbove">
|
||||
<enum name="margin" value="0" />
|
||||
<enum name="fullscreen" value="1" />
|
||||
</attr>
|
||||
<attr name="touchModeBehind">
|
||||
<enum name="margin" value="0" />
|
||||
<enum name="fullscreen" value="1" />
|
||||
</attr>
|
||||
<attr name="shadowDrawable" format="reference" />
|
||||
<attr name="shadowWidth" format="dimension" />
|
||||
<attr name="fadeEnabled" format="boolean" />
|
||||
<attr name="fadeDegree" format="float" />
|
||||
<attr name="selectorEnabled" format="boolean" />
|
||||
<attr name="selectorDrawable" format="reference" />
|
||||
</declare-styleable>
|
||||
|
||||
</resources>
|
|
@ -0,0 +1,5 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<color name="white">#50FFFFFF</color>
|
||||
<color name="black">#B0000000</color>
|
||||
</resources>
|
|
@ -1,4 +1,10 @@
|
|||
<resources>
|
||||
|
||||
<string name="app_name">reicast debugger</string>
|
||||
<string name="app_name">reicast debug</string>
|
||||
|
||||
<string name="select_notify">Message Notifications</string>
|
||||
|
||||
<string name="textOn">ON</string>
|
||||
<string name="textOff">OFF</string>
|
||||
|
||||
</resources>
|
||||
|
|
|
@ -0,0 +1,33 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
|
||||
<style name="Theme.PanelTheme" parent="android:Theme.NoTitleBar">
|
||||
<item name="android:windowIsTranslucent">true</item>
|
||||
<item name="android:windowBackground">@color/black</item>
|
||||
<item name="android:windowContentOverlay">@null</item>
|
||||
<item name="android:windowNoTitle">true</item>
|
||||
<item name="android:windowIsFloating">true</item>
|
||||
<item name="switchStyle">@style/switch_dark</item>
|
||||
<item name="textAppearance">@style/TextAppearance</item>
|
||||
</style>
|
||||
|
||||
<style name="switch_dark">
|
||||
<item name="track">@drawable/switch_track_holo_dark</item>
|
||||
<item name="thumb">@drawable/switch_inner_holo_dark</item>
|
||||
<item name="textOn">@string/textOn</item>
|
||||
<item name="textOff">@string/textOff</item>
|
||||
<item name="thumbTextPadding">12dip</item>
|
||||
<item name="switchMinWidth">96dip</item>
|
||||
<item name="switchPadding">16dip</item>
|
||||
<item name="switchTextAppearance">@style/TextAppearance</item>
|
||||
</style>
|
||||
|
||||
<style name="TextAppearance">
|
||||
<item name="textColor">?android:attr/textColorPrimary</item>
|
||||
<item name="textColorHighlight">?android:attr/textColorHighlight</item>
|
||||
<item name="textColorHint">?android:attr/textColorHint</item>
|
||||
<item name="textColorLink">?android:attr/textColorLink</item>
|
||||
<item name="textSize">16sp</item>
|
||||
</style>
|
||||
|
||||
</resources>
|
|
@ -0,0 +1,613 @@
|
|||
package com.reicast.emulator.debug;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.OutputStream;
|
||||
import java.net.HttpURLConnection;
|
||||
import java.net.InetAddress;
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.NetworkInterface;
|
||||
import java.net.ProtocolException;
|
||||
import java.net.URL;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import org.apache.http.HttpEntity;
|
||||
import org.apache.http.HttpResponse;
|
||||
import org.apache.http.NameValuePair;
|
||||
import org.apache.http.client.ClientProtocolException;
|
||||
import org.apache.http.client.HttpClient;
|
||||
import org.apache.http.client.ResponseHandler;
|
||||
import org.apache.http.client.entity.UrlEncodedFormEntity;
|
||||
import org.apache.http.client.methods.HttpPost;
|
||||
import org.apache.http.conn.util.InetAddressUtils;
|
||||
import org.apache.http.impl.client.DefaultHttpClient;
|
||||
import org.apache.http.message.BasicNameValuePair;
|
||||
|
||||
import android.accounts.Account;
|
||||
import android.accounts.AccountManager;
|
||||
import android.app.Activity;
|
||||
import android.app.AlertDialog;
|
||||
import android.app.Dialog;
|
||||
import android.content.Context;
|
||||
import android.content.DialogInterface;
|
||||
import android.content.SharedPreferences;
|
||||
import android.content.pm.PackageInfo;
|
||||
import android.content.pm.PackageManager.NameNotFoundException;
|
||||
import android.os.AsyncTask;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
import android.preference.PreferenceManager;
|
||||
import android.util.Log;
|
||||
import android.util.Patterns;
|
||||
import android.view.KeyEvent;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.View.OnClickListener;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.AdapterView;
|
||||
import android.widget.AdapterView.OnItemClickListener;
|
||||
import android.widget.CompoundButton.OnCheckedChangeListener;
|
||||
import android.widget.BaseAdapter;
|
||||
import android.widget.Button;
|
||||
import android.widget.CompoundButton;
|
||||
import android.widget.EditText;
|
||||
import android.widget.ListView;
|
||||
import android.widget.TextView;
|
||||
|
||||
import com.google.android.gms.common.ConnectionResult;
|
||||
import com.google.android.gms.common.GooglePlayServicesUtil;
|
||||
import com.google.android.gms.gcm.GoogleCloudMessaging;
|
||||
|
||||
import de.ankri.views.Switch;
|
||||
|
||||
public class Debug extends Activity {
|
||||
|
||||
public static final String APP_TAG = "reicast-debug";
|
||||
|
||||
public static final String EXTRA_MESSAGE = "message";
|
||||
private static final String PROPERTY_APP_VERSION = "appVersion";
|
||||
private static final String PROPERTY_ON_SERVER_EXPIRATION_TIME = "onServerExpirationTimeMs";
|
||||
static final String SERVER_URL = "http://twisted.dyndns.tv:3194/ReicastBot/plugin/register.php";
|
||||
public static final long REGISTRATION_EXPIRY_TIME_MS = 1000 * 3600 * 24 * 30;
|
||||
String SENDER_ID = "847786358946";
|
||||
static final String TAG = "GCM::Service";
|
||||
GoogleCloudMessaging gcm;
|
||||
public static final String REG_ID = "registration_id";
|
||||
AtomicInteger msgId = new AtomicInteger();
|
||||
|
||||
public static final String cloudUrl = "http://twisted.dyndns.tv:3194/ReicastBot/plugin/submit.php";
|
||||
public static final String numberUrl = "http://twisted.dyndns.tv:3194/ReicastBot/plugin/number.php";
|
||||
public static final String archiveUrl = "http://twisted.dyndns.tv:3194/ReicastBot/plugin/archive.php";
|
||||
|
||||
public static final String PREF_IDENTITY = "pref_user_identity";
|
||||
|
||||
private Context mContext;
|
||||
private SharedPreferences mPrefs;
|
||||
|
||||
private ExpandableListView exlist = new ExpandableListView();
|
||||
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
setContentView(R.layout.dialog_message);
|
||||
mContext = this.getApplicationContext();
|
||||
mPrefs = PreferenceManager.getDefaultSharedPreferences(mContext);
|
||||
int resultCode = GooglePlayServicesUtil
|
||||
.isGooglePlayServicesAvailable(mContext);
|
||||
if (resultCode == ConnectionResult.SUCCESS) {
|
||||
String regid = getRegistrationId(mContext);
|
||||
if (regid.length() == 0) {
|
||||
RegisterTask mRegisterTask = new RegisterTask();
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
|
||||
mRegisterTask
|
||||
.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
|
||||
} else {
|
||||
mRegisterTask.execute();
|
||||
}
|
||||
}
|
||||
submitGcmUser(regid);
|
||||
gcm = GoogleCloudMessaging.getInstance(mContext);
|
||||
} else {
|
||||
GooglePlayServicesUtil.getErrorDialog(resultCode, this, 1).show();
|
||||
}
|
||||
|
||||
OnCheckedChangeListener notify_options = new OnCheckedChangeListener() {
|
||||
|
||||
public void onCheckedChanged(CompoundButton buttonView,
|
||||
boolean isChecked) {
|
||||
mPrefs.edit().putBoolean("enable_messaging", isChecked).commit();
|
||||
}
|
||||
};
|
||||
Switch notify_opt = (Switch) findViewById(R.id.notify_option);
|
||||
notify_opt.setChecked(mPrefs.getBoolean("enable_messaging", true));
|
||||
notify_opt.setOnCheckedChangeListener(notify_options);
|
||||
|
||||
RequestArchive mRequestArchive = new RequestArchive() {
|
||||
@Override
|
||||
protected void onPostExecute(List<String[]> jsonArray) {
|
||||
if (jsonArray != null && !jsonArray.isEmpty()) {
|
||||
ListView wrapper = (ListView) findViewById(R.id.dialog_list);
|
||||
DisplayDialog dialog = new DisplayDialog(Debug.this);
|
||||
MessageAdapter adapter = new MessageAdapter(mContext,
|
||||
jsonArray, dialog);
|
||||
wrapper.setAdapter(adapter);
|
||||
adapter.notifyDataSetChanged();
|
||||
exlist.setListViewHeightBasedOnChildren(wrapper);
|
||||
wrapper.invalidate();
|
||||
}
|
||||
}
|
||||
};
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
|
||||
mRequestArchive.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR,
|
||||
archiveUrl);
|
||||
} else {
|
||||
mRequestArchive.execute(archiveUrl);
|
||||
}
|
||||
final EditText msgEntry = (EditText) findViewById(R.id.txt_message);
|
||||
Button submit = (Button) findViewById(R.id.txt_button);
|
||||
submit.setOnClickListener(new OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
if (msgEntry.getText() != null
|
||||
&& !msgEntry.getText().toString().equals("")) {
|
||||
String message = msgEntry.getText().toString();
|
||||
submitGcmBroadcast(message);
|
||||
msgEntry.setText("");
|
||||
}
|
||||
}
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
public class MessageAdapter extends BaseAdapter {
|
||||
@SuppressWarnings("unused")
|
||||
private Context mContext;
|
||||
private List<String[]> data;
|
||||
private DisplayDialog note;
|
||||
|
||||
public MessageAdapter(Context c, List<String[]> d, DisplayDialog n) {
|
||||
mContext = c;
|
||||
data = d;
|
||||
note = n;
|
||||
}
|
||||
|
||||
public int getCount() {
|
||||
return data.size();
|
||||
}
|
||||
|
||||
public Object getItem(int position) {
|
||||
return position;
|
||||
}
|
||||
|
||||
public long getItemId(int position) {
|
||||
return position;
|
||||
}
|
||||
|
||||
public View getView(final int position, View convertView,
|
||||
ViewGroup parent) {
|
||||
System.gc();
|
||||
|
||||
LayoutInflater inflater = (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE);
|
||||
View vi = convertView;
|
||||
if (convertView == null)
|
||||
vi = inflater.inflate(R.layout.dialog_item, null);
|
||||
|
||||
String[] message = data.get(position);
|
||||
final String title = message[0] + " [" + message[2] + "]";
|
||||
final String content = message[1];
|
||||
|
||||
TextView itemName = (TextView) vi.findViewById(R.id.item_name);
|
||||
itemName.setText(title);
|
||||
|
||||
TextView itemContent = (TextView) vi
|
||||
.findViewById(R.id.item_content);
|
||||
itemContent.setText(content);
|
||||
|
||||
vi.setOnClickListener(new OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
new Handler().post(new Runnable() {
|
||||
public void run() {
|
||||
note.showMessage(title, content);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
return vi;
|
||||
}
|
||||
}
|
||||
|
||||
public class DisplayDialog extends AlertDialog.Builder {
|
||||
|
||||
public DisplayDialog(Context context) {
|
||||
super(context);
|
||||
}
|
||||
|
||||
public void showMessage(String title, String content) {
|
||||
|
||||
this.setTitle(title);
|
||||
this.setMessage(content);
|
||||
this.setNegativeButton("Dismiss",
|
||||
new DialogInterface.OnClickListener() {
|
||||
public void onClick(DialogInterface dialog, int which) {
|
||||
dialog.dismiss();
|
||||
}
|
||||
});
|
||||
this.setPositiveButton(null, null);
|
||||
this.setOnKeyListener(new Dialog.OnKeyListener() {
|
||||
public boolean onKey(DialogInterface dialog, int keyCode,
|
||||
KeyEvent event) {
|
||||
dialog.dismiss();
|
||||
return true;
|
||||
}
|
||||
});
|
||||
this.create();
|
||||
this.show();
|
||||
}
|
||||
}
|
||||
|
||||
private String getRegistrationId(Context context) {
|
||||
String registrationId = mPrefs.getString(REG_ID, "");
|
||||
if (registrationId.length() == 0) {
|
||||
return "";
|
||||
}
|
||||
// check if app was updated; if so, it must clear registration id to
|
||||
// avoid a race condition if GCM sends a message
|
||||
int registeredVersion = mPrefs.getInt(PROPERTY_APP_VERSION,
|
||||
Integer.MIN_VALUE);
|
||||
int currentVersion = getAppVersion(context);
|
||||
if (registeredVersion != currentVersion || isRegistrationExpired()) {
|
||||
return "";
|
||||
}
|
||||
return registrationId;
|
||||
}
|
||||
|
||||
private static int getAppVersion(Context context) {
|
||||
try {
|
||||
PackageInfo packageInfo = context.getPackageManager()
|
||||
.getPackageInfo(context.getPackageName(), 0);
|
||||
return packageInfo.versionCode;
|
||||
} catch (NameNotFoundException e) {
|
||||
// should never happen
|
||||
throw new RuntimeException("Could not get package name: " + e);
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isRegistrationExpired() {
|
||||
long expirationTime = mPrefs.getLong(
|
||||
PROPERTY_ON_SERVER_EXPIRATION_TIME, -1);
|
||||
return System.currentTimeMillis() > expirationTime;
|
||||
}
|
||||
|
||||
public class RegisterTask extends AsyncTask<String, Integer, String> {
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
protected String doInBackground(String... params) {
|
||||
try {
|
||||
if (gcm == null) {
|
||||
gcm = GoogleCloudMessaging.getInstance(mContext);
|
||||
}
|
||||
String regid = gcm.register(SENDER_ID);
|
||||
Log.d(APP_TAG, "Registration id: " + regid);
|
||||
setRegistrationId(mContext, regid);
|
||||
String possibleEmail = getAccountEmail();
|
||||
if (possibleEmail != null) {
|
||||
String serverUrl = SERVER_URL;
|
||||
Map<String, String> user = new HashMap<String, String>();
|
||||
user.put("regId", regid);
|
||||
user.put("email", possibleEmail);
|
||||
PostServer mPostServer = new PostServer(serverUrl);
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
|
||||
mPostServer.executeOnExecutor(
|
||||
AsyncTask.THREAD_POOL_EXECUTOR, user);
|
||||
} else {
|
||||
mPostServer.execute(user);
|
||||
}
|
||||
}
|
||||
return regid;
|
||||
} catch (IOException ex) {
|
||||
Log.d(APP_TAG, "Error :" + ex.getMessage());
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPostExecute(String regid) {
|
||||
if (regid != null && !regid.equals(null)) {
|
||||
submitGcmUser(regid);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
private class PostServer extends
|
||||
AsyncTask<Map<String, String>, Integer, String> {
|
||||
|
||||
private String serverUrl;
|
||||
|
||||
PostServer(String serverUrl) {
|
||||
this.serverUrl = serverUrl;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String doInBackground(Map<String, String>... params) {
|
||||
URL url;
|
||||
try {
|
||||
url = new URL(serverUrl);
|
||||
} catch (MalformedURLException e) {
|
||||
throw new IllegalArgumentException("invalid url: " + serverUrl);
|
||||
}
|
||||
StringBuilder bodyBuilder = new StringBuilder();
|
||||
Iterator<Entry<String, String>> iterator = params[0].entrySet()
|
||||
.iterator();
|
||||
while (iterator.hasNext()) {
|
||||
Entry<String, String> param = iterator.next();
|
||||
bodyBuilder.append(param.getKey()).append('=')
|
||||
.append(param.getValue());
|
||||
if (iterator.hasNext()) {
|
||||
bodyBuilder.append('&');
|
||||
}
|
||||
}
|
||||
String body = bodyBuilder.toString();
|
||||
byte[] bytes = body.getBytes();
|
||||
HttpURLConnection conn = null;
|
||||
try {
|
||||
conn = (HttpURLConnection) url.openConnection();
|
||||
conn.setDoOutput(true);
|
||||
conn.setUseCaches(false);
|
||||
conn.setFixedLengthStreamingMode(bytes.length);
|
||||
conn.setRequestMethod("POST");
|
||||
conn.setRequestProperty("Content-Type",
|
||||
"application/x-www-form-urlencoded;charset=UTF-8");
|
||||
// post the request
|
||||
OutputStream out = conn.getOutputStream();
|
||||
out.write(bytes);
|
||||
out.close();
|
||||
// handle the response
|
||||
int status = conn.getResponseCode();
|
||||
if (status != 200) {
|
||||
throw new IOException("Post failed with error code "
|
||||
+ status);
|
||||
}
|
||||
conn.disconnect();
|
||||
} catch (ProtocolException p) {
|
||||
p.printStackTrace();
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPostExecute(String results) {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
private void setRegistrationId(Context context, String regId) {
|
||||
int appVersion = getAppVersion(context);
|
||||
SharedPreferences.Editor editor = mPrefs.edit();
|
||||
editor.putString(REG_ID, regId);
|
||||
editor.putInt(PROPERTY_APP_VERSION, appVersion);
|
||||
long expirationTime = System.currentTimeMillis()
|
||||
+ REGISTRATION_EXPIRY_TIME_MS;
|
||||
editor.putLong(PROPERTY_ON_SERVER_EXPIRATION_TIME, expirationTime);
|
||||
editor.commit();
|
||||
}
|
||||
|
||||
public void submitGcmBroadcast(String message) {
|
||||
CloudMessage mCloudMessage = new CloudMessage();
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
|
||||
mCloudMessage.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR,
|
||||
message);
|
||||
} else {
|
||||
mCloudMessage.execute(message);
|
||||
}
|
||||
}
|
||||
|
||||
public class CloudMessage extends AsyncTask<String, Integer, Object> {
|
||||
|
||||
@Override
|
||||
protected Object doInBackground(String... params) {
|
||||
try {
|
||||
ArrayList<NameValuePair> mPairs = new ArrayList<NameValuePair>();
|
||||
boolean hasIdentitiy = false;
|
||||
String identity = mPrefs.getString(PREF_IDENTITY, "?");
|
||||
if (!identity.equals("?")) {
|
||||
mPairs.add(new BasicNameValuePair("sender", "reicast tester #"
|
||||
+ identity));
|
||||
hasIdentitiy = true;
|
||||
} else {
|
||||
String ip = getSenderIPAddress(true);
|
||||
if (ip != null && !ip.equals(null)) {
|
||||
mPairs.add(new BasicNameValuePair("sender", ip));
|
||||
hasIdentitiy = true;
|
||||
}
|
||||
}
|
||||
if (hasIdentitiy) {
|
||||
mPairs.add(new BasicNameValuePair("message", params[0]));
|
||||
HttpClient client = new DefaultHttpClient();
|
||||
HttpPost post = new HttpPost(cloudUrl);
|
||||
post.setEntity(new UrlEncodedFormEntity(mPairs));
|
||||
return client.execute(post, new ResponseHandler<Object>() {
|
||||
|
||||
@Override
|
||||
public Object handleResponse(HttpResponse response)
|
||||
throws ClientProtocolException, IOException {
|
||||
return response.toString();
|
||||
}
|
||||
});
|
||||
}
|
||||
} catch (MalformedURLException e) {
|
||||
Log.d(APP_TAG, "MalformedURLException: " + e);
|
||||
} catch (IOException e) {
|
||||
Log.d(APP_TAG, "IOException: " + e);
|
||||
} catch (Exception e) {
|
||||
Log.d(APP_TAG, "Exception: " + e);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPostExecute(Object message) {
|
||||
if (message != null && !message.equals(null)) {
|
||||
Log.d(APP_TAG, "GCM: " + message.toString());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public void submitGcmUser(String regid) {
|
||||
CloudSelecao mCloudSelecao = new CloudSelecao();
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
|
||||
mCloudSelecao.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR,
|
||||
regid);
|
||||
} else {
|
||||
mCloudSelecao.execute(regid);
|
||||
}
|
||||
}
|
||||
|
||||
public class CloudSelecao extends AsyncTask<String, Integer, Object> {
|
||||
|
||||
@Override
|
||||
protected Object doInBackground(String... params) {
|
||||
try {
|
||||
ArrayList<NameValuePair> mPairs = new ArrayList<NameValuePair>();
|
||||
mPairs.add(new BasicNameValuePair("gcm_regid", params[0]));
|
||||
HttpClient client = new DefaultHttpClient();
|
||||
HttpPost post = new HttpPost(numberUrl);
|
||||
post.setEntity(new UrlEncodedFormEntity(mPairs));
|
||||
return client.execute(post, new ResponseHandler<Object>() {
|
||||
|
||||
@Override
|
||||
public Object handleResponse(HttpResponse response)
|
||||
throws ClientProtocolException, IOException {
|
||||
HttpEntity resEntitiy = response.getEntity();
|
||||
if (resEntitiy.getContentLength() > 0) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
try {
|
||||
BufferedReader reader = new BufferedReader(
|
||||
new InputStreamReader(resEntitiy
|
||||
.getContent()), 65728);
|
||||
String line = null;
|
||||
|
||||
while ((line = reader.readLine()) != null) {
|
||||
sb.append(line);
|
||||
}
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
return null;
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
return null;
|
||||
}
|
||||
return sb.toString();
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
});
|
||||
} catch (MalformedURLException e) {
|
||||
Log.d(APP_TAG, "MalformedURLException: " + e);
|
||||
} catch (IOException e) {
|
||||
Log.d(APP_TAG, "IOException: " + e);
|
||||
} catch (Exception e) {
|
||||
Log.d(APP_TAG, "Exception: " + e);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPostExecute(Object response) {
|
||||
if (response != null && !response.equals(null)) {
|
||||
String identity = response.toString();
|
||||
if (!identity.equals("Err")) {
|
||||
mPrefs.edit().putString(PREF_IDENTITY, identity).commit();
|
||||
}
|
||||
Log.d(APP_TAG, "reicast tester #" + identity);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public String getAccountEmail() {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ECLAIR) {
|
||||
Pattern emailPattern = Patterns.EMAIL_ADDRESS; // API level 8+
|
||||
Account[] accounts = AccountManager.get(mContext).getAccounts();
|
||||
for (Account account : accounts) {
|
||||
if (emailPattern.matcher(account.name).matches()) {
|
||||
if (account.name.toLowerCase(Locale.getDefault()).endsWith(
|
||||
"@gmail.com")) {
|
||||
return account.name;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Obtain the current device IP address from mobile interface
|
||||
*
|
||||
* @param useIPv4
|
||||
* Specify the infrastructure to return from
|
||||
*
|
||||
* @return String The IP address associated with this device
|
||||
*/
|
||||
public static String getSenderIPAddress(boolean useIPv4) {
|
||||
try {
|
||||
List<NetworkInterface> interfaces = Collections
|
||||
.list(NetworkInterface.getNetworkInterfaces());
|
||||
for (NetworkInterface intf : interfaces) {
|
||||
List<InetAddress> addrs = Collections.list(intf
|
||||
.getInetAddresses());
|
||||
for (InetAddress addr : addrs) {
|
||||
if (!addr.isLoopbackAddress()) {
|
||||
String sAddr = addr.getHostAddress().toUpperCase(
|
||||
Locale.getDefault());
|
||||
boolean isIPv4 = InetAddressUtils.isIPv4Address(sAddr);
|
||||
if (useIPv4) {
|
||||
if (isIPv4)
|
||||
return sAddr;
|
||||
} else {
|
||||
if (!isIPv4) {
|
||||
int delim = sAddr.indexOf('%');
|
||||
return delim < 0 ? sAddr : sAddr.substring(0,
|
||||
delim);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (Exception ex) {
|
||||
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPause() {
|
||||
super.onPause();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDestroy() {
|
||||
super.onDestroy();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onResume() {
|
||||
super.onResume();
|
||||
}
|
||||
}
|
|
@ -1,28 +0,0 @@
|
|||
package com.reicast.emulator.debug;
|
||||
|
||||
|
||||
import android.app.Activity;
|
||||
import android.os.Bundle;
|
||||
|
||||
public class Debugger extends Activity {
|
||||
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPause() {
|
||||
super.onPause();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDestroy() {
|
||||
super.onDestroy();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onResume() {
|
||||
super.onResume();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,33 @@
|
|||
package com.reicast.emulator.debug;
|
||||
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.view.ViewGroup.LayoutParams;
|
||||
import android.widget.ListAdapter;
|
||||
import android.widget.ListView;
|
||||
|
||||
public class ExpandableListView {
|
||||
|
||||
public void setListViewHeightBasedOnChildren(ListView listView) {
|
||||
ListAdapter listAdapter = listView.getAdapter();
|
||||
if (listAdapter == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
int totalHeight = listView.getPaddingTop()
|
||||
+ listView.getPaddingBottom();
|
||||
for (int i = 0; i < listAdapter.getCount(); i++) {
|
||||
View listItem = listAdapter.getView(i, null, listView);
|
||||
if (listItem instanceof ViewGroup)
|
||||
listItem.setLayoutParams(new LayoutParams(
|
||||
LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT));
|
||||
listItem.measure(0, 0);
|
||||
totalHeight += listItem.getMeasuredHeight();
|
||||
}
|
||||
|
||||
ViewGroup.LayoutParams params = listView.getLayoutParams();
|
||||
params.height = totalHeight
|
||||
+ (listView.getDividerHeight() * (listAdapter.getCount() - 1));
|
||||
listView.setLayoutParams(params);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,89 @@
|
|||
package com.reicast.emulator.debug;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.app.Notification;
|
||||
import android.app.NotificationManager;
|
||||
import android.app.PendingIntent;
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.SharedPreferences;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.preference.PreferenceManager;
|
||||
import android.support.v4.app.NotificationCompat;
|
||||
import android.util.Log;
|
||||
|
||||
import com.google.android.gms.gcm.GoogleCloudMessaging;
|
||||
|
||||
public class GcmBroadcastReceiver extends BroadcastReceiver {
|
||||
static final String TAG = "GCM::Service";
|
||||
Context mContext;
|
||||
|
||||
@Override
|
||||
public void onReceive(Context context, Intent intent) {
|
||||
GoogleCloudMessaging gcm = GoogleCloudMessaging.getInstance(context);
|
||||
mContext = context.getApplicationContext();
|
||||
String messageType = gcm.getMessageType(intent);
|
||||
if (GoogleCloudMessaging.MESSAGE_TYPE_SEND_ERROR.equals(messageType)) {
|
||||
// sendNotification(intent.getExtras().toString());
|
||||
} else if (GoogleCloudMessaging.MESSAGE_TYPE_DELETED
|
||||
.equals(messageType)) {
|
||||
// sendNotification(intent.getExtras().toString());
|
||||
} else {
|
||||
Bundle extras = intent.getExtras();
|
||||
if (extras.getString("message") != null) {
|
||||
String sender = extras.getString("sender");
|
||||
Log.d(TAG, "Sender: " + sender);
|
||||
String message = extras.getString("message");
|
||||
if (!message.equals("")) {
|
||||
if (sender.contains("reicast tester #")) {
|
||||
sendNotification(sender, message);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
setResultCode(Activity.RESULT_OK);
|
||||
}
|
||||
|
||||
// Put the GCM message into a notification and post it.
|
||||
private void sendNotification(String sender, String message) {
|
||||
SharedPreferences prefs = PreferenceManager
|
||||
.getDefaultSharedPreferences(mContext);
|
||||
final String number = sender.replace("reicast tester #", "");
|
||||
long timestamp = System.currentTimeMillis();
|
||||
int reference = (int) timestamp;
|
||||
if (prefs.getBoolean("enable_messaging", true)) {
|
||||
NotificationManager notificationManager = (NotificationManager) mContext
|
||||
.getSystemService(Context.NOTIFICATION_SERVICE);
|
||||
String title = "New reicast tester message!";
|
||||
Notification notification;
|
||||
Intent notificationIntent = new Intent(mContext, Debug.class);
|
||||
// notificationIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||
notificationIntent.setAction("reicast.emulator.NOTIFY");
|
||||
notificationIntent.putExtra("sender", sender);
|
||||
notificationIntent.putExtra("message", message);
|
||||
notificationIntent.putExtra("context", "GCM");
|
||||
PendingIntent intent = PendingIntent.getActivity(mContext, 0,
|
||||
notificationIntent, PendingIntent.FLAG_ONE_SHOT);
|
||||
NotificationCompat.Builder builder = new NotificationCompat.Builder(
|
||||
mContext);
|
||||
builder.setTicker(sender).setWhen(timestamp).setContentTitle(title)
|
||||
.setContentText(message).setAutoCancel(true)
|
||||
.setContentIntent(intent)
|
||||
.setSmallIcon(R.drawable.ic_launcher)
|
||||
.setLights(0xff00ff00, 200, 800)
|
||||
.setPriority(Notification.PRIORITY_HIGH);
|
||||
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
|
||||
notification = new NotificationCompat.BigTextStyle(builder)
|
||||
.bigText(message).build();
|
||||
} else {
|
||||
notification = builder.build();
|
||||
}
|
||||
notification.flags |= Notification.FLAG_AUTO_CANCEL;
|
||||
|
||||
notificationManager.notify(reference, notification);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,124 @@
|
|||
/* ====================================================================
|
||||
* Copyright (c) 2012-2013 Lounge Katt Entertainment. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in
|
||||
* the documentation and/or other materials provided with the
|
||||
* distribution.
|
||||
*
|
||||
* 3. All advertising materials mentioning features or use of this
|
||||
* software must display the following acknowledgment:
|
||||
* "This product includes software developed by Lounge Katt for use
|
||||
* by authorized development projects. (http://loungekatt.com/)"
|
||||
*
|
||||
* 4. The names "Lounge Katt", "TwistedUmbrella", and "StarKissed"
|
||||
* must not be used to endorse or promote products derived from this
|
||||
* software without prior written permission. For written permission,
|
||||
* please contact admin@loungekatt.com.
|
||||
*
|
||||
* 5. Products derived from this software may not be called "Lounge Katt"
|
||||
* nor may "Lounge Katt" appear in their names without prior written
|
||||
* permission of Lounge Katt Entertainment.
|
||||
*
|
||||
* 6. Redistributions of any form whatsoever must retain the following
|
||||
* acknowledgment:
|
||||
* "This product includes software developed by Lounge Katt for use
|
||||
* by authorized development projects. (http://loungekatt.com/)"
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY Lounge Katt ``AS IS'' AND ANY
|
||||
* EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE OpenSSL PROJECT OR
|
||||
* ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||||
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
||||
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
|
||||
* OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
* ====================================================================
|
||||
*
|
||||
* The license and distribution terms for any publicly available version or
|
||||
* derivative of this code cannot be changed. i.e. this code cannot simply be
|
||||
* copied and put under another distribution license
|
||||
* [including the GNU Public License.] Content not subject to these terms is
|
||||
* subject to to the terms and conditions of the Apache License, Version 2.0.
|
||||
*/
|
||||
|
||||
package com.reicast.emulator.debug;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.MalformedURLException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.apache.http.client.HttpClient;
|
||||
import org.apache.http.client.ResponseHandler;
|
||||
import org.apache.http.client.methods.HttpPost;
|
||||
import org.apache.http.impl.client.BasicResponseHandler;
|
||||
import org.apache.http.impl.client.DefaultHttpClient;
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONObject;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.os.AsyncTask;
|
||||
import android.os.Build;
|
||||
import android.os.StrictMode;
|
||||
import android.util.Log;
|
||||
|
||||
public class RequestArchive extends AsyncTask<String, String, List<String[]>> {
|
||||
|
||||
@SuppressLint("NewApi")
|
||||
protected void onPreExecute() {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.GINGERBREAD) {
|
||||
StrictMode.ThreadPolicy policy = new StrictMode.ThreadPolicy.Builder()
|
||||
.permitAll().build();
|
||||
StrictMode.setThreadPolicy(policy);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<String[]> doInBackground(String... urls) {
|
||||
try {
|
||||
List<String[]> messages = new ArrayList<String[]>();
|
||||
HttpClient client = new DefaultHttpClient();
|
||||
HttpPost post = new HttpPost(urls[0]);
|
||||
ResponseHandler<String> responseHandler = new BasicResponseHandler();
|
||||
String response = client.execute(post, responseHandler);
|
||||
if (response.contains("{") && response.contains("}")) {
|
||||
JSONObject archive = new JSONObject(response);
|
||||
JSONArray items = archive.getJSONArray("archive");
|
||||
for (int i = 0; i < items.length(); i++) {
|
||||
JSONObject log = items.getJSONObject(i);
|
||||
String id = log.getString("identifier");
|
||||
String msg = log.getString("message");
|
||||
String date = log.getString("created_at");
|
||||
messages.add(new String[] { id, msg, date });
|
||||
}
|
||||
return messages;
|
||||
}
|
||||
return null;
|
||||
|
||||
} catch (MalformedURLException e) {
|
||||
Log.d("reicast-debug", "MalformedURLException: " + e);
|
||||
} catch (IOException e) {
|
||||
Log.d("reicast-debug", "IOException: " + e);
|
||||
} catch (Exception e) {
|
||||
Log.d("reicast-debug", "Exception: " + e);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPostExecute(List<String[]> jsonArray) {
|
||||
super.onPostExecute(jsonArray);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,61 @@
|
|||
/*
|
||||
* Copyright (C) 2011 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package de.ankri.text.method;
|
||||
|
||||
import java.util.Locale;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.Rect;
|
||||
import android.util.Log;
|
||||
import android.view.View;
|
||||
|
||||
/**
|
||||
* Transforms source text into an ALL CAPS string, locale-aware.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
public class AllCapsTransformationMethod implements TransformationMethodCompat2
|
||||
{
|
||||
private static final String TAG = "AllCapsTransformationMethod";
|
||||
|
||||
private boolean mEnabled;
|
||||
private Locale mLocale;
|
||||
|
||||
public AllCapsTransformationMethod(Context context)
|
||||
{
|
||||
mLocale = context.getResources().getConfiguration().locale;
|
||||
}
|
||||
|
||||
public CharSequence getTransformation(CharSequence source, View view)
|
||||
{
|
||||
if (mEnabled)
|
||||
{
|
||||
return source != null ? source.toString().toUpperCase(mLocale) : null;
|
||||
}
|
||||
Log.w(TAG, "Caller did not enable length changes; not transforming text");
|
||||
return source;
|
||||
}
|
||||
|
||||
public void onFocusChanged(View view, CharSequence sourceText, boolean focused, int direction, Rect previouslyFocusedRect)
|
||||
{
|
||||
}
|
||||
|
||||
public void setLengthChangesAllowed(boolean allowLengthChanges)
|
||||
{
|
||||
mEnabled = allowLengthChanges;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,37 @@
|
|||
/*
|
||||
* Copyright (C) 2006 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package de.ankri.text.method;
|
||||
|
||||
import android.graphics.Rect;
|
||||
import android.view.View;
|
||||
|
||||
/**
|
||||
* TextView uses TransformationMethods to do things like replacing the characters of passwords with dots, or keeping the newline characters from causing line breaks in single-line text fields.
|
||||
*/
|
||||
public interface TransformationMethodCompat
|
||||
{
|
||||
/**
|
||||
* Returns a CharSequence that is a transformation of the source text -- for example, replacing each character with a dot in a password field. Beware that the returned text must be exactly the
|
||||
* same length as the source text, and that if the source text is Editable, the returned text must mirror it dynamically instead of doing a one-time copy.
|
||||
*/
|
||||
public CharSequence getTransformation(CharSequence source, View view);
|
||||
|
||||
/**
|
||||
* This method is called when the TextView that uses this TransformationMethod gains or loses focus.
|
||||
*/
|
||||
public void onFocusChanged(View view, CharSequence sourceText, boolean focused, int direction, Rect previouslyFocusedRect);
|
||||
}
|
|
@ -0,0 +1,33 @@
|
|||
/*
|
||||
* Copyright (C) 2011 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package de.ankri.text.method;
|
||||
|
||||
/**
|
||||
* TransformationMethod2 extends the TransformationMethod interface
|
||||
* and adds the ability to relax restrictions of TransformationMethod.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
public interface TransformationMethodCompat2 extends TransformationMethodCompat {
|
||||
/**
|
||||
* Relax the contract of TransformationMethod to allow length changes,
|
||||
* or revert to the length-restricted behavior.
|
||||
*
|
||||
* @param allowLengthChanges true to allow the transformation to change the length
|
||||
* of the input string.
|
||||
*/
|
||||
public void setLengthChangesAllowed(boolean allowLengthChanges);
|
||||
}
|
|
@ -0,0 +1,890 @@
|
|||
package de.ankri.views;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.content.Context;
|
||||
import android.content.res.ColorStateList;
|
||||
import android.content.res.Resources;
|
||||
import android.content.res.TypedArray;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Paint;
|
||||
import android.graphics.Rect;
|
||||
import android.graphics.Typeface;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.text.Layout;
|
||||
import android.text.StaticLayout;
|
||||
import android.text.TextPaint;
|
||||
import android.text.TextUtils;
|
||||
import android.util.AttributeSet;
|
||||
import android.util.FloatMath;
|
||||
import android.view.Gravity;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.VelocityTracker;
|
||||
import android.view.View;
|
||||
import android.view.ViewConfiguration;
|
||||
import android.widget.CompoundButton;
|
||||
|
||||
import com.reicast.emulator.debug.R;
|
||||
|
||||
import de.ankri.text.method.AllCapsTransformationMethod;
|
||||
import de.ankri.text.method.TransformationMethodCompat2;
|
||||
|
||||
/**
|
||||
* A Switch is a two-state toggle switch widget that can select between two options. The user may drag the "thumb" back and forth to choose the selected option, or simply tap to toggle as if it were a
|
||||
* checkbox. The {@link #setText(CharSequence) text} property controls the text displayed in the label for the switch, whereas the {@link #setTextOff(CharSequence) off} and
|
||||
* {@link #setTextOn(CharSequence) on} text controls the text on the thumb. Similarly, the {@link #setTextAppearance(android.content.Context, int) textAppearance} and the related setTypeface() methods
|
||||
* control the typeface and style of label text, whereas the {@link #setSwitchTextAppearance(android.content.Context, int) switchTextAppearance} and the related seSwitchTypeface() methods control that
|
||||
* of the thumb. <br>
|
||||
*
|
||||
* <p>
|
||||
* See the <a href="http://developer.android.com/guide/topics/ui/controls/togglebutton.html">Toggle Buttons</a> guide.
|
||||
* </p>
|
||||
*
|
||||
* @attr ref android.R.styleable#Switch_textOn
|
||||
* @attr ref android.R.styleable#Switch_textOff
|
||||
* @attr ref android.R.styleable#Switch_switchMinWidth
|
||||
* @attr ref android.R.styleable#Switch_switchPadding
|
||||
* @attr ref android.R.styleable#Switch_switchTextAppearance
|
||||
* @attr ref android.R.styleable#Switch_thumb
|
||||
* @attr ref android.R.styleable#Switch_thumbTextPadding
|
||||
* @attr ref android.R.styleable#Switch_track
|
||||
*/
|
||||
public class Switch extends CompoundButton
|
||||
{
|
||||
private static final int TOUCH_MODE_IDLE = 0;
|
||||
private static final int TOUCH_MODE_DOWN = 1;
|
||||
private static final int TOUCH_MODE_DRAGGING = 2;
|
||||
|
||||
// Enum for the "typeface" XML parameter.
|
||||
private static final int SANS = 1;
|
||||
private static final int SERIF = 2;
|
||||
private static final int MONOSPACE = 3;
|
||||
|
||||
private Drawable mThumbDrawable;
|
||||
private Drawable mTrackDrawable;
|
||||
private int mThumbTextPadding;
|
||||
private int mSwitchMinWidth;
|
||||
private int mSwitchPadding;
|
||||
private CharSequence mTextOn;
|
||||
private CharSequence mTextOff;
|
||||
|
||||
private int mTouchMode;
|
||||
private int mTouchSlop;
|
||||
private float mTouchX;
|
||||
private float mTouchY;
|
||||
@SuppressLint("Recycle")
|
||||
private VelocityTracker mVelocityTracker = VelocityTracker.obtain();
|
||||
private int mMinFlingVelocity;
|
||||
|
||||
private float mThumbPosition;
|
||||
private int mSwitchWidth;
|
||||
private int mSwitchHeight;
|
||||
private int mThumbWidth; // Does not include padding
|
||||
|
||||
private int mSwitchLeft;
|
||||
private int mSwitchTop;
|
||||
private int mSwitchRight;
|
||||
private int mSwitchBottom;
|
||||
|
||||
private TextPaint mTextPaint;
|
||||
private ColorStateList mTextColors;
|
||||
private Layout mOnLayout;
|
||||
private Layout mOffLayout;
|
||||
|
||||
private TransformationMethodCompat2 mSwitchTransformationMethod;
|
||||
|
||||
private final Rect mTempRect = new Rect();
|
||||
|
||||
private static final int[] CHECKED_STATE_SET = { android.R.attr.state_checked };
|
||||
|
||||
/**
|
||||
* Construct a new Switch with default styling.
|
||||
*
|
||||
* @param context
|
||||
* The Context that will determine this widget's theming.
|
||||
*/
|
||||
public Switch(Context context)
|
||||
{
|
||||
this(context, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct a new Switch with default styling, overriding specific style attributes as requested.
|
||||
*
|
||||
* @param context
|
||||
* The Context that will determine this widget's theming.
|
||||
* @param attrs
|
||||
* Specification of attributes that should deviate from default styling.
|
||||
*/
|
||||
public Switch(Context context, AttributeSet attrs)
|
||||
{
|
||||
this(context, attrs, R.attr.switchStyle);
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct a new Switch with a default style determined by the given theme attribute, overriding specific style attributes as requested.
|
||||
*
|
||||
* @param context
|
||||
* The Context that will determine this widget's theming.
|
||||
* @param attrs
|
||||
* Specification of attributes that should deviate from the default styling.
|
||||
* @param defStyle
|
||||
* An attribute ID within the active theme containing a reference to the default style for this widget. e.g. android.R.attr.switchStyle.
|
||||
*/
|
||||
public Switch(Context context, AttributeSet attrs, int defStyle)
|
||||
{
|
||||
super(context, attrs, defStyle);
|
||||
|
||||
mTextPaint = new TextPaint(Paint.ANTI_ALIAS_FLAG);
|
||||
Resources res = getResources();
|
||||
mTextPaint.density = res.getDisplayMetrics().density;
|
||||
|
||||
// TODO resolve error
|
||||
// mTextPaint.setCompatibilityScaling(res.getCompatibilityInfo().applicationScale);
|
||||
|
||||
final TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.Switch, defStyle, 0);
|
||||
|
||||
mThumbDrawable = a.getDrawable(R.styleable.Switch_thumb);
|
||||
mTrackDrawable = a.getDrawable(R.styleable.Switch_track);
|
||||
mTextOn = a.getText(R.styleable.Switch_textOn);
|
||||
mTextOff = a.getText(R.styleable.Switch_textOff);
|
||||
mThumbTextPadding = a.getDimensionPixelSize(R.styleable.Switch_thumbTextPadding, 0);
|
||||
mSwitchMinWidth = a.getDimensionPixelSize(R.styleable.Switch_switchMinWidth, 0);
|
||||
mSwitchPadding = a.getDimensionPixelSize(R.styleable.Switch_switchPadding, 0);
|
||||
|
||||
int appearance = a.getResourceId(R.styleable.Switch_switchTextAppearance, 0);
|
||||
if (appearance != 0)
|
||||
{
|
||||
setSwitchTextAppearance(context, appearance);
|
||||
}
|
||||
a.recycle();
|
||||
|
||||
ViewConfiguration config = ViewConfiguration.get(context);
|
||||
mTouchSlop = config.getScaledTouchSlop();
|
||||
mMinFlingVelocity = config.getScaledMinimumFlingVelocity();
|
||||
|
||||
// Refresh display with current params
|
||||
refreshDrawableState();
|
||||
setChecked(isChecked());
|
||||
|
||||
// TODO I don't know why, but in order for the OnCheckedChangeListener to work this switch has to have an OnClickListener
|
||||
this.setOnClickListener(new View.OnClickListener()
|
||||
{
|
||||
|
||||
public void onClick(View v)
|
||||
{
|
||||
// do nothing
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the switch text color, size, style, hint color, and highlight color from the specified TextAppearance resource.
|
||||
*
|
||||
* @attr ref android.R.styleable#Switch_switchTextAppearance
|
||||
*/
|
||||
public void setSwitchTextAppearance(Context context, int resid)
|
||||
{
|
||||
TypedArray appearance = context.obtainStyledAttributes(resid, R.styleable.TextAppearanceSwitch);
|
||||
|
||||
ColorStateList colors;
|
||||
int ts;
|
||||
|
||||
colors = appearance.getColorStateList(R.styleable.TextAppearanceSwitch_textColor);
|
||||
if (colors != null)
|
||||
{
|
||||
mTextColors = colors;
|
||||
}
|
||||
else
|
||||
{
|
||||
// If no color set in TextAppearance, default to the view's textColor
|
||||
mTextColors = getTextColors();
|
||||
}
|
||||
|
||||
ts = appearance.getDimensionPixelSize(R.styleable.TextAppearanceSwitch_textSize, 0);
|
||||
if (ts != 0)
|
||||
{
|
||||
if (ts != mTextPaint.getTextSize())
|
||||
{
|
||||
mTextPaint.setTextSize(ts);
|
||||
requestLayout();
|
||||
}
|
||||
}
|
||||
|
||||
int typefaceIndex, styleIndex;
|
||||
|
||||
typefaceIndex = appearance.getInt(R.styleable.TextAppearanceSwitch_typeface, -1);
|
||||
styleIndex = appearance.getInt(R.styleable.TextAppearanceSwitch_textStyle, -1);
|
||||
|
||||
setSwitchTypefaceByIndex(typefaceIndex, styleIndex);
|
||||
|
||||
boolean allCaps = appearance.getBoolean(R.styleable.TextAppearanceSwitch_textAllCaps, false);
|
||||
if (allCaps)
|
||||
{
|
||||
mSwitchTransformationMethod = new AllCapsTransformationMethod(getContext());
|
||||
mSwitchTransformationMethod.setLengthChangesAllowed(true);
|
||||
}
|
||||
else
|
||||
{
|
||||
mSwitchTransformationMethod = null;
|
||||
}
|
||||
|
||||
appearance.recycle();
|
||||
}
|
||||
|
||||
private void setSwitchTypefaceByIndex(int typefaceIndex, int styleIndex)
|
||||
{
|
||||
Typeface tf = null;
|
||||
switch (typefaceIndex)
|
||||
{
|
||||
case SANS:
|
||||
tf = Typeface.SANS_SERIF;
|
||||
break;
|
||||
|
||||
case SERIF:
|
||||
tf = Typeface.SERIF;
|
||||
break;
|
||||
|
||||
case MONOSPACE:
|
||||
tf = Typeface.MONOSPACE;
|
||||
break;
|
||||
}
|
||||
|
||||
setSwitchTypeface(tf, styleIndex);
|
||||
}
|
||||
|
||||
public void setSwitchTypeface(Typeface tf, int style)
|
||||
{
|
||||
if (style > 0)
|
||||
{
|
||||
if (tf == null)
|
||||
{
|
||||
tf = Typeface.defaultFromStyle(style);
|
||||
}
|
||||
else
|
||||
{
|
||||
tf = Typeface.create(tf, style);
|
||||
}
|
||||
|
||||
setSwitchTypeface(tf);
|
||||
// now compute what (if any) algorithmic styling is needed
|
||||
int typefaceStyle = tf != null ? tf.getStyle() : 0;
|
||||
int need = style & ~typefaceStyle;
|
||||
mTextPaint.setFakeBoldText((need & Typeface.BOLD) != 0);
|
||||
mTextPaint.setTextSkewX((need & Typeface.ITALIC) != 0 ? -0.25f : 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
mTextPaint.setFakeBoldText(false);
|
||||
mTextPaint.setTextSkewX(0);
|
||||
setSwitchTypeface(tf);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the typeface in which the text should be displayed on the switch. Note that not all Typeface families actually have bold and italic variants, so you may need to use
|
||||
* {@link #setSwitchTypeface(Typeface, int)} to get the appearance that you actually want.
|
||||
*
|
||||
* @attr ref android.R.styleable#TextView_typeface
|
||||
* @attr ref android.R.styleable#TextView_textStyle
|
||||
*/
|
||||
public void setSwitchTypeface(Typeface tf)
|
||||
{
|
||||
if (mTextPaint.getTypeface() != tf)
|
||||
{
|
||||
mTextPaint.setTypeface(tf);
|
||||
|
||||
requestLayout();
|
||||
invalidate();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the amount of horizontal padding between the switch and the associated text.
|
||||
*
|
||||
* @param pixels
|
||||
* Amount of padding in pixels
|
||||
*
|
||||
* @attr ref android.R.styleable#Switch_switchPadding
|
||||
*/
|
||||
public void setSwitchPadding(int pixels)
|
||||
{
|
||||
mSwitchPadding = pixels;
|
||||
requestLayout();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the amount of horizontal padding between the switch and the associated text.
|
||||
*
|
||||
* @return Amount of padding in pixels
|
||||
*
|
||||
* @attr ref android.R.styleable#Switch_switchPadding
|
||||
*/
|
||||
public int getSwitchPadding()
|
||||
{
|
||||
return mSwitchPadding;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the minimum width of the switch in pixels. The switch's width will be the maximum of this value and its measured width as determined by the switch drawables and text used.
|
||||
*
|
||||
* @param pixels
|
||||
* Minimum width of the switch in pixels
|
||||
*
|
||||
* @attr ref android.R.styleable#Switch_switchMinWidth
|
||||
*/
|
||||
public void setSwitchMinWidth(int pixels)
|
||||
{
|
||||
mSwitchMinWidth = pixels;
|
||||
requestLayout();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the minimum width of the switch in pixels. The switch's width will be the maximum of this value and its measured width as determined by the switch drawables and text used.
|
||||
*
|
||||
* @return Minimum width of the switch in pixels
|
||||
*
|
||||
* @attr ref android.R.styleable#Switch_switchMinWidth
|
||||
*/
|
||||
public int getSwitchMinWidth()
|
||||
{
|
||||
return mSwitchMinWidth;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the horizontal padding around the text drawn on the switch itself.
|
||||
*
|
||||
* @param pixels
|
||||
* Horizontal padding for switch thumb text in pixels
|
||||
*
|
||||
* @attr ref android.R.styleable#Switch_thumbTextPadding
|
||||
*/
|
||||
public void setThumbTextPadding(int pixels)
|
||||
{
|
||||
mThumbTextPadding = pixels;
|
||||
requestLayout();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the horizontal padding around the text drawn on the switch itself.
|
||||
*
|
||||
* @return Horizontal padding for switch thumb text in pixels
|
||||
*
|
||||
* @attr ref android.R.styleable#Switch_thumbTextPadding
|
||||
*/
|
||||
public int getThumbTextPadding()
|
||||
{
|
||||
return mThumbTextPadding;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the drawable used for the track that the switch slides within.
|
||||
*
|
||||
* @param track
|
||||
* Track drawable
|
||||
*
|
||||
* @attr ref android.R.styleable#Switch_track
|
||||
*/
|
||||
public void setTrackDrawable(Drawable track)
|
||||
{
|
||||
mTrackDrawable = track;
|
||||
requestLayout();
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the drawable used for the track that the switch slides within.
|
||||
*
|
||||
* @param resId
|
||||
* Resource ID of a track drawable
|
||||
*
|
||||
* @attr ref android.R.styleable#Switch_track
|
||||
*/
|
||||
public void setTrackResource(int resId)
|
||||
{
|
||||
setTrackDrawable(getContext().getResources().getDrawable(resId));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the drawable used for the track that the switch slides within.
|
||||
*
|
||||
* @return Track drawable
|
||||
*
|
||||
* @attr ref android.R.styleable#Switch_track
|
||||
*/
|
||||
public Drawable getTrackDrawable()
|
||||
{
|
||||
return mTrackDrawable;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the drawable used for the switch "thumb" - the piece that the user can physically touch and drag along the track.
|
||||
*
|
||||
* @param thumb
|
||||
* Thumb drawable
|
||||
*
|
||||
* @attr ref android.R.styleable#Switch_thumb
|
||||
*/
|
||||
public void setThumbDrawable(Drawable thumb)
|
||||
{
|
||||
mThumbDrawable = thumb;
|
||||
requestLayout();
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the drawable used for the switch "thumb" - the piece that the user can physically touch and drag along the track.
|
||||
*
|
||||
* @param resId
|
||||
* Resource ID of a thumb drawable
|
||||
*
|
||||
* @attr ref android.R.styleable#Switch_thumb
|
||||
*/
|
||||
public void setThumbResource(int resId)
|
||||
{
|
||||
setThumbDrawable(getContext().getResources().getDrawable(resId));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the drawable used for the switch "thumb" - the piece that the user can physically touch and drag along the track.
|
||||
*
|
||||
* @return Thumb drawable
|
||||
*
|
||||
* @attr ref android.R.styleable#Switch_thumb
|
||||
*/
|
||||
public Drawable getThumbDrawable()
|
||||
{
|
||||
return mThumbDrawable;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the text displayed when the button is in the checked state.
|
||||
*
|
||||
* @attr ref android.R.styleable#Switch_textOn
|
||||
*/
|
||||
public CharSequence getTextOn()
|
||||
{
|
||||
return mTextOn;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the text displayed when the button is in the checked state.
|
||||
*
|
||||
* @attr ref android.R.styleable#Switch_textOn
|
||||
*/
|
||||
public void setTextOn(CharSequence textOn)
|
||||
{
|
||||
mTextOn = textOn;
|
||||
requestLayout();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the text displayed when the button is not in the checked state.
|
||||
*
|
||||
* @attr ref android.R.styleable#Switch_textOff
|
||||
*/
|
||||
public CharSequence getTextOff()
|
||||
{
|
||||
return mTextOff;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the text displayed when the button is not in the checked state.
|
||||
*
|
||||
* @attr ref android.R.styleable#Switch_textOff
|
||||
*/
|
||||
public void setTextOff(CharSequence textOff)
|
||||
{
|
||||
mTextOff = textOff;
|
||||
requestLayout();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onMeasure(int widthMeasureSpec, int heightMeasureSpec)
|
||||
{
|
||||
if (mOnLayout == null)
|
||||
{
|
||||
mOnLayout = makeLayout(mTextOn);
|
||||
}
|
||||
if (mOffLayout == null)
|
||||
{
|
||||
mOffLayout = makeLayout(mTextOff);
|
||||
}
|
||||
|
||||
mTrackDrawable.getPadding(mTempRect);
|
||||
final int maxTextWidth = Math.max(mOnLayout.getWidth(), mOffLayout.getWidth());
|
||||
final int switchWidth = Math.max(mSwitchMinWidth, maxTextWidth * 2 + mThumbTextPadding * 4 + mTempRect.left + mTempRect.right);
|
||||
final int switchHeight = mTrackDrawable.getIntrinsicHeight();
|
||||
|
||||
mThumbWidth = maxTextWidth + mThumbTextPadding * 2;
|
||||
|
||||
mSwitchWidth = switchWidth;
|
||||
mSwitchHeight = switchHeight;
|
||||
|
||||
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
|
||||
final int measuredHeight = getMeasuredHeight();
|
||||
if (measuredHeight < switchHeight)
|
||||
{
|
||||
setMeasuredDimension(getMeasuredWidth(), switchHeight);
|
||||
}
|
||||
}
|
||||
|
||||
// TODO add onPopulateAccessibilityEvent(AccessibilityEvent event)
|
||||
// @Override
|
||||
// public void onPopulateAccessibilityEvent(AccessibilityEvent event)
|
||||
// {
|
||||
// super.onPopulateAccessibilityEvent(event);
|
||||
// Layout layout = isChecked() ? mOnLayout : mOffLayout;
|
||||
// if (layout != null && !TextUtils.isEmpty(layout.getText()))
|
||||
// {
|
||||
// event.getText().add(layout.getText());
|
||||
// }
|
||||
// }
|
||||
|
||||
@SuppressLint("FloatMath")
|
||||
private Layout makeLayout(CharSequence text)
|
||||
{
|
||||
final CharSequence transformed = (mSwitchTransformationMethod != null) ? mSwitchTransformationMethod.getTransformation(text, this) : text;
|
||||
return new StaticLayout(transformed, mTextPaint, (int) FloatMath.ceil(Layout.getDesiredWidth(transformed, mTextPaint)), Layout.Alignment.ALIGN_NORMAL, 1.f, 0, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return true if (x, y) is within the target area of the switch thumb
|
||||
*/
|
||||
private boolean hitThumb(float x, float y)
|
||||
{
|
||||
mThumbDrawable.getPadding(mTempRect);
|
||||
final int thumbTop = mSwitchTop - mTouchSlop;
|
||||
final int thumbLeft = mSwitchLeft + (int) (mThumbPosition + 0.5f) - mTouchSlop;
|
||||
final int thumbRight = thumbLeft + mThumbWidth + mTempRect.left + mTempRect.right + mTouchSlop;
|
||||
final int thumbBottom = mSwitchBottom + mTouchSlop;
|
||||
return x > thumbLeft && x < thumbRight && y > thumbTop && y < thumbBottom;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onTouchEvent(MotionEvent ev)
|
||||
{
|
||||
mVelocityTracker.addMovement(ev);
|
||||
final int action = ev.getActionMasked();
|
||||
switch (action)
|
||||
{
|
||||
case MotionEvent.ACTION_DOWN:
|
||||
{
|
||||
final float x = ev.getX();
|
||||
final float y = ev.getY();
|
||||
if (isEnabled() && hitThumb(x, y))
|
||||
{
|
||||
mTouchMode = TOUCH_MODE_DOWN;
|
||||
mTouchX = x;
|
||||
mTouchY = y;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case MotionEvent.ACTION_MOVE:
|
||||
{
|
||||
switch (mTouchMode)
|
||||
{
|
||||
case TOUCH_MODE_IDLE:
|
||||
// Didn't target the thumb, treat normally.
|
||||
break;
|
||||
|
||||
case TOUCH_MODE_DOWN:
|
||||
{
|
||||
final float x = ev.getX();
|
||||
final float y = ev.getY();
|
||||
if (Math.abs(x - mTouchX) > mTouchSlop || Math.abs(y - mTouchY) > mTouchSlop)
|
||||
{
|
||||
mTouchMode = TOUCH_MODE_DRAGGING;
|
||||
getParent().requestDisallowInterceptTouchEvent(true);
|
||||
mTouchX = x;
|
||||
mTouchY = y;
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case TOUCH_MODE_DRAGGING:
|
||||
{
|
||||
final float x = ev.getX();
|
||||
final float dx = x - mTouchX;
|
||||
float newPos = Math.max(0, Math.min(mThumbPosition + dx, getThumbScrollRange()));
|
||||
if (newPos != mThumbPosition)
|
||||
{
|
||||
mThumbPosition = newPos;
|
||||
mTouchX = x;
|
||||
invalidate();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case MotionEvent.ACTION_UP:
|
||||
case MotionEvent.ACTION_CANCEL:
|
||||
{
|
||||
if (mTouchMode == TOUCH_MODE_DRAGGING)
|
||||
{
|
||||
stopDrag(ev);
|
||||
return true;
|
||||
}
|
||||
mTouchMode = TOUCH_MODE_IDLE;
|
||||
mVelocityTracker.clear();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return super.onTouchEvent(ev);
|
||||
}
|
||||
|
||||
private void cancelSuperTouch(MotionEvent ev)
|
||||
{
|
||||
MotionEvent cancel = MotionEvent.obtain(ev);
|
||||
cancel.setAction(MotionEvent.ACTION_CANCEL);
|
||||
super.onTouchEvent(cancel);
|
||||
cancel.recycle();
|
||||
}
|
||||
|
||||
/**
|
||||
* Called from onTouchEvent to end a drag operation.
|
||||
*
|
||||
* @param ev
|
||||
* Event that triggered the end of drag mode - ACTION_UP or ACTION_CANCEL
|
||||
*/
|
||||
private void stopDrag(MotionEvent ev)
|
||||
{
|
||||
mTouchMode = TOUCH_MODE_IDLE;
|
||||
// Up and not canceled, also checks the switch has not been disabled during the drag
|
||||
boolean commitChange = ev.getAction() == MotionEvent.ACTION_UP && isEnabled();
|
||||
|
||||
cancelSuperTouch(ev);
|
||||
|
||||
if (commitChange)
|
||||
{
|
||||
boolean newState;
|
||||
mVelocityTracker.computeCurrentVelocity(1000);
|
||||
float xvel = mVelocityTracker.getXVelocity();
|
||||
if (Math.abs(xvel) > mMinFlingVelocity)
|
||||
{
|
||||
// newState = isLayoutRtl() ? (xvel < 0) : (xvel > 0);
|
||||
newState = xvel > 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
newState = getTargetCheckedState();
|
||||
}
|
||||
animateThumbToCheckedState(newState);
|
||||
}
|
||||
else
|
||||
{
|
||||
animateThumbToCheckedState(isChecked());
|
||||
}
|
||||
}
|
||||
|
||||
private void animateThumbToCheckedState(boolean newCheckedState)
|
||||
{
|
||||
// TODO animate!
|
||||
// float targetPos = newCheckedState ? 0 : getThumbScrollRange();
|
||||
// mThumbPosition = targetPos;
|
||||
setChecked(newCheckedState);
|
||||
}
|
||||
|
||||
private boolean getTargetCheckedState()
|
||||
{
|
||||
return mThumbPosition >= getThumbScrollRange() / 2;
|
||||
}
|
||||
|
||||
private void setThumbPosition(boolean checked)
|
||||
{
|
||||
mThumbPosition = checked ? getThumbScrollRange() : 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setChecked(boolean checked)
|
||||
{
|
||||
super.setChecked(checked);
|
||||
setThumbPosition(isChecked());
|
||||
invalidate();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onLayout(boolean changed, int left, int top, int right, int bottom)
|
||||
{
|
||||
super.onLayout(changed, left, top, right, bottom);
|
||||
|
||||
setThumbPosition(isChecked());
|
||||
|
||||
int switchRight;
|
||||
int switchLeft;
|
||||
|
||||
switchRight = getWidth() - getPaddingRight();
|
||||
switchLeft = switchRight - mSwitchWidth;
|
||||
|
||||
int switchTop = 0;
|
||||
int switchBottom = 0;
|
||||
switch (getGravity() & Gravity.VERTICAL_GRAVITY_MASK)
|
||||
{
|
||||
default:
|
||||
case Gravity.TOP:
|
||||
switchTop = getPaddingTop();
|
||||
switchBottom = switchTop + mSwitchHeight;
|
||||
break;
|
||||
|
||||
case Gravity.CENTER_VERTICAL:
|
||||
switchTop = (getPaddingTop() + getHeight() - getPaddingBottom()) / 2 - mSwitchHeight / 2;
|
||||
switchBottom = switchTop + mSwitchHeight;
|
||||
break;
|
||||
|
||||
case Gravity.BOTTOM:
|
||||
switchBottom = getHeight() - getPaddingBottom();
|
||||
switchTop = switchBottom - mSwitchHeight;
|
||||
break;
|
||||
}
|
||||
|
||||
mSwitchLeft = switchLeft;
|
||||
mSwitchTop = switchTop;
|
||||
mSwitchBottom = switchBottom;
|
||||
mSwitchRight = switchRight;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDraw(Canvas canvas)
|
||||
{
|
||||
super.onDraw(canvas);
|
||||
|
||||
// Draw the switch
|
||||
int switchLeft = mSwitchLeft;
|
||||
int switchTop = mSwitchTop;
|
||||
int switchRight = mSwitchRight;
|
||||
int switchBottom = mSwitchBottom;
|
||||
|
||||
mTrackDrawable.setBounds(switchLeft, switchTop, switchRight, switchBottom);
|
||||
mTrackDrawable.draw(canvas);
|
||||
|
||||
canvas.save();
|
||||
|
||||
mTrackDrawable.getPadding(mTempRect);
|
||||
int switchInnerLeft = switchLeft + mTempRect.left;
|
||||
int switchInnerTop = switchTop + mTempRect.top;
|
||||
int switchInnerRight = switchRight - mTempRect.right;
|
||||
int switchInnerBottom = switchBottom - mTempRect.bottom;
|
||||
canvas.clipRect(switchInnerLeft, switchTop, switchInnerRight, switchBottom);
|
||||
|
||||
mThumbDrawable.getPadding(mTempRect);
|
||||
final int thumbPos = (int) (mThumbPosition + 0.5f);
|
||||
int thumbLeft = switchInnerLeft - mTempRect.left + thumbPos;
|
||||
int thumbRight = switchInnerLeft + thumbPos + mThumbWidth + mTempRect.right;
|
||||
|
||||
mThumbDrawable.setBounds(thumbLeft, switchTop, thumbRight, switchBottom);
|
||||
mThumbDrawable.draw(canvas);
|
||||
|
||||
// mTextColors should not be null, but just in case
|
||||
if (mTextColors != null)
|
||||
{
|
||||
mTextPaint.setColor(mTextColors.getColorForState(getDrawableState(), mTextColors.getDefaultColor()));
|
||||
}
|
||||
mTextPaint.drawableState = getDrawableState();
|
||||
|
||||
Layout switchText = getTargetCheckedState() ? mOnLayout : mOffLayout;
|
||||
if (switchText != null)
|
||||
{
|
||||
canvas.translate((thumbLeft + thumbRight) / 2 - switchText.getWidth() / 2, (switchInnerTop + switchInnerBottom) / 2 - switchText.getHeight() / 2);
|
||||
switchText.draw(canvas);
|
||||
}
|
||||
|
||||
canvas.restore();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getCompoundPaddingRight()
|
||||
{
|
||||
int padding = super.getCompoundPaddingRight() + mSwitchWidth;
|
||||
if (!TextUtils.isEmpty(getText()))
|
||||
{
|
||||
padding += mSwitchPadding;
|
||||
}
|
||||
return padding;
|
||||
}
|
||||
|
||||
private int getThumbScrollRange()
|
||||
{
|
||||
if (mTrackDrawable == null)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
mTrackDrawable.getPadding(mTempRect);
|
||||
return mSwitchWidth - mThumbWidth - mTempRect.left - mTempRect.right;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int[] onCreateDrawableState(int extraSpace)
|
||||
{
|
||||
final int[] drawableState = super.onCreateDrawableState(extraSpace + 1);
|
||||
if (isChecked())
|
||||
{
|
||||
mergeDrawableStates(drawableState, CHECKED_STATE_SET);
|
||||
}
|
||||
return drawableState;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void drawableStateChanged()
|
||||
{
|
||||
super.drawableStateChanged();
|
||||
|
||||
int[] myDrawableState = getDrawableState();
|
||||
|
||||
// Set the state of the Drawable
|
||||
// Drawable may be null when checked state is set from XML, from super constructor
|
||||
if (mThumbDrawable != null)
|
||||
mThumbDrawable.setState(myDrawableState);
|
||||
if (mTrackDrawable != null)
|
||||
mTrackDrawable.setState(myDrawableState);
|
||||
|
||||
invalidate();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean verifyDrawable(Drawable who)
|
||||
{
|
||||
return super.verifyDrawable(who) || who == mThumbDrawable || who == mTrackDrawable;
|
||||
}
|
||||
|
||||
// TODO check if even necessary?!
|
||||
// @Override
|
||||
// public void jumpDrawablesToCurrentState()
|
||||
// {
|
||||
// super.jumpDrawablesToCurrentState();
|
||||
// mThumbDrawable.jumpToCurrentState();
|
||||
// mTrackDrawable.jumpToCurrentState();
|
||||
// }
|
||||
|
||||
// TODO onInitializeAccessibilityEvent(AccessibilityEvent event)
|
||||
// @Override
|
||||
// public void onInitializeAccessibilityEvent(AccessibilityEvent event)
|
||||
// {
|
||||
// super.onInitializeAccessibilityEvent(event);
|
||||
// event.setClassName(MySwitch.class.getName());
|
||||
// }
|
||||
|
||||
// TODO onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info)
|
||||
// @Override
|
||||
// public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info)
|
||||
// {
|
||||
// super.onInitializeAccessibilityNodeInfo(info);
|
||||
// info.setClassName(MySwitch.class.getName());
|
||||
// CharSequence switchText = isChecked() ? mTextOn : mTextOff;
|
||||
// if (!TextUtils.isEmpty(switchText))
|
||||
// {
|
||||
// CharSequence oldText = info.getText();
|
||||
// if (TextUtils.isEmpty(oldText))
|
||||
// {
|
||||
// info.setText(switchText);
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// StringBuilder newText = new StringBuilder();
|
||||
// newText.append(oldText).append(' ').append(switchText);
|
||||
// info.setText(newText);
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
}
|
|
@ -0,0 +1,305 @@
|
|||
/*
|
||||
* This source is part of the
|
||||
* _____ ___ ____
|
||||
* __ / / _ \/ _ | / __/___ _______ _
|
||||
* / // / , _/ __ |/ _/_/ _ \/ __/ _ `/
|
||||
* \___/_/|_/_/ |_/_/ (_)___/_/ \_, /
|
||||
* /___/
|
||||
* repository.
|
||||
*
|
||||
* Copyright 2012 Benoit 'BoD' Lubek (BoD@JRAF.org). All Rights Reserved.
|
||||
* Copyright (C) 2012 Intrications (intrications.com)
|
||||
* Copyright (C) 2010 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package de.ankri.views;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.SharedPreferences;
|
||||
import android.content.res.TypedArray;
|
||||
import android.os.Parcel;
|
||||
import android.os.Parcelable;
|
||||
import android.preference.Preference;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.View;
|
||||
import android.widget.TextView;
|
||||
//import android.view.accessibility.AccessibilityEvent;
|
||||
//import android.view.accessibility.AccessibilityManager;
|
||||
|
||||
/**
|
||||
* Common base class for preferences that have two selectable states, persist a
|
||||
* boolean value in SharedPreferences, and may have dependent preferences that are
|
||||
* enabled/disabled based on the current state.
|
||||
*/
|
||||
public abstract class TwoStatePreference extends Preference {
|
||||
|
||||
private CharSequence mSummaryOn;
|
||||
private CharSequence mSummaryOff;
|
||||
boolean mChecked;
|
||||
// private boolean mSendClickAccessibilityEvent;
|
||||
private boolean mDisableDependentsState;
|
||||
|
||||
|
||||
public TwoStatePreference(Context context, AttributeSet attrs, int defStyle) {
|
||||
super(context, attrs, defStyle);
|
||||
}
|
||||
|
||||
public TwoStatePreference(Context context, AttributeSet attrs) {
|
||||
this(context, attrs, 0);
|
||||
}
|
||||
|
||||
public TwoStatePreference(Context context) {
|
||||
this(context, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onClick() {
|
||||
super.onClick();
|
||||
|
||||
boolean newValue = !isChecked();
|
||||
|
||||
// mSendClickAccessibilityEvent = true;
|
||||
|
||||
if (!callChangeListener(newValue)) {
|
||||
return;
|
||||
}
|
||||
|
||||
setChecked(newValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the checked state and saves it to the {@link SharedPreferences}.
|
||||
*
|
||||
* @param checked The checked state.
|
||||
*/
|
||||
public void setChecked(boolean checked) {
|
||||
if (mChecked != checked) {
|
||||
mChecked = checked;
|
||||
persistBoolean(checked);
|
||||
notifyDependencyChange(shouldDisableDependents());
|
||||
notifyChanged();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the checked state.
|
||||
*
|
||||
* @return The checked state.
|
||||
*/
|
||||
public boolean isChecked() {
|
||||
return mChecked;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean shouldDisableDependents() {
|
||||
boolean shouldDisable = mDisableDependentsState ? mChecked : !mChecked;
|
||||
return shouldDisable || super.shouldDisableDependents();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the summary to be shown when checked.
|
||||
*
|
||||
* @param summary The summary to be shown when checked.
|
||||
*/
|
||||
public void setSummaryOn(CharSequence summary) {
|
||||
mSummaryOn = summary;
|
||||
if (isChecked()) {
|
||||
notifyChanged();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @see #setSummaryOn(CharSequence)
|
||||
* @param summaryResId The summary as a resource.
|
||||
*/
|
||||
public void setSummaryOn(int summaryResId) {
|
||||
setSummaryOn(getContext().getString(summaryResId));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the summary to be shown when checked.
|
||||
* @return The summary.
|
||||
*/
|
||||
public CharSequence getSummaryOn() {
|
||||
return mSummaryOn;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the summary to be shown when unchecked.
|
||||
*
|
||||
* @param summary The summary to be shown when unchecked.
|
||||
*/
|
||||
public void setSummaryOff(CharSequence summary) {
|
||||
mSummaryOff = summary;
|
||||
if (!isChecked()) {
|
||||
notifyChanged();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @see #setSummaryOff(CharSequence)
|
||||
* @param summaryResId The summary as a resource.
|
||||
*/
|
||||
public void setSummaryOff(int summaryResId) {
|
||||
setSummaryOff(getContext().getString(summaryResId));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the summary to be shown when unchecked.
|
||||
* @return The summary.
|
||||
*/
|
||||
public CharSequence getSummaryOff() {
|
||||
return mSummaryOff;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether dependents are disabled when this preference is on ({@code true})
|
||||
* or when this preference is off ({@code false}).
|
||||
*
|
||||
* @return Whether dependents are disabled when this preference is on ({@code true})
|
||||
* or when this preference is off ({@code false}).
|
||||
*/
|
||||
public boolean getDisableDependentsState() {
|
||||
return mDisableDependentsState;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets whether dependents are disabled when this preference is on ({@code true})
|
||||
* or when this preference is off ({@code false}).
|
||||
*
|
||||
* @param disableDependentsState The preference state that should disable dependents.
|
||||
*/
|
||||
public void setDisableDependentsState(boolean disableDependentsState) {
|
||||
mDisableDependentsState = disableDependentsState;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Object onGetDefaultValue(TypedArray a, int index) {
|
||||
return a.getBoolean(index, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onSetInitialValue(boolean restoreValue, Object defaultValue) {
|
||||
setChecked(restoreValue ? getPersistedBoolean(mChecked)
|
||||
: (Boolean) defaultValue);
|
||||
}
|
||||
|
||||
// void sendAccessibilityEvent(View view) {
|
||||
// // Since the view is still not attached we create, populate,
|
||||
// // and send the event directly since we do not know when it
|
||||
// // will be attached and posting commands is not as clean.
|
||||
// AccessibilityManager accessibilityManager = AccessibilityManager.getInstance(getContext());
|
||||
// if (mSendClickAccessibilityEvent && accessibilityManager.isEnabled()) {
|
||||
// AccessibilityEvent event = AccessibilityEvent.obtain();
|
||||
// event.setEventType(AccessibilityEvent.TYPE_VIEW_CLICKED);
|
||||
// view.onInitializeAccessibilityEvent(event);
|
||||
// view.dispatchPopulateAccessibilityEvent(event);
|
||||
// accessibilityManager.sendAccessibilityEvent(event);
|
||||
// }
|
||||
// mSendClickAccessibilityEvent = false;
|
||||
// }
|
||||
|
||||
/**
|
||||
* Sync a summary view contained within view's subhierarchy with the correct summary text.
|
||||
* @param view View where a summary should be located
|
||||
*/
|
||||
void syncSummaryView(View view) {
|
||||
// Sync the summary view
|
||||
TextView summaryView = (TextView) view.findViewById(android.R.id.summary);
|
||||
if (summaryView != null) {
|
||||
boolean useDefaultSummary = true;
|
||||
if (mChecked && mSummaryOn != null) {
|
||||
summaryView.setText(mSummaryOn);
|
||||
useDefaultSummary = false;
|
||||
} else if (!mChecked && mSummaryOff != null) {
|
||||
summaryView.setText(mSummaryOff);
|
||||
useDefaultSummary = false;
|
||||
}
|
||||
|
||||
if (useDefaultSummary) {
|
||||
final CharSequence summary = getSummary();
|
||||
if (summary != null) {
|
||||
summaryView.setText(summary);
|
||||
useDefaultSummary = false;
|
||||
}
|
||||
}
|
||||
|
||||
int newVisibility = View.GONE;
|
||||
if (!useDefaultSummary) {
|
||||
// Someone has written to it
|
||||
newVisibility = View.VISIBLE;
|
||||
}
|
||||
if (newVisibility != summaryView.getVisibility()) {
|
||||
summaryView.setVisibility(newVisibility);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Parcelable onSaveInstanceState() {
|
||||
final Parcelable superState = super.onSaveInstanceState();
|
||||
if (isPersistent()) {
|
||||
// No need to save instance state since it's persistent
|
||||
return superState;
|
||||
}
|
||||
|
||||
final SavedState myState = new SavedState(superState);
|
||||
myState.checked = isChecked();
|
||||
return myState;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onRestoreInstanceState(Parcelable state) {
|
||||
if (state == null || !state.getClass().equals(SavedState.class)) {
|
||||
// Didn't save state for us in onSaveInstanceState
|
||||
super.onRestoreInstanceState(state);
|
||||
return;
|
||||
}
|
||||
|
||||
SavedState myState = (SavedState) state;
|
||||
super.onRestoreInstanceState(myState.getSuperState());
|
||||
setChecked(myState.checked);
|
||||
}
|
||||
|
||||
static class SavedState extends BaseSavedState {
|
||||
boolean checked;
|
||||
|
||||
public SavedState(Parcel source) {
|
||||
super(source);
|
||||
checked = source.readInt() == 1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeToParcel(Parcel dest, int flags) {
|
||||
super.writeToParcel(dest, flags);
|
||||
dest.writeInt(checked ? 1 : 0);
|
||||
}
|
||||
|
||||
public SavedState(Parcelable superState) {
|
||||
super(superState);
|
||||
}
|
||||
|
||||
public static final Parcelable.Creator<SavedState> CREATOR =
|
||||
new Parcelable.Creator<SavedState>() {
|
||||
public SavedState createFromParcel(Parcel in) {
|
||||
return new SavedState(in);
|
||||
}
|
||||
|
||||
public SavedState[] newArray(int size) {
|
||||
return new SavedState[size];
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|