Integrate an inline messaging system using Android GCM

This commit is contained in:
TwistedUmbrella 2014-03-03 11:37:29 -05:00
parent 78ad0d6044
commit ad4ecbab0e
22 changed files with 1146 additions and 85 deletions

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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();
}
});

View File

@ -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() {

View File

@ -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();
}
}

View File

@ -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>

View File

@ -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

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -0,0 +1,38 @@
<?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" >
<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>

View File

@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<color name="white">#50FFFFFF</color>
<color name="black">#B0000000</color>
</resources>

View File

@ -1,4 +1,4 @@
<resources>
<string name="app_name">reicast debugger</string>
<string name="app_name">reicast debug</string>
</resources>

View File

@ -0,0 +1,12 @@
<?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>
</style>
</resources>

View File

@ -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<String[]> 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<String[]> data;
public MessageAdapter(Context c, List<String[]> 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<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, false);
}
}
};
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 tag #" + 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, boolean speak) {
CloudSelecao mCloudSelecao = new CloudSelecao(speak);
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> {
private boolean speak;
CloudSelecao(boolean speak) {
this.speak = speak;
}
@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 tag #" + 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();
}
}

View File

@ -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();
}
}

View File

@ -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);
}
}

View File

@ -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 tag #")) {
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 tag #", "");
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 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);
}
}
}

View File

@ -0,0 +1,123 @@
/* ====================================================================
* 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 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[]> message = 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("]")) {
String[] jsonString = response.split(" , ");
for (String jsonText : jsonString) {
if (jsonText.contains("[\"")) {
jsonText = jsonText.replace("[\"", "");
}
if (jsonText.contains("\"]")) {
jsonText = jsonText.replace("\"]", "");
}
message.add(jsonText.split("\",\""));
}
return message;
}
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);
}
}