diff --git a/shell/android/res/layout/drawer_menu.xml b/shell/android/res/layout/drawer_menu.xml
index c13daa8f1..62ce95152 100644
--- a/shell/android/res/layout/drawer_menu.xml
+++ b/shell/android/res/layout/drawer_menu.xml
@@ -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" >
+ android:gravity="center_vertical|left"
+ android:orientation="horizontal" >
+ android:textAppearance="@style/BaseText"
+ android:textSize="28dp" />
+ android:gravity="center_vertical|left"
+ android:orientation="horizontal" >
+ android:textAppearance="@style/BaseText"
+ android:textSize="28dp" />
+ android:gravity="center_vertical|left"
+ android:orientation="horizontal" >
+ android:textAppearance="@style/BaseText"
+ android:textSize="28dp" />
+ android:gravity="center_vertical|left"
+ android:orientation="horizontal" >
+ android:textAppearance="@style/BaseText"
+ android:textSize="28dp" />
+ android:gravity="center_vertical|left"
+ android:orientation="horizontal" >
+ android:textAppearance="@style/BaseText"
+ android:textSize="28dp" />
+ android:gravity="center_vertical|left"
+ android:orientation="horizontal" >
+ android:textAppearance="@style/BaseText"
+ android:textSize="28dp" />
+
+
+
+
+
+
+
diff --git a/shell/android/res/layout/toast_layout.xml b/shell/android/res/layout/toast_layout.xml
new file mode 100755
index 000000000..d9a5993fe
--- /dev/null
+++ b/shell/android/res/layout/toast_layout.xml
@@ -0,0 +1,32 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/shell/android/res/values/strings.xml b/shell/android/res/values/strings.xml
index 72cb6e40f..99229ceba 100644
--- a/shell/android/res/values/strings.xml
+++ b/shell/android/res/values/strings.xml
@@ -15,6 +15,7 @@
Flash Missing. The Dreamcast Flash is required for this emulator to work. Place the Flash file in %1$s/data/dc_flash.bin
You have to provide the BIOS
The data folder is assumed
+ GitHub Native Unavailable!
BOOT BIOS
SELECT CURRENT FOLDER
@@ -87,6 +88,7 @@
Paths
Input
About
+ Messages
Rate Me
ON
diff --git a/shell/android/src/com/reicast/emulator/AboutFragment.java b/shell/android/src/com/reicast/emulator/AboutFragment.java
index dabe8812b..324087aa2 100644
--- a/shell/android/src/com/reicast/emulator/AboutFragment.java
+++ b/shell/android/src/com/reicast/emulator/AboutFragment.java
@@ -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();
}
});
diff --git a/shell/android/src/com/reicast/emulator/MainActivity.java b/shell/android/src/com/reicast/emulator/MainActivity.java
index 6a71452e9..ead481498 100644
--- a/shell/android/src/com/reicast/emulator/MainActivity.java
+++ b/shell/android/src/com/reicast/emulator/MainActivity.java
@@ -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() {
diff --git a/shell/android/src/com/reicast/emulator/config/Config.java b/shell/android/src/com/reicast/emulator/config/Config.java
index 5401f5fae..2fae922ae 100644
--- a/shell/android/src/com/reicast/emulator/config/Config.java
+++ b/shell/android/src/com/reicast/emulator/config/Config.java
@@ -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();
+ }
+
}
diff --git a/shell/debug/AndroidManifest.xml b/shell/debug/AndroidManifest.xml
index 2acdba003..57a016c4f 100644
--- a/shell/debug/AndroidManifest.xml
+++ b/shell/debug/AndroidManifest.xml
@@ -9,18 +9,38 @@
android:targetSdkVersion="18" />
+
+
+
+
+
+
-
+ android:label="@string/app_name">
+
-
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/shell/debug/build.xml b/shell/debug/build.xml
index 8164f2645..35d10a74f 100644
--- a/shell/debug/build.xml
+++ b/shell/debug/build.xml
@@ -1,5 +1,5 @@
-
+
+
+
+
\ No newline at end of file
diff --git a/shell/debug/res/drawable/gradient_bg_hover.xml b/shell/debug/res/drawable/gradient_bg_hover.xml
new file mode 100755
index 000000000..aab77af2e
--- /dev/null
+++ b/shell/debug/res/drawable/gradient_bg_hover.xml
@@ -0,0 +1,12 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/shell/debug/res/drawable/image_bg.xml b/shell/debug/res/drawable/image_bg.xml
new file mode 100755
index 000000000..a1004de21
--- /dev/null
+++ b/shell/debug/res/drawable/image_bg.xml
@@ -0,0 +1,14 @@
+
+
+
+ -
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/shell/debug/res/drawable/list_selector.xml b/shell/debug/res/drawable/list_selector.xml
new file mode 100755
index 000000000..fb1560c2e
--- /dev/null
+++ b/shell/debug/res/drawable/list_selector.xml
@@ -0,0 +1,9 @@
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/shell/debug/res/layout/dialog_item.xml b/shell/debug/res/layout/dialog_item.xml
new file mode 100644
index 000000000..a18331bd9
--- /dev/null
+++ b/shell/debug/res/layout/dialog_item.xml
@@ -0,0 +1,57 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/shell/debug/res/layout/dialog_message.xml b/shell/debug/res/layout/dialog_message.xml
new file mode 100644
index 000000000..db9d185ad
--- /dev/null
+++ b/shell/debug/res/layout/dialog_message.xml
@@ -0,0 +1,38 @@
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/shell/debug/res/values/colors.xml b/shell/debug/res/values/colors.xml
new file mode 100755
index 000000000..2a8bfd4d4
--- /dev/null
+++ b/shell/debug/res/values/colors.xml
@@ -0,0 +1,5 @@
+
+
+ #50FFFFFF
+ #B0000000
+
diff --git a/shell/debug/res/values/strings.xml b/shell/debug/res/values/strings.xml
index c6e95d3ac..4f3c86c9c 100644
--- a/shell/debug/res/values/strings.xml
+++ b/shell/debug/res/values/strings.xml
@@ -1,4 +1,4 @@
- reicast debugger
+ reicast debug
diff --git a/shell/debug/res/values/themes.xml b/shell/debug/res/values/themes.xml
new file mode 100644
index 000000000..1898eb1dc
--- /dev/null
+++ b/shell/debug/res/values/themes.xml
@@ -0,0 +1,12 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/shell/debug/src/com/reicast/emulator/debug/Debug.java b/shell/debug/src/com/reicast/emulator/debug/Debug.java
new file mode 100644
index 000000000..c6cb97a74
--- /dev/null
+++ b/shell/debug/src/com/reicast/emulator/debug/Debug.java
@@ -0,0 +1,554 @@
+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.text.DateFormat;
+import java.text.ParseException;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Date;
+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.content.Context;
+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.preference.PreferenceManager;
+import android.util.Log;
+import android.util.Patterns;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.view.ViewGroup;
+import android.widget.BaseAdapter;
+import android.widget.Button;
+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;
+
+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, false);
+ gcm = GoogleCloudMessaging.getInstance(mContext);
+ } else {
+ GooglePlayServicesUtil.getErrorDialog(resultCode, this, 1).show();
+ }
+ RequestArchive mRequestArchive = new RequestArchive() {
+ @Override
+ protected void onPostExecute(List jsonArray) {
+ if (jsonArray != null && !jsonArray.isEmpty()) {
+ ListView wrapper = (ListView) findViewById(R.id.dialog_list);
+ MessageAdapter adapter = new MessageAdapter(mContext, jsonArray);
+ 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 data;
+
+ public MessageAdapter(Context c, List d) {
+ mContext = c;
+ data = d;
+ }
+
+ 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);
+
+ TextView itemName = (TextView) vi
+ .findViewById(R.id.item_name);
+ itemName.setText(message[0] + " [" + message[2] + "]");
+
+ TextView itemContent = (TextView) vi
+ .findViewById(R.id.item_content);
+ itemContent.setText(message[1]);
+
+ return vi;
+ }
+ }
+
+ 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 {
+ @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 user = new HashMap();
+ 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, false);
+ }
+ }
+ };
+
+ private class PostServer extends
+ AsyncTask