I trying to develop an application that can send AT commands directly to modem, from a native application interface (or possibly via terminal).
There are many obstacles here, but the main one is that the modem AT interface is rarely available through a simple serial device, which is why all other AT command apps always fail for the Samsung Galaxy class devices, which use a modified IPC protocol over sockets. However, by using the built-in RIL_OEM_HOOK_RAW and RIL_OEM_HOOK_STR we should be able to do this.
I then found this document for the Ublox LISA-U2 UMTS/HSPA voice and data modules, which show an example Android App, including most of the code used in that App. However, I cannot get this to compile in Eclipse. I think the reason is that there are many cosmetic changes in the AOS, while there are many typos in the code in that document.
I would very much like to get this or something very similar to work. I'm mainly developing on the Samsung Galaxy S2 (GB).
Here is a picture (from that document).
{
"lightbox_close": "Close",
"lightbox_next": "Next",
"lightbox_previous": "Previous",
"lightbox_error": "The requested content cannot be loaded. Please try again later.",
"lightbox_start_slideshow": "Start slideshow",
"lightbox_stop_slideshow": "Stop slideshow",
"lightbox_full_screen": "Full screen",
"lightbox_thumbnails": "Thumbnails",
"lightbox_download": "Download",
"lightbox_share": "Share",
"lightbox_zoom": "Zoom",
"lightbox_new_window": "New window",
"lightbox_toggle_sidebar": "Toggle sidebar"
}
Any help with this would be very very much appreciated!
It will also benefit the rest of the community as it will provide a foundation
for my future development of many other things to come, for free!
BTW. The BP used in that device is the very popular Intel/Infineon XGOLD-626...
When trying to run the code shown below in Eclipse (using GB 2.3.3+),
it fails with the following errors:The import com.android.internal.telephony cannot be resolved
The import android.os.AsyncResult cannot be resolvedIt seem like it doesn't recognize these imports:
Code:
import com.android.internal.telephony.Phone;
import com.android.internal.telephony.PhoneFactory;
import android.os.AsyncResult;
The java code is:
Code:
[SIZE=2]
/*=========================================================
Demo App Code by Ublox, modified copy and paste from:
http://www.u-blox.com/images/downloads/Product_Docs/AndroidRIL_Source_Code_ApplicationNote_%283G.G2-CS-11003%29.pdf
=========================================================== */
package com.testapp.sat;
import android.app.Activity;
import android.os.Bundle;
import android.view.View;
import android.widget.EditText;
import android.widget.RadioButton;
import android.widget.RadioGroup;
import android.widget.Toast;
import com.android.internal.telephony.Phone;
import com.android.internal.telephony.PhoneFactory;
import android.view.View.OnKeyListener;
import android.view.KeyEvent;
import android.os.Message;
import android.os.Handler;
import android.os.AsyncResult;
import android.util.Log;
import android.app.AlertDialog;
public class RilOemHookTest extends Activity
{
private static final String LOG_TAG = "RILOemHookTestApp";
private RadioButton mRadioButtonAPI1 = null;
private RadioGroup mRadioGroupAPI = null;
private Phone mPhone = null;
private EditText CmdRespText = null;
private static final int EVENT_RIL_OEM_HOOK_CMDRAW_COMPLETE = 1300;
private static final int EVENT_RIL_OEM_HOOK_CMDSTR_COMPLETE = 1400;
private static final int EVENT_UNSOL_RIL_OEM_HOOK_RAW = 500;
private static final int EVENT_UNSOL_RIL_OEM_HOOK_STR = 600;
@Override
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
setContentView(R.layout.riloemhook_layout);
mRadioButtonAPI1 = (RadioButton) findViewById(R.id.radio_api1);
mRadioGroupAPI = (RadioGroup) findViewById(R.id.radio_group_api);
// Initially turn on first button.
mRadioButtonAPI1.toggle();
//Get our main phone object.
// mPhone = PhoneFactory.getDefaultPhone();
//Register for OEM raw notification.
// mPhone.mCM.setOnUnsolOemHookRaw(mHandler, EVENT_UNSOL_RIL_OEM_HOOK_RAW, null);
//Capture text edit key press
CmdRespText = (EditText) findViewById(R.id.edit_cmdstr);
CmdRespText.setOnKeyListener(new OnKeyListener() {
public boolean onKey(View v, int keyCode, KeyEvent event) {
//If the event is a key-down event on the "enter" button
if ((event.getAction() == KeyEvent.ACTION_DOWN) && (keyCode == KeyEvent.KEYCODE_ENTER)) {
//Perform action on key press
Toast.makeText(RilOemHookTest.this,
CmdRespText.getText(), Toast.LENGTH_SHORT).show();
return true;
}
return false;
}
});
}
@Override
public void onPause()
{
super.onPause();
log("onPause()");
//Unregister for OEM raw notification.
// mPhone.mCM.unSetOnUnsolOemHookRaw(mHandler);
}
@Override
public void onResume()
{
super.onResume();
log("onResume()");
//Register for OEM raw notification.
// mPhone.mCM.setOnUnsolOemHookRaw(mHandler, EVENT_UNSOL_RIL_OEM_HOOK_RAW, null);
}
public void onRun(View view)
{
//Get the checked button
int idButtonChecked = mRadioGroupAPI.getCheckedRadioButtonId();
//Get the response field
CmdRespText = (EditText) findViewById(R.id.edit_response);
byte[] oemhook = null;
switch(idButtonChecked)
{
case R.id.radio_api1:
oemhook = new byte[1];
oemhook[0] = (byte)0xAA;
break;
case R.id.radio_api2:
oemhook = new byte[2];
oemhook[0] = (byte)0xBB;
oemhook[1] = (byte)0x55;
break;
case R.id.radio_api3:
//Send OEM notification (just echo the data bytes)
oemhook = new byte[7];
oemhook[0] = (byte)0xCC;
oemhook[1] = (byte)0x12;
oemhook[2] = (byte)0x34;
oemhook[3] = (byte)0x56;
oemhook[4] = (byte)0x78;
oemhook[5] = (byte)0x9A;
oemhook[6] = (byte)0xBC;
break;
case R.id.radio_api4:
//Send OEM command string
break;
default:
log("unknown button selected");
break;
}
if (idButtonChecked!=R.id.radio_api4) {
Message msg =
mHandler.obtainMessage(EVENT_RIL_OEM_HOOK_CMDRAW_COMPLETE);
mPhone.invokeOemRilRequestRaw(oemhook, msg);
CmdRespText.setText("");
} else {
//Copy string from EditText and add carriage return
String[] oemhookstring = { ((EditText)
findViewById(R.id.edit_cmdstr)).getText().toString()+'\r' } ;
//Create message
Message msg =
mHandler.obtainMessage(EVENT_RIL_OEM_HOOK_CMDSTR_COMPLETE);
//Send request
mPhone.invokeOemRilRequestStrings(oemhookstring, msg);
CmdRespText = (EditText) findViewById(R.id.edit_response);
CmdRespText.setText("---Wait response---");
}
}
private void logRilOemHookResponse(AsyncResult ar) {
log("received oem hook response");
String str = new String("");
if (ar.exception != null) {
log("Exception:" + ar.exception);
str += "Exception:" + ar.exception + "\n\n";
}
if (ar.result != null)
{
byte[] oemResponse = (byte[])ar.result;
int size = oemResponse.length;
log("oemResponse length=[" + Integer.toString(size) + "]");
str += "oemResponse length=[" + Integer.toString(size) + "]" + "\n";
if (size > 0) {
for (int i=0; i<size; i++) {
byte myByte = oemResponse[i];
int myInt = (int)(myByte & 0xFF);
log("oemResponse[" + Integer.toString(i) + "]=[0x" +
Integer.toString(myInt,16) + "]");
str += "oemResponse[" + Integer.toString(i) + "]=[0x" +
Integer.toString(myInt,16) + "]" + "\n";
}
}
} else {
log("received NULL oem hook response");
str += "received NULL oem hook response";
}
// Display message box
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setMessage(str);
builder.setPositiveButton("OK", null);
AlertDialog alert = builder.create();
alert.show();
}
private void logRilOemHookResponseString(AsyncResult ar) {
log("received oem hook string response");
String str = new String("");
CmdRespText = (EditText) findViewById(R.id.edit_response);
if (ar.exception != null) {
log("Exception:" + ar.exception);
str += "Exception:" + ar.exception + "\n\n";
}
if (ar.result != null) {
String[] oemStrResponse = (String[])ar.result;
int sizeStr = oemStrResponse.length;
log("oemResponseString[0] [" + oemStrResponse[0] + "]");
CmdRespText.setText( "" + oemStrResponse[0] );
} else {
log("received NULL oem hook response");
CmdRespText.setText( "No response or error received" );
}
}
private void log(String msg) {
Log.d(LOG_TAG, "[RIL_HOOK_OEM_TESTAPP] " + msg);
}
private Handler mHandler = new Handler() {
public void handleMessage(Message msg) {
AsyncResult ar;
switch (msg.what) {
case EVENT_RIL_OEM_HOOK_CMDRAW_COMPLETE:
log("EVENT_RIL_OEM_HOOK_CMDRAW_COMPLETE");
ar = (AsyncResult) msg.obj;
logRilOemHookResponse(ar);
break;
case EVENT_RIL_OEM_HOOK_CMDSTR_COMPLETE:
log("EVENT_RIL_OEM_HOOK_CMDSTR_COMPLETE");
ar = (AsyncResult) msg.obj;
logRilOemHookResponseString(ar);
break;
case EVENT_UNSOL_RIL_OEM_HOOK_RAW:
break;
case EVENT_UNSOL_RIL_OEM_HOOK_STR:
break;
}
}
};
}
[/SIZE]
------------------ EDIT ---------------------
Of course they aren't recognized! They are internal packages not available outside the standard AOS API's. But I'm very stubborn and have now spent 2 days trying to use those damn packages anyway! I managed! I will post an update here and a whole new thread on how that is done, once I can get some other bugs out of the way...
new thread posted?
Here is my new thread on how to use and import internal packages into your project,
including instructions for hacking Eclipse ADT plugin, in order to allow for using
the com.android.internal classes.
"[APP][DEV][GUIDE] Using the Android Java Internal/Hidden API classes"
It is work in progress (WIP), and include a lot of manual file manipulations.
As such it is closely related to this thread. So keep an eye open for updates.
EDIT: 2014-01-06
That thread still work for API-17 JAR's!!
So, I managed to get it to compile but not without two main issues.
0) I got it to run on an SGS2 running GB2.3.4, but it FC's after being run.
1) I kept on getting AlertDialog builder complaints like:
"the constructor AlertDialog.Builder is undefined" so I had to comment out all code related to that. I searched for fixes, but since I'm not a Java programmer, I could not resolve this in the proper way...
2) Then I ran into some other undocumented errors from my own built (according to instructions) android.jar, so I just used the inazaruk's pre-made one, and it compiled. Here are his files:
https://github.com/inazaruk/android-sdk/tree/master/platforms
The main point is that we should not expect to blindly use the Ublox code and think it will work. Instead we need to understand the following:
a) How to properly use the RIL constants
Code:
[SIZE=2]RILConstants.java:
int RIL_REQUEST_OEM_HOOK_RAW = 59;
int RIL_REQUEST_OEM_HOOK_STRINGS = 60;
int RIL_UNSOL_OEM_HOOK_RAW = 1028;
my code:
EVENT_RIL_OEM_HOOK_CMDRAW_COMPLETE
EVENT_RIL_OEM_HOOK_CMDSTR_COMPLETE
EVENT_UNSOL_RIL_OEM_HOOK_RAW
EVENT_UNSOL_RIL_OEM_HOOK_STR
[/SIZE]
b) What the various bytecodes sent, are actually doing, and how to make them do what we want.
Code:
[SIZE=2] case R.id.radio_api1:
oemhook = new byte[1];
oemhook[0] = (byte)0xAA;
break;
case R.id.radio_api2:
oemhook = new byte[2];
oemhook[0] = (byte)0xBB;
oemhook[1] = (byte)0x55;
break;
case R.id.radio_api3:
//Send OEM notification (just echo the data bytes)
oemhook = new byte[7];
oemhook[0] = (byte)0xCC;
oemhook[1] = (byte)0x12;
oemhook[2] = (byte)0x34;
oemhook[3] = (byte)0x56;
oemhook[4] = (byte)0x78;
oemhook[5] = (byte)0x9A;
oemhook[6] = (byte)0xBC;
break;
[/SIZE]
c) If we can simplify the code to just send ONE hard-coded AT command and read the response.
d) alternatively use a completely different method, that will undoubtedly work, but will be device dependent. I'm talking about the (Samsung modified) IPC modem-communication protocols, as discussed in my older threads...
e) Find out how to use a local shell + device to send AT commands directly to BP.
To monitor the radio and related messages from the App (on a Sumsung) , you can use:
Code:
[SIZE=2]adb shell logcat AndroidRuntime:* ActivityManager:* dalvikvm:* DataRouter:E NetdConnector:D *:s
[/SIZE]
I've done similar work in my SprintDiagnostics app included in our Eos project. Its in the sprint gnex thread. There's no way to make this a user app as you have to be on the Phone looper thread. Meaning you have to declare the phone process in the manifest for the activity calling the raw ril requests. I can grab msl, write prl, and some other good stuff. And enable diagnostic mode based on shell work from Autoprime.
Edit: http://git.teameos.org/eos/device/samsung/toroplus.git/tree/SprintDiagnostics?h=jellybean
bigrushdog said:
I've done similar work in my SprintDiagnostics app included in our Eos project. Its in the sprint gnex thread. There's no way to make this a user app as you have to be on the Phone looper thread. Meaning you have to declare the phone process in the manifest for the activity calling the raw ril requests. I can grab msl, write prl, and some other good stuff. And enable diagnostic mode based on shell work from Autoprime.
Edit: http://git.teameos.org/eos/device/samsung/toroplus.git/tree/SprintDiagnostics?h=jellybean
Click to expand...
Click to collapse
Hi! Thanks for interesting info, but unfortunately I don't understand all you're saying. But I got many more questions that answers.
First of all, what thread are you referring to? (Could you give a link?) Second, how do you do that declaration? (Code example?) BTW. I looked briefly at the code and must admit it seem a bit cryptic, as I didn't quite find the where AT's goes or are created. Third, what is "msl"? Do you have an APK to try on? Finally, what specific modem (cellular) processor are you running in that device? LTE?
Screenshots?
Thanks in advance.
Hi There
Forgive me if I'm oversimplifying it here but wouldn't minicom or some similar terminal program do this? I believe minicom is included in busybox.
Surely you could just wrap that around using JNI
AT Commands in android are sent natively by the vendor implemented ril library, normally set in global properties as rild.libpath. It may be worth stepping away from the JAVA code for a moment and looking at the rild and reference-ril sources in the aosp source. The atchannel.c is where the "magic" happens. Also If you haven't already, read PDK Telephony Documentation
When you get down to it, all you really want to do is read and write from a tty character device so doing it "raw" is always an option.
Also to monitor the Radio log in android use adb logcat -b radio , You will be enlightened as you watch the AT commands fly by!
trevd said:
Forgive me if I'm oversimplifying it here but ...
Also to monitor the Radio log in android use adb logcat -b radio , You will be enlightened as you watch the AT commands fly by!
Click to expand...
Click to collapse
No, you are missing the entire point of this thread. We are trying to circumvent actually using the vendor RIL, by tunneling the commands via the OEM_RAW requests. That is because it is often filtering out non-standard OEM AT commands that are not part of the common standard. These are all dependent on the BP/CP and and varies widely from device to device. In addition on many devices the direct modem serial is either not possible (because there is no such connection/transport) or it has been blocked/disabled. The Java way of circumventing this, is what this thread is all about. In addition the "-b radio" logcat doesn't fetch ATs sent by vendor specific OEM IPCs, for example. This is the case for the SGS2 (GT-I9100) and many other devices.
E:V:A said:
No, you are missing the entire point of this thread.
Click to expand...
Click to collapse
Ah, Okay...If I had a SGS2 too play with I'd probably join in on this little adventure, but I don't
trevd said:
Ah, Okay...If I had a SGS2 too play with I'd probably join in on this little adventure, but I don't
Click to expand...
Click to collapse
Well again... The point is that: RIL_OEM_HOOK_RAW and RIL_OEM_HOOK_STR are (internally) accessible for all RIL's. Which means that you CAN join in!
E:V:A said:
Hi! Thanks for interesting info, but unfortunately I don't understand all you're saying. But I got many more questions that answers.
First of all, what thread are you referring to? (Could you give a link?) Second, how do you do that declaration? (Code example?) BTW. I looked briefly at the code and must admit it seem a bit cryptic, as I didn't quite find the where AT's goes or are created. Third, what is "msl"? Do you have an APK to try on? Finally, what specific modem (cellular) processor are you running in that device? LTE?
Screenshots?
Thanks in advance.
Click to expand...
Click to collapse
My apologies for not responding sooner. I forgot to subscribe ;( . I'd like to try and clear up my previous post as well as try to shed some light on this topic. First, I'm by no means a telephony expert. I've read some of your other threads and found them remarkable. I write Java and do feature development in a AOSP project. I currently am doing telephony work on Samsun Sprint CDMA devices including galaxy nexus, nexus s, and working on s3. As you know, those use the VIA chipset. However, working through the Android radio layer makes that irrelevant, for the most part. I've also worked with Autoprime on some telephony/modem/ril matters.
First some code and background on what I have done on Sprint Galaxy Nexus. These are some code fragments from my CDMATools app.
Manifest:
Code:
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="org.teameos.settings.device"
android:sharedUserId="android.uid.system"
android:versionCode="1"
android:versionName="1.0" >
Of importance if the android.uid.system uid. This allows the app to access the phone process, which is required for PhoneFactory.getDefaultPhone(); The following activity queries ril for a Master Subsidy Lock code (msl)
Code:
<activity
android:name=".MslActivity"
android:label="@string/msl_activity"
android:process="com.android.phone" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</activity>
And the actual method which is used to query the radio...
Code:
private void checkMSLCode()
{
ByteArrayOutputStream bytearrayoutputstream;
DataOutputStream dataoutputstream;
bytearrayoutputstream = new ByteArrayOutputStream();
dataoutputstream = new DataOutputStream(bytearrayoutputstream);
try {
dataoutputstream.writeByte(main_cmd_hidden);
dataoutputstream.writeByte(OEM_SUB_CMD_GET_MSL);
dataoutputstream.writeShort(device_short);
if (isTuna) {
dataoutputstream.writeByte(OEM_SUB_RAW_MSL);
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
mPhone.invokeOemRilRequestRaw(bytearrayoutputstream.toByteArray(),
mHandler.obtainMessage(GET_MSL_DONE));
}
A snipit of the handler code to receive and process incoming messages from radio.
Code:
mHandler = new Handler() {
public void handleMessage(Message message) {
Log.i(LOG_TAG, "MSL response incoming!!");
AsyncResult asyncresult = (AsyncResult) message.obj;
if (asyncresult.exception != null)
{
Log.i(LOG_TAG, "AsyncResult Exception Occur!!!");
} else
if (asyncresult.result == null)
{
Log.i(LOG_TAG, "ar.result == NULL! - No answer for MSL response");
} else
{
byte abyte0[] = (byte[]) (byte[]) asyncresult.result;
String s = new String("");
for (int j = 0; j < 6; j++)
s = (new StringBuilder()).append(s).append((char) abyte0[j]).toString();
Intent intent = new Intent().setAction(getString(R.string.msl_action));
intent.putExtra(getString(R.string.msl_key), Integer.parseInt(s));
mContext.sendBroadcast(intent);
finish();
}
}
};
I have been able to get the raw commands from RE'ing various device proprietary apk's. Of importance to this topic is this checkMSLCode() function. This is the equivalent of raw AT commands, but being passed though the Android radio layer. From looking at the code for that AT command application you posted in the OP, it uses a similar structure. In fact, if time permits, i'm confident i can expand on it and make it somewhat more robust. One problem is see is finding an effective way to interpret the AT command responses from the radio. Requests often get a response in different forms, thus requiring a unique handler for each function. Of course, we could always do a Object.toString() to the log and try to decipher it. I hope this info sheds some light on the topic. I'll post when I come up with more.
Edit: also I should note, i build this apk inside of my AOSP repo, so it sees all the internal api automatically. I'm now looking at using reflection to do some telephony stuff, but it's proving rather tricky.
Very nice! (That was an understatement!)
I just wish I knew how to apply that...
Regarding the raw command (and their appropriate responses), I think there is both code and documents available for this, for Qualcomm chips. I know nothing about the VIA chipsets, but a whole lot more about the Qualcomm MSM stuff... Many CDMA devices use just Qualcomm, so perhaps you've been across some of these. I bet we could get this working on them, somehow.
Since, just as you said, the RIL should take care of much of the details. We just need to apply the command requests and responses. But my Java App making skills are really not even worthy a try at the moment.
Somebody else who'd care to join us, to give this a try?
As an aside and a note of reference, mostly to myself. (For a professional OEM programmer, this may be obvious, but me it's not. ) After having dug through some of the QC MSM code, I've come to the conclusion that QC like to use what they (?) call SMD (Shared memory Device) for doing their IPC stuff, which include talking to modem. While for Infineon/Intel modem, we know that Samsung have developed their own (non-standard) IPC protocol version for this communication...
While rewriting my Cdmatools app making it fragment based, I used reflection to access all the hidden apis. I'll fork the source for the at command app and apply the reflected classes.
I also got access to my Samsung Galaxy S3 via Reflection... although I had to go AOSP and sign with they same system key to run as looper. But now I just need help with what to send........ via RAW/String
bigrushdog said:
While rewriting my Cdmatools app making it fragment based, I used reflection to access all the hidden apis. I'll fork the source for the at command app and apply the reflected classes.
Click to expand...
Click to collapse
Excellent. Where can we download this?
Have you been able to make demo App or something?
enigma99a said:
I also got access to my Samsung Galaxy S3 via Reflection... although I had to go AOSP and sign with they same system key to run as looper. But now I just need help with what to send........ via RAW/String
Click to expand...
Click to collapse
Can you elaborate? Do you need info on what AT commands to send, or are you asking about the protocol?
PS. I would be great if you could say something more about how you did it, so that perhaps other interested people can join in. We need more people on this, so that we can start working on THIS project.
my apologies that i've been unable to continue efforts on that project. My aosp project consumes all of my development time and then some. Here is the source for CDMATools. I was looking for ways to monetize it. However, due to android's permissions scheme, it's not gonna happen lol. so here ya go.
www.miuimods.com/bigrushdog/CDMATools.tar.bz2
compile in eclipse then sign with aosp platorm test keys. I use some libraries that require it be compiled in eclipse. and because of the internal telephony imports, it must have the platform signature. Also, because it's compiled in eclipse, i use reflection to access internal telephony. Of interest will be the Phone service class. everything else is just ui. Look at how i structure the reflection. You can reflect all the internal telephony calls in the same fashion. any issues, just hit me. but my time now is very limited as I have a large team to manage and my plate is more than full. Good luck!
feel free to post on github, distribute, or whatever.
So I have not yet had time to modify OP app or using bigrushdog's CDMAtools. But I will soon. In the meantime I just post the internals for the _OEM_HOOK_ statements.
The code base is here:
https://android.googlesource.com/platform/frameworks/base.git/
So from the JellyBean RIL.java:
Code:
[SIZE=2]public void invokeOemRilRequestRaw(byte[] data, Message response) {
RILRequest rr = RILRequest.obtain(RIL_REQUEST_OEM_HOOK_RAW, response);
if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)
+ "[" + IccUtils.bytesToHexString(data)
+ "]");
rr.mp.writeByteArray(data);
send(rr);
}
public void invokeOemRilRequestStrings(String[] strings, Message response) {
RILRequest rr = RILRequest.obtain(RIL_REQUEST_OEM_HOOK_STRINGS, response);
if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest));
rr.mp.writeStringArray(strings);
send(rr);
}[/SIZE]
And the expected response can be found in the ril.h:
Code:
[SIZE=2]#define [B]RIL_REQUEST_DATA_REGISTRATION_STATE[/B] 21
/**
* RIL_REQUEST_DATA_REGISTRATION_STATE
*
* Request current DATA registration state
*
* "data" is NULL
* "response" is a "char **"
* ((const char **)response)[0] is registration state 0-5 from TS 27.007 10.1.20 AT+CGREG
* ((const char **)response)[1] is LAC if registered or NULL if not
* ((const char **)response)[2] is CID if registered or NULL if not
* ((const char **)response)[3] indicates the available data radio technology,
* valid values as defined by RIL_RadioTechnology.
* ((const char **)response)[4] if registration state is 3 (Registration
* denied) this is an enumerated reason why
* registration was denied. See 3GPP TS 24.008,
* Annex G.6 "Additonal cause codes for GMM".
* 7 == GPRS services not allowed
* 8 == GPRS services and non-GPRS services not allowed
* 9 == MS identity cannot be derived by the network
* 10 == Implicitly detached
* 14 == GPRS services not allowed in this PLMN
* 16 == MSC temporarily not reachable
* 40 == No PDP context activated
* ((const char **)response)[5] The maximum number of simultaneous Data Calls that can be
* established using RIL_REQUEST_SETUP_DATA_CALL.
*
* [/SIZE]The values at offsets 6..10 are [COLOR=Blue][B]optional LTE[/B][/COLOR] location information in decimal.[SIZE=2]
* If a value is unknown that value may be NULL. If all values are NULL,
* none need to be present.
* ((const char **)response)[6] is TAC, a 16-bit Tracking Area Code.
* ((const char **)response)[7] is CID, a 0-503 Physical Cell Identifier.
* ((const char **)response)[8] is ECI, a 28-bit E-UTRAN Cell Identifier.
* ((const char **)response)[9] is CSGID, a 27-bit Closed Subscriber Group Identity.
* [COLOR=Red]((const char **)response)[10] is TADV, a 6-bit timing advance value.[/COLOR]
*
* LAC and CID are in hexadecimal format.
* valid LAC are 0x0000 - 0xffff
* valid CID are 0x00000000 - 0x0fffffff
*
* Please note that registration state 4 ("unknown") is treated
* as "out of service" in the Android telephony system
*
* Valid errors:
* SUCCESS
* RADIO_NOT_AVAILABLE
* GENERIC_FAILURE
*/
#define [B]RIL_REQUEST_OEM_HOOK_RAW[/B] 59
/**
* RIL_REQUEST_OEM_HOOK_RAW
*
* This request reserved for OEM-specific uses. It passes raw byte arrays
* back and forth.
*
* It can be invoked on the Java side from
* com.android.internal.telephony.Phone.invokeOemRilRequestRaw()
*
* "data" is a char * of bytes copied from the byte[] data argument in java
* "response" is a char * of bytes that will returned via the
* caller's "response" Message here:
* (byte[])(((AsyncResult)response.obj).result)
*
* An error response here will result in
* (((AsyncResult)response.obj).result) == null and
* (((AsyncResult)response.obj).exception) being an instance of
* com.android.internal.telephony.gsm.CommandException
*
* Valid errors:
* All
*/
#define [B]RIL_REQUEST_OEM_HOOK_STRINGS[/B] 60
/**
* RIL_REQUEST_OEM_HOOK_STRINGS
*
* This request reserved for OEM-specific uses. It passes strings
* back and forth.
*
* It can be invoked on the Java side from
* com.android.internal.telephony.Phone.invokeOemRilRequestStrings()
*
* "data" is a const char **, representing an array of null-terminated UTF-8
* strings copied from the "String[] strings" argument to
* invokeOemRilRequestStrings()
*
* "response" is a const char **, representing an array of null-terminated UTF-8
* stings that will be returned via the caller's response message here:
*
* (String[])(((AsyncResult)response.obj).result)
*
* An error response here will result in
* (((AsyncResult)response.obj).result) == null and
* (((AsyncResult)response.obj).exception) being an instance of
* com.android.internal.telephony.gsm.CommandException
*
* Valid errors:
* All
*/
[SIZE=2]#define [COLOR=Purple][B]RIL_REQUEST_RADIO_POWER[/B][/COLOR] 23
/**
* RIL_REQUEST_RADIO_POWER
*
* Toggle radio on and off (for "airplane" mode)
* If the radio is is turned off/on the radio modem subsystem
* is expected return to an initialized state. For instance,
* any voice and data calls will be terminated and all associated
* lists emptied.
*
* "data" is int *
* ((int *)data)[0] is > 0 for "Radio On"
* ((int *)data)[0] is == 0 for "Radio Off"
*
* "response" is NULL
*
* Turn radio on if "on" > 0
* Turn radio off if "on" == 0
*
* Valid errors:
* SUCCESS
* RADIO_NOT_AVAILABLE
* GENERIC_FAILURE
*/
[/SIZE]
[/SIZE]
The last one seem to imply that there could be other "bytes" to be read from that data[] array... Some suggestions have been the Transmitted power... But this was assuming a Qualcomm based modem, so there is no telling if there is another standard there. We don't know...
Here are links to two highly relevant threads on Gmane/GoogleGroups with code excerpts similar to what we need...
http://article.gmane.org/gmane.comp.handhelds.android.ndk/10555
http://article.gmane.org/gmane.comp.handhelds.android.platform/8436
https://groups.google.com/forum/?fromgroups=#!topic/android-platform/tVyNMnXtcEI
With a link to Google Phone G1 Field Test:
http://phoneftd.blogspot.com/2009/03/google-g1-phone-field-test.html
Related
Hello, I am the developer of Zeus Arena, an Xperia Play optimized port of the ioquake3 engine for android.
It was a bit of a headache to get Xperia Play controls working when I first started working on Zeus Arena about 4 or 5 months ago. However recently I decided to try and support more devices with Zeus Arena by adding touch screen controls. Since all of Zeus Arenas graphics where being done in native code, using a native activity no less, this meant I would either have to have two completely separate applications or rewrite most of Zeus Arena. I did the later.
I found an easy way to support Xperia Play controls to an existing application (to those of you unfamiliar with Zeus Arena it is built upon kwaak3). So this post will be a brief tutorial on adding Xperia Play controls to an existing application.
First a note: This tutorial will not tell you how to set up or use the ndk, there are plenty of tutorials for that already. The hardest part of this process should be settting up the ndk.
Adding Xperia Play controls to your existing application:
The main problem with adding Xperia Play controls to your application is the touch pad (if you don't need to support the touch pad ignore this tutorial and look online for SE's tutorial, it's easy). The touchpad requires that you use a native activity to get your input, so the main purpose of this tutorial will be how to use a native activity whilst changing your existing code as little as possible.
First you will need to make a native activity in c code, this activity will poll for input from the touchpad as well as load references to the methods in your android code that deal with the touchpad input. (see sample code below)
Next open your main activity and change it from extending Activity to extend NativeActivity.
Now, and this is the key part really, asap after your call of super.onCreate(savedInstanceState); add this line of code: getWindow().takeSurface(null);
That one magical line of code allows you to add your graphics in your java code, meaning you don't have to change your existing java code any more than this.
However we aren't quite done yet. As mentioned above the native code is getting the input for the touchpad, we probably want to send this to the java code where all the other event handling takes place.
This is simple make a java method that accepts touch pad input and load it up in your native activity and then call it when ever the touch pad is touched.
Example code from Zeus Arena (if you are familiar with Zeus Arena's code (it's open source) the example code won't look too familiar because the code has modified to make it a bit simpler and some of it is from a new update coming to Zeus Arena soon):
java code:
public class Game extends NativeActivity{
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
getWindow().takeSurface(null);
RegisterThis();
mGLSurfaceView = new KwaakView(this, this); // a custom made view for Zeus Arena
setContentView(mGLSurfaceView);
mGLSurfaceView.requestFocus();
mGLSurfaceView.setId(1);
}
//gives the native activity a copy of this object so it can call OnNativeMotion
public native int RegisterThis();
//loads the .so, change library name to whater your library is called
static {
System.loadLibrary("kwaakjni");
}
//called by the native activity when ever touch input is found
public void OnNativeMotion(int action, int x, int y, int source, int device_id) {
if(source == 1048584){ //touchpad
// Obtain MotionEvent object
long downTime = SystemClock.uptimeMillis();
long eventTime = SystemClock.uptimeMillis() + 100;
// List of meta states found here: developer.android.com/reference/android/view/KeyEvent.html#getMetaState()
int metaState = 0;
MotionEvent motionEvent = MotionEvent.obtain(
downTime,
eventTime,
action,
x,
(366-y),
metaState
);
mGLSurfaceView.onTouchPadEvent(motionEvent); //custom made method for dealing with touch input
}
else{
// Obtain MotionEvent object
long downTime = SystemClock.uptimeMillis();
long eventTime = SystemClock.uptimeMillis() + 100;
// List of meta states found here: developer.android.com/reference/android/view/KeyEvent.html#getMetaState()
int metaState = 0;
MotionEvent motionEvent = MotionEvent.obtain(
downTime,
eventTime,
action,
x,
y,
metaState
);
// Dispatch touch event to view
mGLSurfaceView.dispatchTouchEvent(motionEvent);
}
}
}
Native code:
#include <dlfcn.h>
#include <stdio.h>
#include <string.h>
#include <android/log.h>
#include <jni.h>
#include <errno.h>
#include <android_native_app_glue.h>
#include <time.h>
#include <unistd.h>
#include "quake_two_android_Quake2.h"
#define EXPORT_ME __attribute__ ((visibility("default")))
static JavaVM *jVM;
typedef unsigned char BOOL;
#define FALSE 0
#define TRUE 1
//|------------------------------------------------------ NATIVE ACTIVITY ------------------------------------------------------|
static jobject g_pActivity = 0;
static jmethodID javaOnNDKTouch = 0;
/**
* Our saved state data.
*/
struct TOUCHSTATE
{
int down;
int x;
int y;
};
/**
* Shared state for our app.
*/
struct ENGINE
{
struct android_app* app;
int render;
int width;
int height;
int has_focus;
//ugly way to track touch states
struct TOUCHSTATE touchstate_screen[64];
struct TOUCHSTATE touchstate_pad[64];
};
void attach(){
}
/**
* Process the next input event.
*/
static
int32_t
engine_handle_input( struct android_app* app, AInputEvent* event )
{
JNIEnv *jni;
(*jVM)->AttachCurrentThread(jVM, &jni, NULL);
struct ENGINE* engine = (struct ENGINE*)app->userData;
if( AInputEvent_getType(event) == AINPUT_EVENT_TYPE_MOTION )
{
int nPointerCount = AMotionEvent_getPointerCount( event );
int nSourceId = AInputEvent_getSource( event );
int n;
for( n = 0 ; n < nPointerCount ; ++n )
{
int nPointerId = AMotionEvent_getPointerId( event, n );
int nAction = AMOTION_EVENT_ACTION_MASK & AMotionEvent_getAction( event );
int nRawAction = AMotionEvent_getAction( event );
struct TOUCHSTATE *touchstate = 0;
if( nSourceId == AINPUT_SOURCE_TOUCHPAD )
touchstate = engine->touchstate_pad;
else
touchstate = engine->touchstate_screen;
if( nAction == AMOTION_EVENT_ACTION_POINTER_DOWN || nAction == AMOTION_EVENT_ACTION_POINTER_UP )
{
int nPointerIndex = (AMotionEvent_getAction( event ) & AMOTION_EVENT_ACTION_POINTER_INDEX_MASK) >> AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT;
nPointerId = AMotionEvent_getPointerId( event, nPointerIndex );
}
if( nAction == AMOTION_EVENT_ACTION_DOWN || nAction == AMOTION_EVENT_ACTION_POINTER_DOWN )
{
touchstate[nPointerId].down = 1;
}
else if( nAction == AMOTION_EVENT_ACTION_UP || nAction == AMOTION_EVENT_ACTION_POINTER_UP || nAction == AMOTION_EVENT_ACTION_CANCEL )
{
touchstate[nPointerId].down = 0;
}
if (touchstate[nPointerId].down == 1)
{
touchstate[nPointerId].x = AMotionEvent_getX( event, n );
touchstate[nPointerId].y = AMotionEvent_getY( event, n );
}
int handled = 0;
if( jni && g_pActivity ){
//send the event to java code, sends both touch screen and touch pad events, I think the java code will still intercept touch screen events
//so sending them probably isn't needed. If it is needed intercepting key events will be needed in native code as well.
(*jni)->CallVoidMethod( jni, g_pActivity, javaOnNDKTouch, nRawAction, touchstate[nPointerId].x, touchstate[nPointerId].y, nSourceId, 0 );
}
}
return 1;
}
return 0;
}
/**
* Process the next main command.
*/
static
void
engine_handle_cmd( struct android_app* app, int32_t cmd )
{
struct ENGINE* engine = (struct ENGINE*)app->userData;
switch( cmd )
{
case APP_CMD_SAVE_STATE:
// The system has asked us to save our current state. Do so if needed
break;
case APP_CMD_INIT_WINDOW:
// The window is being shown, get it ready.
if( engine->app->window != NULL )
{
engine->has_focus = 1;
}
break;
case APP_CMD_GAINED_FOCUS:
engine->has_focus = 1;
break;
case APP_CMD_LOST_FOCUS:
// When our app loses focus, we stop rendering.
engine->render = 0;
engine->has_focus = 0;
//engine_draw_frame( engine );
break;
}
}
/**
* This is the main entry point of a native application that is using
* android_native_app_glue. It runs in its own thread, with its own
* event loop for receiving input events and doing other things (rendering).
*/
void
android_main( struct android_app* state )
{
struct ENGINE engine;
// Make sure glue isn't stripped.
app_dummy();
memset( &engine, 0, sizeof(engine) );
state->userData = &engine;
state->onAppCmd = engine_handle_cmd;
state->onInputEvent = engine_handle_input;
engine.app = state;
//setup(state);
//JNIEnv *env;
//(*jVM)->AttachCurrentThread(jVM, &env, NULL);
if( state->savedState != NULL )
{
// We are starting with a previous saved state; restore from it.
}
// our 'main loop'
while( 1 )
{
// Read all pending events.
int ident;
int events;
struct android_poll_source* source;
// If not rendering, we will block forever waiting for events.
// If rendering, we loop until all events are read, then continue
// to draw the next frame.
while( (ident = ALooper_pollAll( 100, NULL, &events, (void**)&source) ) >= 0 )
//while( (ident = ALooper_pollAll( 100, NULL, &events, (void**)&source) ) >= 0 )
{
// Process this event.
// This will call the function pointer android_app:nInputEvent() which in our case is
// engine_handle_input()
if( source != NULL )
{
source->process( state, source );
}
// Check if we are exiting.
if( state->destroyRequested != 0 )
{
return;
}
usleep(17000); //17 miliseconds
}
}
}
jint EXPORT_ME
JNICALL Java_quake_two_android_Quake2_RegisterThis(JNIEnv * env, jobject clazz){
g_pActivity = (jobject)(*env)->NewGlobalRef(env, clazz);
return 0;
}
jint EXPORT_ME JNICALL
JNI_OnLoad(JavaVM * vm, void * reserved)
{
JNIEnv *env;
jVM = vm;
if((*vm)->GetEnv(vm, (void**) &env, JNI_VERSION_1_4) != JNI_OK)
{
return -1;
}
const char* interface_path = "quake/two/android/Quake2";
jclass java_activity_class = (*env)->FindClass( env, interface_path );
javaOnNDKTouch = (*env)->GetMethodID( env, java_activity_class, "OnNativeMotion", "(IIIII)V");
javaOnNDKKey = (*env)->GetMethodID( env, java_activity_class, "OnNativeKeyPress", "(III)V");
return JNI_VERSION_1_4;
}
Licences:
/*
* Copyright (c) 2011, Sony Ericsson Mobile Communications AB.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * 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.
* * Neither the name of the Sony Ericsson Mobile Communications AB nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS 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 COPYRIGHT HOLDER OR 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.
*/
/*
* This example uses the NDK and a helper library available in the NDK called 'native app glue',
* which is available in %NDK_ROOT/source/android/native_app_glue. If you are new to NDK or to
* the NativeActivity, look through the native_app_glue source to see how you should set up your
* native app and handle callbacks and messages from Android. Note that the callbacks registered
* in the ANativeActivity_onCreate() entry-point must return in a timely manner, as does
* ANativeActivity_onCreate() itself. The Native App Glue does this by creating a pipe() and
* synchronization objects to handle communication between the Android, the NativeActivity and
* the game/sample logic.
*
* In this example, we read the 'pointer' information from touch events from both the touch-screen
* and the touch-pad (if available). We store their positions and state (up or down), then draw
* the touch positions scaled to the screen.
*
* Although we are using hard-coded values for the touch-pad resultion, you can and should read
* those values at runtime in java by enumerating InputDevices and finding the touchpad device.
*
*/
/*
* Kwaak3 - Java to quake3 interface
* Copyright (C) 2010 Roderick Colenbrander
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
tl;dr
Seriously though, that's awesome. Hopefully this gets implemented and put to good use!
Yeah I just had to scroll down to see if there where any reply's and it is damn long.
Most of it is example code which can be skipped unless your actually going to use it, the first section tells you what you need to know.
Also anyone can take this tutorial and put it anywhere people may want to see it if they want
Sweet, thanks for posting! I've only done a bit of development, and eventually want to get a game going - this is bookmarked and thanked!!!
bigbison said:
... of it is example code which can be skipped unless your actually going to use ...
Click to expand...
Click to collapse
bigbison,
I've also been playing a bit with the native app example. And I still have some questions and didn't find a good place to ask them. So, maybe you can help me out ?
If I understand it correctly ...
The onAppCmd and onInputEvent are called from within the main application thread.
The android_main is running in a seperate thread.
But I don't see any mutexes (or other locking mechanism) in place to prevent simultaneous access to the same application data from both threads. Could it be that your code (and also the standard example code) is not thread-safe?
Are we up for some random crashes ? Or am I missing something ? (I hope so ...)
Dear XDA Admins...
PIN THIS THREAD RIGHT NOW!!!
...thank you.
Nice post, hopefully helps someone....
Just to let you know there is an emoticon in your code that should be fixed....
thanks.
Just wanted to say im a big fan of Zeus Arena. Its really nice.
Very fun to play offline to
Great work man. you got the Xperia controls working perfectly
wiffeltje said:
bigbison,
... Or am I missing something ? (I hope so ...)
Click to expand...
Click to collapse
Just answering my own question.
After posting my question I took yet another look at the android_native_app_glue code and how the ALoop_pollAll interacts with that. And looking at it again, I think I get it.
It looks like the threading is OK and that (most of) the main loop won't be able to run when the call-back functions are running. So, that should be OK.
So, I did overlook some things
Sorry to disturb you a little bit too early with my question.
OnNativeKeyPress
Hi,
I am trying to follow this a little, I can access the touchpad just fine in my nativeactivity but I have lost all access to onKeyDown and onKeyUp using the Sony tutorial. Your code mentions OnNativeKeyPress but I can't see you calling this anywhere, is this expected?
Thanks for any pointers you can offer!
bigbison said:
Hello, I am the developer of Zeus Arena, an Xperia Play optimized port of the ioquake3 engine for android.
It was a bit of a headache to get Xperia Play controls working when I first started working on Zeus Arena about 4 or 5 months ago. However recently I decided to try and support more devices with Zeus Arena by adding touch screen controls. Since all of Zeus Arenas graphics where being done in native code, using a native activity no less, this meant I would either have to have two completely separate applications or rewrite most of Zeus Arena. I did the later.
I found an easy way to support Xperia Play controls to an existing application (to those of you unfamiliar with Zeus Arena it is built upon kwaak3). So this post will be a brief tutorial on adding Xperia Play controls to an existing application.
First a note: This tutorial will not tell you how to set up or use the ndk, there are plenty of tutorials for that already. The hardest part of this process should be settting up the ndk.
Adding Xperia Play controls to your existing application:
The main problem with adding Xperia Play controls to your application is the touch pad (if you don't need to support the touch pad ignore this tutorial and look online for SE's tutorial, it's easy). The touchpad requires that you use a native activity to get your input, so the main purpose of this tutorial will be how to use a native activity whilst changing your existing code as little as possible.
First you will need to make a native activity in c code, this activity will poll for input from the touchpad as well as load references to the methods in your android code that deal with the touchpad input. (see sample code below)
Next open your main activity and change it from extending Activity to extend NativeActivity.
Now, and this is the key part really, asap after your call of super.onCreate(savedInstanceState); add this line of code: getWindow().takeSurface(null);
That one magical line of code allows you to add your graphics in your java code, meaning you don't have to change your existing java code any more than this.
However we aren't quite done yet. As mentioned above the native code is getting the input for the touchpad, we probably want to send this to the java code where all the other event handling takes place.
This is simple make a java method that accepts touch pad input and load it up in your native activity and then call it when ever the touch pad is touched.
Example code from Zeus Arena (if you are familiar with Zeus Arena's code (it's open source) the example code won't look too familiar because the code has modified to make it a bit simpler and some of it is from a new update coming to Zeus Arena soon):
java code:
public class Game extends NativeActivity{
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
getWindow().takeSurface(null);
RegisterThis();
mGLSurfaceView = new KwaakView(this, this); // a custom made view for Zeus Arena
setContentView(mGLSurfaceView);
mGLSurfaceView.requestFocus();
mGLSurfaceView.setId(1);
}
//gives the native activity a copy of this object so it can call OnNativeMotion
public native int RegisterThis();
//loads the .so, change library name to whater your library is called
static {
System.loadLibrary("kwaakjni");
}
//called by the native activity when ever touch input is found
public void OnNativeMotion(int action, int x, int y, int source, int device_id) {
if(source == 1048584){ //touchpad
// Obtain MotionEvent object
long downTime = SystemClock.uptimeMillis();
long eventTime = SystemClock.uptimeMillis() + 100;
// List of meta states found here: developer.android.com/reference/android/view/KeyEvent.html#getMetaState()
int metaState = 0;
MotionEvent motionEvent = MotionEvent.obtain(
downTime,
eventTime,
action,
x,
(366-y),
metaState
);
mGLSurfaceView.onTouchPadEvent(motionEvent); //custom made method for dealing with touch input
}
else{
// Obtain MotionEvent object
long downTime = SystemClock.uptimeMillis();
long eventTime = SystemClock.uptimeMillis() + 100;
// List of meta states found here: developer.android.com/reference/android/view/KeyEvent.html#getMetaState()
int metaState = 0;
MotionEvent motionEvent = MotionEvent.obtain(
downTime,
eventTime,
action,
x,
y,
metaState
);
// Dispatch touch event to view
mGLSurfaceView.dispatchTouchEvent(motionEvent);
}
}
}
Native code:
#include <dlfcn.h>
#include <stdio.h>
#include <string.h>
#include <android/log.h>
#include <jni.h>
#include <errno.h>
#include <android_native_app_glue.h>
#include <time.h>
#include <unistd.h>
#include "quake_two_android_Quake2.h"
#define EXPORT_ME __attribute__ ((visibility("default")))
static JavaVM *jVM;
typedef unsigned char BOOL;
#define FALSE 0
#define TRUE 1
//|------------------------------------------------------ NATIVE ACTIVITY ------------------------------------------------------|
static jobject g_pActivity = 0;
static jmethodID javaOnNDKTouch = 0;
/**
* Our saved state data.
*/
struct TOUCHSTATE
{
int down;
int x;
int y;
};
/**
* Shared state for our app.
*/
struct ENGINE
{
struct android_app* app;
int render;
int width;
int height;
int has_focus;
//ugly way to track touch states
struct TOUCHSTATE touchstate_screen[64];
struct TOUCHSTATE touchstate_pad[64];
};
void attach(){
}
/**
* Process the next input event.
*/
static
int32_t
engine_handle_input( struct android_app* app, AInputEvent* event )
{
JNIEnv *jni;
(*jVM)->AttachCurrentThread(jVM, &jni, NULL);
struct ENGINE* engine = (struct ENGINE*)app->userData;
if( AInputEvent_getType(event) == AINPUT_EVENT_TYPE_MOTION )
{
int nPointerCount = AMotionEvent_getPointerCount( event );
int nSourceId = AInputEvent_getSource( event );
int n;
for( n = 0 ; n < nPointerCount ; ++n )
{
int nPointerId = AMotionEvent_getPointerId( event, n );
int nAction = AMOTION_EVENT_ACTION_MASK & AMotionEvent_getAction( event );
int nRawAction = AMotionEvent_getAction( event );
struct TOUCHSTATE *touchstate = 0;
if( nSourceId == AINPUT_SOURCE_TOUCHPAD )
touchstate = engine->touchstate_pad;
else
touchstate = engine->touchstate_screen;
if( nAction == AMOTION_EVENT_ACTION_POINTER_DOWN || nAction == AMOTION_EVENT_ACTION_POINTER_UP )
{
int nPointerIndex = (AMotionEvent_getAction( event ) & AMOTION_EVENT_ACTION_POINTER_INDEX_MASK) >> AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT;
nPointerId = AMotionEvent_getPointerId( event, nPointerIndex );
}
if( nAction == AMOTION_EVENT_ACTION_DOWN || nAction == AMOTION_EVENT_ACTION_POINTER_DOWN )
{
touchstate[nPointerId].down = 1;
}
else if( nAction == AMOTION_EVENT_ACTION_UP || nAction == AMOTION_EVENT_ACTION_POINTER_UP || nAction == AMOTION_EVENT_ACTION_CANCEL )
{
touchstate[nPointerId].down = 0;
}
if (touchstate[nPointerId].down == 1)
{
touchstate[nPointerId].x = AMotionEvent_getX( event, n );
touchstate[nPointerId].y = AMotionEvent_getY( event, n );
}
int handled = 0;
if( jni && g_pActivity ){
//send the event to java code, sends both touch screen and touch pad events, I think the java code will still intercept touch screen events
//so sending them probably isn't needed. If it is needed intercepting key events will be needed in native code as well.
(*jni)->CallVoidMethod( jni, g_pActivity, javaOnNDKTouch, nRawAction, touchstate[nPointerId].x, touchstate[nPointerId].y, nSourceId, 0 );
}
}
return 1;
}
return 0;
}
/**
* Process the next main command.
*/
static
void
engine_handle_cmd( struct android_app* app, int32_t cmd )
{
struct ENGINE* engine = (struct ENGINE*)app->userData;
switch( cmd )
{
case APP_CMD_SAVE_STATE:
// The system has asked us to save our current state. Do so if needed
break;
case APP_CMD_INIT_WINDOW:
// The window is being shown, get it ready.
if( engine->app->window != NULL )
{
engine->has_focus = 1;
}
break;
case APP_CMD_GAINED_FOCUS:
engine->has_focus = 1;
break;
case APP_CMD_LOST_FOCUS:
// When our app loses focus, we stop rendering.
engine->render = 0;
engine->has_focus = 0;
//engine_draw_frame( engine );
break;
}
}
/**
* This is the main entry point of a native application that is using
* android_native_app_glue. It runs in its own thread, with its own
* event loop for receiving input events and doing other things (rendering).
*/
void
android_main( struct android_app* state )
{
struct ENGINE engine;
// Make sure glue isn't stripped.
app_dummy();
memset( &engine, 0, sizeof(engine) );
state->userData = &engine;
state->onAppCmd = engine_handle_cmd;
state->onInputEvent = engine_handle_input;
engine.app = state;
//setup(state);
//JNIEnv *env;
//(*jVM)->AttachCurrentThread(jVM, &env, NULL);
if( state->savedState != NULL )
{
// We are starting with a previous saved state; restore from it.
}
// our 'main loop'
while( 1 )
{
// Read all pending events.
int ident;
int events;
struct android_poll_source* source;
// If not rendering, we will block forever waiting for events.
// If rendering, we loop until all events are read, then continue
// to draw the next frame.
while( (ident = ALooper_pollAll( 100, NULL, &events, (void**)&source) ) >= 0 )
//while( (ident = ALooper_pollAll( 100, NULL, &events, (void**)&source) ) >= 0 )
{
// Process this event.
// This will call the function pointer android_app:nInputEvent() which in our case is
// engine_handle_input()
if( source != NULL )
{
source->process( state, source );
}
// Check if we are exiting.
if( state->destroyRequested != 0 )
{
return;
}
usleep(17000); //17 miliseconds
}
}
}
jint EXPORT_ME
JNICALL Java_quake_two_android_Quake2_RegisterThis(JNIEnv * env, jobject clazz){
g_pActivity = (jobject)(*env)->NewGlobalRef(env, clazz);
return 0;
}
jint EXPORT_ME JNICALL
JNI_OnLoad(JavaVM * vm, void * reserved)
{
JNIEnv *env;
jVM = vm;
if((*vm)->GetEnv(vm, (void**) &env, JNI_VERSION_1_4) != JNI_OK)
{
return -1;
}
const char* interface_path = "quake/two/android/Quake2";
jclass java_activity_class = (*env)->FindClass( env, interface_path );
javaOnNDKTouch = (*env)->GetMethodID( env, java_activity_class, "OnNativeMotion", "(IIIII)V");
javaOnNDKKey = (*env)->GetMethodID( env, java_activity_class, "OnNativeKeyPress", "(III)V");
return JNI_VERSION_1_4;
}
Licences:
/*
* Copyright (c) 2011, Sony Ericsson Mobile Communications AB.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * 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.
* * Neither the name of the Sony Ericsson Mobile Communications AB nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS 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 COPYRIGHT HOLDER OR 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.
*/
/*
* This example uses the NDK and a helper library available in the NDK called 'native app glue',
* which is available in %NDK_ROOT/source/android/native_app_glue. If you are new to NDK or to
* the NativeActivity, look through the native_app_glue source to see how you should set up your
* native app and handle callbacks and messages from Android. Note that the callbacks registered
* in the ANativeActivity_onCreate() entry-point must return in a timely manner, as does
* ANativeActivity_onCreate() itself. The Native App Glue does this by creating a pipe() and
* synchronization objects to handle communication between the Android, the NativeActivity and
* the game/sample logic.
*
* In this example, we read the 'pointer' information from touch events from both the touch-screen
* and the touch-pad (if available). We store their positions and state (up or down), then draw
* the touch positions scaled to the screen.
*
* Although we are using hard-coded values for the touch-pad resultion, you can and should read
* those values at runtime in java by enumerating InputDevices and finding the touchpad device.
*
*/
/*
* Kwaak3 - Java to quake3 interface
* Copyright (C) 2010 Roderick Colenbrander
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
Click to expand...
Click to collapse
ninjatjj said:
Hi,
I am trying to follow this a little, I can access the touchpad just fine in my nativeactivity but I have lost all access to onKeyDown and onKeyUp using the Sony tutorial. Your code mentions OnNativeKeyPress but I can't see you calling this anywhere, is this expected?
Thanks for any pointers you can offer!
Click to expand...
Click to collapse
If you're still searching:
Code:
static
int32_t
engine_handle_input( struct android_app* app, AInputEvent* event )
{
JNIEnv *jni;
(*jVM)->AttachCurrentThread(jVM, &jni, NULL);
struct ENGINE* engine = (struct ENGINE*)app->userData;
if( AInputEvent_getType(event) == AINPUT_EVENT_TYPE_MOTION )
{
int nPointerCount = AMotionEvent_getPointerCount( event );
int nSourceId = AInputEvent_getSource( event );
int n;
jboolean newTouch = JNI_TRUE;
for( n = 0 ; n < nPointerCount ; ++n )
{
int nPointerId = AMotionEvent_getPointerId( event, n );
int nAction = AMOTION_EVENT_ACTION_MASK & AMotionEvent_getAction( event );
int nRawAction = AMotionEvent_getAction( event );
struct TOUCHSTATE *touchstate = 0;
if( nSourceId == AINPUT_SOURCE_TOUCHPAD )
touchstate = engine->touchstate_pad;
else
touchstate = engine->touchstate_screen;
if( nAction == AMOTION_EVENT_ACTION_POINTER_DOWN || nAction == AMOTION_EVENT_ACTION_POINTER_UP )
{
int nPointerIndex = (AMotionEvent_getAction( event ) & AMOTION_EVENT_ACTION_POINTER_INDEX_MASK) >> AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT;
nPointerId = AMotionEvent_getPointerId( event, nPointerIndex );
}
if( nAction == AMOTION_EVENT_ACTION_DOWN || nAction == AMOTION_EVENT_ACTION_POINTER_DOWN )
{
touchstate[nPointerId].down = 1;
}
else if( nAction == AMOTION_EVENT_ACTION_UP || nAction == AMOTION_EVENT_ACTION_POINTER_UP || nAction == AMOTION_EVENT_ACTION_CANCEL )
{
touchstate[nPointerId].down = 0;
}
if (touchstate[nPointerId].down == 1)
{
touchstate[nPointerId].x = AMotionEvent_getX( event, n );
touchstate[nPointerId].y = AMotionEvent_getY( event, n );
}
int handled = 0;
if( jni && g_pActivity ){
(*jni)->CallVoidMethod( jni, g_pActivity, javaOnNDKTouch, nRawAction, touchstate[nPointerId].x, touchstate[nPointerId].y, nSourceId, 0, newTouch);
}
newTouch = JNI_FALSE;
}
return 1;
}
else if (AInputEvent_getType(event) == AINPUT_EVENT_TYPE_KEY){
int action = AKeyEvent_getAction(event);
int keyCode = AKeyEvent_getKeyCode(event);
if(jni && g_pActivity){
if((*jni)->ExceptionCheck(jni)) {
(*jni)->ExceptionDescribe(jni);
(*jni)->ExceptionClear(jni);
}
(*jni)->CallIntMethod(jni, g_pActivity, javaOnNDKKey, action, keyCode, AKeyEvent_getMetaState(event));
}
}
return 0;
}
taken from: https://play.google.com/store/apps/details?id=zeus.arena.source&hl=en&rdid=zeus.arena.source
Btw: The application should not be run. It doesn't work and will waste your time. Open the APK with a file browser and get the 7z out of the assets manually. There are plenty of places to copy that code from, but I guess it wasn't on Sony's developer page :/
Thanks for the code snippet, I managed to get this working already: github.com/ninjatjj/btjoypad
ninjatjj said:
Thanks for the code snippet, I managed to get this working already: github.com/ninjatjj/btjoypad
Click to expand...
Click to collapse
Alright. I just added support to reicast and stumbled on this trying to find the Sony page that moved. This is a very basic copy paste implementation. It should really explain the what and why if it's going to leave out so much.
I'll take a look at how you did it shortly and let you know if there's anything I did that might help. I rewrote a lot of the native to allow skipping over the input for everything but the Play.
twistedumbrella said:
Alright. I just added support to reicast and stumbled on this trying to find the Sony page that moved. This is a very basic copy paste implementation. It should really explain the what and why if it's going to leave out so much.
I'll take a look at how you did it shortly and let you know if there's anything I did that might help. I rewrote a lot of the native to allow skipping over the input for everything but the Play.
Click to expand...
Click to collapse
Much appreciated - I was actually checking out reicast yesterday but I don't have my MvC rom with me (on holiday) - are you getting decent performance on the xperia play? That is one device that definitely needs a hardware upgrade soon.
ninjatjj said:
Much appreciated - I was actually checking out reicast yesterday but I don't have my MvC rom with me (on holiday) - are you getting decent performance on the xperia play? That is one device that definitely needs a hardware upgrade soon.
Click to expand...
Click to collapse
I saw you're using the full native version so not much I did will have any benefit. I use the native just to pass back to the Java for ease of use now since everything else is Java-based.
It is starting to. It doesn't work on Gingerbread due to memory allocation issues with the build but I get the cutscenes at about 60 fps and gameplay is anywhere from 20 to 40 depending on how intense the scene is. That's what I've been working on, though. Every little option to squeeze another 10 or so out of it.
How to Send and Receive MMS in Android:
This is a guide I am creating to help all of you out there making messaging apps like sliding messaging, hopefully it will help someone at least get started in the right direction! This guide will not be a guide on how to read MMS messages, which is actually very simple. Here is a tutorial on that which will get you started: How to read MMS data in Android (The first answer is a lifesaver, it will get you where you need to go). Now onto sending and receiving!
First off, I want to say that all of this may seem very daunting, especially to a first time, independent Android app developer like I was when this started out, trying to manange time between this and college studies! A messaging app can be extremely difficult to write since there is no supported API to it at all and you have to go through lines and lines trying to decipher stock source code. Stick to it and you can definitely get there eventually I do feel that Google should add this type of thing into their API and document it though, so as to make it easier for us 3rd party developers without a giant team behind us! Come on Google!
Here's the steps in the order that I went through:
1) You need to import a lot of internal android classes, which can all be found here: GrepCode
Here are all of the files you will need to grab (there may be more, but I was able to in the end modify all of these files so that they don't depend on others such as taking out unnecessary lines of code, etc)
Code:
android.annotation.SdkConstant.java
android.database.sqlite.SqliteWrapper.java
android.net.ConnectivityManager.java
android.net.DhcpInfoInternal.java
android.net.IConnectivityManager.java
android.net.INetworkPolicyListener.java
android.net.InetworkPolicyManager.java
android.net.LinkAddress.java
android.net.LinkCapabilities.java
android.net.LinkProperties.java
android.net.NetworkIdentity.java
android.net.NetworkPolicy.java
android.net.NetworkPolicyManager.java
android.net.NetowkrQuotaInfo.java
android.net.NetowrkState.java
android.net.NetworkTemplate.java
android.net.NetworkUtils.java
android.net.ProxyProperties.java
android.net.RouteInfo.java
android.provider.Downloads.java
android.provider.Telephony.java
com.android.internal.annotations.VisibleForTesting.java
com.android.internal.net.LegacyVpnInfo.java
com.android.internal.net.VpnConfig.java
com.android.internal.net.VpnProfile.java
com.android.internal.telephony.EncodeException.java
com.android.internal.telephony.GsmAlphabet.java
com.android.internal.telephony.IccUtils.java
com.android.internal.telephony.SmsConstants.java
com.android.internal.telephony.TelephonyProperties.java
com.android.internal.util.ArrayUtils.java
com.android.internal.util.Objects.java
com.android.internal.util.Preconditions.java
com.android.mms.MmsConfig.java
com.android.mms.transaction.AbstractRetryScheme.java
com.android.mms.transaction.DefaultRetryScheme.java
com.android.mms.transaction.HttpUtils.java
com.android.mms.transaction.MmsSystemEventReceiver.java
com.android.mms.transaction.NotificationTransaction.java
com.android.mms.transaction.Observable.java
com.android.mms.transaction.Observer.java
com.android.mms.transaction.ProgressCalbackEntity.java
com.android.mms.transaction.PushReceiver.java
com.android.mms.transaction.ReadRecTransaction.java
com.android.mms.transaction.RetrieveTransaction.java
com.android.mms.transaction.RetryScheduler.java
com.android.mms.transaction.SendTransaction.java
com.android.mms.transaction.Transaction.java
com.android.mms.transaction.TransactionBundle.java
com.android.mms.transaction.TransactionService.java
com.android.mms.transaction.TransactionSettings.java
com.android.mms.transaction.TransactionState.java
com.android.mms.util.DownloadManager.java
com.android.mms.util.RateController.java
com.android.mms.util.SendingprogressTokenManager.java
com.google.android.collect.Sets.java
com.google.android.mms.ContentType.java
com.google.android.mms.InvalidHeaderValueException.java
com.google.android.mms.MmsException.java
com.google.android.mms.pdu.AcknowledgeInd.java
com.google.android.mms.pdu.Base64.java
com.google.android.mms.pdu.CharacterSets.java
com.google.android.mms.pdu.DeliveryInd.java
com.google.android.mms.pdu.EncodedStringValue.java
com.google.android.mms.pdu.GenericPdu.java
com.google.android.mms.pdu.MultimediaMessagePdu.java
com.google.android.mms.pdu.NotifictionInd.java
com.google.android.mms.pdu.NotifyRespInd.java
com.google.android.mms.pdu.PduBody.java
com.google.android.mms.pdu.PduComposer.java
com.google.android.mms.pdu.PduContentTypes.java
com.google.android.mms.pdu.PduHeaders.java
com.google.android.mms.pdu.PduParser.java
com.google.android.mms.pdu.PduPart.java
com.google.android.mms.pdu.PduPersister.java
com.google.android.mms.pdu.QuotedPrintable.java
com.google.android.mms.pdu.ReadOrigInd.java
com.google.android.mms.pdu.RetrieveConfjava
com.google.android.mms.pdu.SendConf.java
com.google.android.mms.pdu.SendRequ.java
com.google.android.mms.util.AbstractCache.java
com.google.android.mms.util.DownloadDrmHelper.java
com.google.android.mms.util.DrmConvertSession.jav
com.google.android.mms.util.PduCache.java
com.google.android.mms.util.PduCacheEntry.java
com.google.android.mms.util.SqliteWrapper.java
Whew, that was a ton of typing, hopefully its worth it! Haha. May be some typos in there too, but I'm sure you will be able to find the right one.
I know that's a lot, but I found it to be the easiest way of doing things. I'm not actually sure that all of these are required, and I may have missed a couple or added a couple extras, but you get the picture at least and thats most of them. You'll probably only end up using about 10% of those files, but since they all have others imported, I chose to import them all instead of the possibility of messing something up that was necessary. Like I said, its a lot, but almost all of them are really small so they won't increase the size of your app by more then half a megabyte at max (not sure on an exact number) so that's not really something to worry about. If you can make it by this daunting task, you should be good to go for the rest of the tutorial.
2) You need an MMS Part class that will be used to store MMS data you are going to be sending. Here is what mine looks like:
Code:
public class MMSPart {
public String Name = "";
public String MimeType = "";
public byte[] Data;
}
Just copy and paste this class.
Very straightforward as to what this will do for you, it is the actual file that we will be sending through out http connection. MimeType is the type of object that the part is, for example image/png or text/plain are the 2 that I use.
3) Next up, we need a class that will assist us in finding the system APNs. Here is mine:
Code:
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import android.content.Context;
import android.database.Cursor;
import android.net.Uri;
import android.provider.Telephony;
import android.text.TextUtils;
import android.widget.Toast;
public class APNHelper {
public APNHelper(final Context context) {
this.context = context;
}
@SuppressWarnings("unchecked")
public List<APN> getMMSApns() {
final Cursor apnCursor = this.context.getContentResolver().query(Uri.withAppendedPath(Telephony.Carriers.CONTENT_URI, "current"), null, null, null, null);
if ( apnCursor == null ) {
return Collections.EMPTY_LIST;
} else {
final List<APN> results = new ArrayList<APN>();
if ( apnCursor.moveToFirst() ) {
do {
final String type = apnCursor.getString(apnCursor.getColumnIndex(Telephony.Carriers.TYPE));
if ( !TextUtils.isEmpty(type) && ( type.equalsIgnoreCase("*") || type.equalsIgnoreCase("mms") ) ) {
final String mmsc = apnCursor.getString(apnCursor.getColumnIndex(Telephony.Carriers.MMSC));
final String mmsProxy = apnCursor.getString(apnCursor.getColumnIndex(Telephony.Carriers.MMSPROXY));
final String port = apnCursor.getString(apnCursor.getColumnIndex(Telephony.Carriers.MMSPORT));
final APN apn = new APN();
apn.MMSCenterUrl = mmsc;
apn.MMSProxy = mmsProxy;
apn.MMSPort = port;
results.add(apn);
Toast.makeText(context, mmsc + " " + mmsProxy + " " + port, Toast.LENGTH_LONG).show();
}
} while ( apnCursor.moveToNext() );
}
apnCursor.close();
return results;
}
}
private Context context;
}
Also, you will need an APN class, here is what that one looks like:
Code:
public class APN {
public String MMSCenterUrl = "";
public String MMSPort = "";
public String MMSProxy = "";
public APN(String MMSCenterUrl, String MMSPort, String MMSProxy)
{
this.MMSCenterUrl = MMSCenterUrl;
this.MMSPort = MMSPort;
this.MMSProxy = MMSProxy;
}
public APN()
{
}
}
Just copy and paste these classes.
The problem with this is that in Android 4.0 and up, Google blocks 3rd party access to APNs (though from trial and error I believe that you can access them on some touchwiz roms/phones, but not all). To get around this, in Sliding Messaging I had to have users input this information manually, so if you have users who will be on an API higher than 14 (ICS), you will need to have this option or it WILL ABSOLUTELY NOT work. At all. A little further down I'll show you how to implement so that you can try and find APNs, and if it fails then you can set them manually.
4) You need to include these permissions in the app for things to work and not get FCs because of lack of permissions:
Code:
<uses-permission android:name="android.permission.CHANGE_NETWORK_STATE" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.RECEIVE_MMS" />
<uses-permission android:name="android.permission.READ_SMS"/>
<uses-permission android:name="android.permission.WRITE_SMS"/>
<uses-permission android:name="android.permission.WRITE_APN_SETTINGS" />
Just put them in the AndroidManifest with the rest. If you can't figure this step out maybe its time to start with something a little easier
As for the last one, write apn settings, I know for a fact you will not be able to compile your app through eclipse if you include it and you are working with Android 4.0+. It can only be applied to system apps only for API 14 and up and I don't know if you can include it without error if you are only targeting Gingerbread or not. More then likely you will have to take it out if you are not compiling your app as a system app by building it with the rest of the android source.
Please Note: I'm not actually sure that the internet permission is necessary, but the stock google app has it so I decided to include it.
5) Ok, now we have all of our classes we will use and it is time to send an MMS message. I will just copy the 2 functions that I use to do this directly below, and then explain them after that. Here they are:
Code:
public void sendMMS(final String recipient, final MMSPart[] parts)
{
ConnectivityManager mConnMgr = (ConnectivityManager)getSystemService(Context.CONNECTIVITY_SERVICE);
final int result = mConnMgr.startUsingNetworkFeature(ConnectivityManager.TYPE_MOBILE, "enableMMS");
if (result != 0)
{
IntentFilter filter = new IntentFilter();
filter.addAction(ConnectivityManager.CONNECTIVITY_ACTION);
BroadcastReceiver receiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
if (!action.equals(ConnectivityManager.CONNECTIVITY_ACTION))
{
return;
}
@SuppressWarnings("deprecation")
NetworkInfo mNetworkInfo = (NetworkInfo) intent.getParcelableExtra(ConnectivityManager.EXTRA_NETWORK_INFO);
if ((mNetworkInfo == null) || (mNetworkInfo.getType() != ConnectivityManager.TYPE_MOBILE_MMS))
{
return;
}
if (!mNetworkInfo.isConnected())
{
return;
} else
{
sendData(recipient, parts);
unregisterReceiver(this);
}
}
};
registerReceiver(receiver, filter);
} else
{
sendData(recipient, parts);
}
}
This function is fairly simple. All it does is tell the sytem that we are going to start using a mobile connection and it should connect using the term "enableMMS" so that we start the right type of connection. The variable result is set to the type of connection already active when we call this, and we are looking for it to be set to 0, meaning that our apns are already active. If this is the case, you can just skip right ahead to sending the MMS message. More than likely though, apns will not be active when you start the call, so you need to listen for a change in the connectivity through a broadcast receiver. That is what is going on inside the block of code where (result != 0). Once the receiver gets the correct type of connection, it calls the below function of sendData and unregisters itself so we don't leak receivers.
When the message is ready to be sent:
Code:
public void sendData(final String recipient, final MMSPart[] parts)
{
final Context context = this;
new Thread(new Runnable() {
@Override
public void run() {
final SendReq sendRequest = new SendReq();
final EncodedStringValue[] phoneNumber = EncodedStringValue.extract(recipient);
if (phoneNumber != null && phoneNumber.length > 0)
{
sendRequest.addTo(phoneNumber);
}
final PduBody pduBody = new PduBody();
if (parts != null)
{
for (MMSPart part : parts)
{
if (part != null)
{
try
{
final PduPart partPdu = new PduPart();
partPdu.setName(part.Name.getBytes());
partPdu.setContentType(part.MimeType.getBytes());
partPdu.setData(part.Data);
pduBody.addPart(partPdu);
} catch (Exception e)
{
}
}
}
}
sendRequest.setBody(pduBody);
final PduComposer composer = new PduComposer(context, sendRequest);
final byte[] bytesToSend = composer.make();
List<APN> apns = new ArrayList<APN>();
try
{
APNHelper helper = new APNHelper(context);
apns = helper.getMMSApns();
} catch (Exception e)
{
APN apn = new APN(sharedPrefs.getString("mmsc_url", ""), sharedPrefs.getString("mms_port", ""), sharedPrefs.getString("mms_proxy", ""));
apns.add(apn);
}
try {
HttpUtils.httpConnection(context, 4444L, apns.get(0).MMSCenterUrl, bytesToSend, HttpUtils.HTTP_POST_METHOD, !TextUtils.isEmpty(apns.get(0).MMSProxy), apns.get(0).MMSProxy, Integer.parseInt(apns.get(0).MMSPort));
ConnectivityManager mConnMgr = (ConnectivityManager)getSystemService(Context.CONNECTIVITY_SERVICE);
mConnMgr.startUsingNetworkFeature(ConnectivityManager.TYPE_MOBILE_MMS, "enableMMS");
IntentFilter filter = new IntentFilter();
filter.addAction(ConnectivityManager.CONNECTIVITY_ACTION);
BroadcastReceiver receiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
Cursor query = context.getContentResolver().query(Uri.parse("content://mms"), new String[] {"_id"}, null, null, "date desc");
query.moveToFirst();
String id = query.getString(query.getColumnIndex("_id"));
query.close();
ContentValues values = new ContentValues();
values.put("msg_box", 2);
String where = "_id" + " = '" + id + "'";
context.getContentResolver().update(Uri.parse("content://mms"), values, where, null);
context.unregisterReceiver(this);
}
};
registerReceiver(receiver, filter);
} catch (Exception e) {
Cursor query = context.getContentResolver().query(Uri.parse("content://mms"), new String[] {"_id"}, null, null, "date desc");
query.moveToFirst();
String id = query.getString(query.getColumnIndex("_id"));
query.close();
ContentValues values = new ContentValues();
values.put("msg_box", 5);
String where = "_id" + " = '" + id + "'";
context.getContentResolver().update(Uri.parse("content://mms"), values, where, null);
((Activity) context).getWindow().getDecorView().findViewById(android.R.id.content).post(new Runnable() {
@Override
public void run() {
Toast.makeText(context, "MMS Error", Toast.LENGTH_SHORT).show();
}
});
}
}
}).start();
}
This function is where all the magic happens. First, we need to create a new thread to run on because you can't access an HTTP connection on the UI thread (I ran into this error the first time around since I had never worked with data connections before). Once that thread is running, we create a new SendReq object which is what we will be sending through the http request. You will need to encode your recipient (this part of the code can also be used to support group messaging, but I have not included that code, you will need to write it yourself if you so choose. You can attach multiple addresses by calling sendRequest.addTo() multiple times) and attach it to the sendRequest. Next up, attach your MMSPart file using a for loop. Here is how to initialize that MMSPart to whatever you want to include in it:
Code:
Bitmap b = ________; // Whatever your bitmap is that you want to send
ByteArrayOutputStream stream = new ByteArrayOutputStream();
b.compress(Bitmap.CompressFormat.PNG, 100, stream);
byte[] byteArray = stream.toByteArray();
MMSPart[] parts = new MMSPart[1];
parts[0] = new MMSPart();
parts[0].Name = "Image";
parts[0].MimeType = "image/png";
parts[0].Data = byteArray;
Using this, you can also add text to the request with the mimetype "text/plain" and encode text as a byte array so that it can be attached in the same mannor. I won't post that code, you should be able to figure it out.
Back to the original sendData function, we can now attach the pduBody which includes all of the byte arrays that we want to send, in this specific case, just an image and no text. After that, all that is left is to actually create the byte array to send, bytesToSend, which can be done very easily through internal classes.
Now we are ready to make the actual send request, which must be done through the users APNs using an http_post method. But first, we need to retrieve APNs. As I said earlier, if a user is running an Android version less then 4.0, then the app should be able to get the system defined APNs through the APN helper class that I provided (still no promises on this though as it has been untested, I don't have an old phone anymore to test on). Surround the request to APNHelper with a try/catch block so that we can catch the error of insigificant permissions, and then apply your user defined custom APNs that will actually work. I have users enter these in settings, thats why you see them being set through sharedPreference strings. you can manually type in whatever strings you want to for your network during testing phases though and that should work fine, just remember different networks use different APNs.
Ahh finally we are ready to make our Http request. This is fairly simple, as it is just one line of code which you should be able to directly take from my example if you initialize apns the same way I have:
Code:
HttpUtils.httpConnection(context, 4444L, apns.get(0).MMSCenterUrl, bytesToSend, HttpUtils.HTTP_POST_METHOD, !TextUtils.isEmpty(apns.get(0).MMSProxy), apns.get(0).MMSProxy, Integer.parseInt(apns.get(0).MMSPort));
You can look at javadocs for this function if you want to know what all of this is, but just know that it will make a post request through your MMSC you have defined and then send the message through the proxy and port (if those are needed, some carriers do not require them). Also, remember to add a catch block around this in case the sending fails for whatever reason, and tell your users that the request has failed. In this example, when the message fails to send, it moves the message to msg_box = 5, which is where MMS with errors are stored. This is simply updating the database with a new location for our recently failed message.
Last thing we have to do is listen for when the message has sent. This is where I've completely made up a function, and it functions correctly for ALMOST everyone (the only people not so far are Sprint users, and I haven't found a way around it yet). To do this, after our request, we register a new connectivity receiver just as we did when we were preparing to send a message, and this time around, when the state of the phones internet connection changes - usually dropping out of the APN request space I believe - we can move the message from the outbox to the sent message box and boom, message sent.
Yay, you should have just been able to send your first MMS message! (Unless I forgot a step in there lol) That's exciting stuff! Notice now though, that although you sent the message (and you have coded in how to change which message box the message is in), the message didn't get saved to the database so you can't actually see it in the app. That doesn't do most of us any good, so next step is how to save it to the SMS database on your phone.
6) Here we save the message, I'll just list out the functions again and then explain them after that:
Code:
public static Uri insert(Context context, String[] to, String subject, byte[] imageBytes)
{
try
{
Uri destUri = Uri.parse("content://mms");
// Get thread id
Set<String> recipients = new HashSet<String>();
recipients.addAll(Arrays.asList(to));
long thread_id = Telephony.Threads.getOrCreateThreadId(context, recipients);
// Create a dummy sms
ContentValues dummyValues = new ContentValues();
dummyValues.put("thread_id", thread_id);
dummyValues.put("body", "Dummy SMS body.");
Uri dummySms = context.getContentResolver().insert(Uri.parse("content://sms/sent"), dummyValues);
// Create a new message entry
long now = System.currentTimeMillis();
ContentValues mmsValues = new ContentValues();
mmsValues.put("thread_id", thread_id);
mmsValues.put("date", now/1000L);
mmsValues.put("msg_box", 4);
//mmsValues.put("m_id", System.currentTimeMillis());
mmsValues.put("read", 1);
mmsValues.put("sub", subject);
mmsValues.put("sub_cs", 106);
mmsValues.put("ct_t", "application/vnd.wap.multipart.related");
if (imageBytes != null)
{
mmsValues.put("exp", imageBytes.length);
} else
{
mmsValues.put("exp", 0);
}
mmsValues.put("m_cls", "personal");
mmsValues.put("m_type", 128); // 132 (RETRIEVE CONF) 130 (NOTIF IND) 128 (SEND REQ)
mmsValues.put("v", 19);
mmsValues.put("pri", 129);
mmsValues.put("tr_id", "T"+ Long.toHexString(now));
mmsValues.put("resp_st", 128);
// Insert message
Uri res = context.getContentResolver().insert(destUri, mmsValues);
String messageId = res.getLastPathSegment().trim();
// Create part
if (imageBytes != null)
{
createPartImage(context, messageId, imageBytes);
}
// Create addresses
for (String addr : to)
{
createAddr(context, messageId, addr);
}
//res = Uri.parse(destUri + "/" + messageId);
// Delete dummy sms
context.getContentResolver().delete(dummySms, null, null);
return res;
}
catch (Exception e)
{
e.printStackTrace();
}
return null;
}
Code:
private static Uri createPartImage(Context context, String id, byte[] imageBytes) throws Exception
{
ContentValues mmsPartValue = new ContentValues();
mmsPartValue.put("mid", id);
mmsPartValue.put("ct", "image/png");
mmsPartValue.put("cid", "<" + System.currentTimeMillis() + ">");
Uri partUri = Uri.parse("content://mms/" + id + "/part");
Uri res = context.getContentResolver().insert(partUri, mmsPartValue);
// Add data to part
OutputStream os = context.getContentResolver().openOutputStream(res);
ByteArrayInputStream is = new ByteArrayInputStream(imageBytes);
byte[] buffer = new byte[256];
for (int len=0; (len=is.read(buffer)) != -1;)
{
os.write(buffer, 0, len);
}
os.close();
is.close();
return res;
}
Code:
private static Uri createAddr(Context context, String id, String addr) throws Exception
{
ContentValues addrValues = new ContentValues();
addrValues.put("address", addr);
addrValues.put("charset", "106");
addrValues.put("type", 151); // TO
Uri addrUri = Uri.parse("content://mms/"+ id +"/addr");
Uri res = context.getContentResolver().insert(addrUri, addrValues);
return res;
}
I didn't write these functions, credit to Vodemki on Stack Overflow. All you have to do is call the insert function and it will do all of the work for you. Send that fuction the activity context, a string array of the numbers who you sent the message to, a subject for the message, and the same byte array of the image you passed earlier to your MMS part file. You can look at the code for inserting the image and reproduce this to do the same for a string of text you are sending with the image.
You will want to actually call these funtions right after you push the send button for example, to first save the message and then you can send it and update it later after it has sent or failed to send.
Ok, now that will successfully allow you to put the MMS message in the database. For some reason, these messages just show up as blank messages in the stock app, not sure why, but Sliding Messaging is able to read them at least, so depending on your implementation, yours should be able to do so as well.
Now all this function does is insert the image. I don't want to give away all of my secrets for Sliding Messaging so you will have to go through and find out how to insert text and group message addresses yourself, I think that's fair enough and if you've made it this far in your app, you shouldn't have to much of a problem with it
Wow, we just sent and saved that message to our phone, that means we are halfway home! (Nice rhyme huh )
Onto receiving in the next post.
7) First up, you'll have to know a little something about querying the mms-sms database, read up on that first if you haven't already (first link I have posted at the top). This is all out of my brain and what I was able to do from experience, and seems to work very well... you won't find a tutorial anywhere else on the internet of how to do this (at least I couldn't anywhere). I'm not going to just give you a simple function for this one though, you will have to adapt the code according to how you want it to be used. Once again, here is the code you will need to use:
Code:
id = ________; // this is the id of your MMS message that you are going to search for
Cursor locationQuery = context.getContentResolver().query(Uri.parse("content://mms/"), new String[] {"m_size", "exp", "ct_l", "_id"}, "_id=?", new String[]{id}, null);
locationQuery.moveToFirst();
String exp = "1";
String size = "1";
try
{
size = locationQuery.getString(locationQuery.getColumnIndex("m_size"));
exp = locationQuery.getString(locationQuery.getColumnIndex("exp"));
} catch (Exception f)
{
}
String location = locationQuery.getString(locationQuery.getColumnIndex("ct_l"));
The above function is where you query the message you are interested in downloading, and get data from it such as expiration date, size, and location on a server. The date will be a date in milliseconds that you can format accordingly and display to the screen if you so choose, and the size will be in bytes, but you will probably want to convert to kb by dividing by 1000. As for the location, that is what we are going to use to download the message:
Code:
List<APN> apns = new ArrayList<APN>();
try
{
APNHelper helper = new APNHelper(context);
apns = helper.getMMSApns();
} catch (Exception e)
{
APN apn = new APN(sharedPrefs.getString("mmsc_url", ""), sharedPrefs.getString("mms_port", ""), sharedPrefs.getString("mms_proxy", ""));
apns.add(apn);
}
Get your APNs the same way as we did before when sending, I won't explain this again.
Code:
try {
byte[] resp = HttpUtils.httpConnection(
context, SendingProgressTokenManager.NO_TOKEN,
downloadLocation, null, HttpUtils.HTTP_GET_METHOD,
!TextUtils.isEmpty(apns.get(0).MMSProxy),
apns.get(0).MMSProxy,
Integer.parseInt(apns.get(0).MMSPort));
RetrieveConf retrieveConf = (RetrieveConf) new PduParser(resp).parse();
PduPersister persister = PduPersister.getPduPersister(context);
Uri msgUri = persister.persist(retrieveConf, Inbox.CONTENT_URI, true,
true, null);
ContentValues values = new ContentValues(1);
values.put(Mms.DATE, System.currentTimeMillis() / 1000L);
SqliteWrapper.update(context, context.getContentResolver(),
msgUri, values, null, null);
SqliteWrapper.delete(context, context.getContentResolver(),
Uri.parse("content://mms/"), "thread_id=? and _id=?", new String[] {threadIds, msgId});
((Activity) context).getWindow().getDecorView().findViewById(android.R.id.content).post(new Runnable() {
@Override
public void run() {
Toast.makeText(context, "Message Received", Toast.LENGTH_SHORT).show();
}
});
} catch (Exception e) {
e.printStackTrace();
((Activity) context).getWindow().getDecorView().findViewById(android.R.id.content).post(new Runnable() {
@Override
public void run() {
Toast.makeText(context, "Download Failed", Toast.LENGTH_SHORT).show();
}
});
}
This request is much the same as the previous we used when sending the message, except this time we use a get request instead of post, send the funtion a null where before it was out bytesToSend array, and receive a byte array from it. Once you have received this byte array, you can easily use internal classes to save the message to the database (these messages are saved 100% correctly and will show up in the stock app once downloaded, unlike before) and then delete the old message stored there that only had location data for the message to be downloaded. I won't go into much more detail then that, it should be easy enough to understand just from the code.
I have this function tied to a button press, but in theory you should also be able to register a WAP_PUSH_RECEIVED broadcast receiver that saves the message automatically when your phone gets one (WAP is the type of broadcast you get when receiving MMS data). I'm not going to post anything here about that because I haven't done it, but if you want, all of the code is the same and you can easily base it happening off of a setting in your app and the network connectivity state when the message is received.
Another Note: There is probably a way to use the above code with persisters etc to be able to save an MMS to the database in a much easier way, but I haven't looked into that at all, mostly because the code I posted in step 6 works just fine and I don't see a need for much else if you can handle just copying and pasting those functions.
Now I think that may be it... we covered sending MMS, saving sent MMS and finally receiving MMS! Fun stuff!
In conclusion, I just want to give something back to the community that has given my app, Sliding Messaging, so much love over the past couple of months. I hope this helps someone out there, because as far as I know and could find, this is the only full MMS tutorial there is for Android (and the only one at all for actually downloading MMS from the internet) and should get everything done that you need! Ask any questions you want and feel free to PM me if you need more assistance.
Now no one out there should have any excuse for not including at least some MMS support in your messaging apps! Hopefully this will save you from days upon days of research to no avail on the subject and unjust criticism from people who know absolutely nothing about the topic like I did (Had one person tell me to f-off and if I couldn't get it working, then his $0.99 entitled him to say that I needed to hire someone else who was actually competent at programming and spend all of my college savings so that person would do it for me ... to that person I say, HAHA, I did it. lol) Your users will be happy people if you get this implemented!
Also guys, I wouldn't mind getting a little credit if anyone out there uses this tutorial to get things up and running, but its not necessary if you don't want! Instead you could just buy me a beer and donate to me To the people who actually read through these last 3 paragraphs, I say thank you! Hopefully this tutorial can at least get you started, wish I had it reference when I was adding the support in!
Sources:
1) How to Read MMS Data in Android
2) Android Add MMS to Database
3) MMS in Android: Part 1
4) GrepCode
5) My own experience
Cheers, and good luck to anyone trying this out this is my first tutorial so if you find anything missing or any typos, let me know!
grepcode files missing
I tried to download classes from your given link 'Grepcode'.
It is missing the contents of com.google folder even it do not contain folder named google in com folder. (i tried different versions like 4.2.1,4.2.2 etc .. still same problem )
can you give me any other link to download classes ?
Cool, thanks.
(You might want to add [Guide] to your thread title. For now it looks like a question. )
Thanks very much for doing this. Had to do a bit of searching on the internet for some of the files, and some of the functions needed slight modification but this was enough of a push in the right direction that I've got mms sending working. Great job and good luck with the continued success of your app!
Also- your responses on the android marketplace to people leaving stupid feedback make me laugh!
Getting Connection Refused error
Hi,
I tried your code. I am getting the APN settings perfectly. But a soon as the httpClient tries to connect to my MMSC it throws ConnectionRefused Error.
Please help me. Its been head-scratcher 3 days in a row now.
Thanks. in advance.
Please help
I am not able to import the classes with name
com.android.mms.*
and
com.google.*
I downloaded the android-4.2.2_r1.jar and also android-4.4_r1.jar
But these jars do not contain all required classes.
Please help how to add these classes.
Regarding the missing classes
Hi,
I am looking for a solution to programmatically sending MMS through my Android Application.
Some classess are missing(com.google.*) in the jar downloaded from grepcode.
Can anybody help in this regard?
Thanks.
Missing Function
hi
When I want to insert MMS message in DB, eclipse says "Telephony.Threads.getOrCreateThreadId" is undefined!
What should I do?
Thanks
CursorIndexOutOfBoundsException
hi ,
I get exception like this
android.database.CursorIndexOutOfBoundsException: Index 0 requested, with a size of 0
AS per yourCode
Cursor query = context.getContentResolver().query(Uri.parse("content://mms"), new String[]
{
"_id"
}, null, null, "date desc");
query.moveToFirst();
String id = query.getString(query.getColumnIndex("_id"));
get force close when i try to get id
Does this still work? I noticed a lot of the comments say there are missing stuff. I need a good tutorial for implementing MMS sending/ receiving.
Hello i am working this code on Programmr.com and i have tried everything to figure this simple code out and any help would be great-full this is from java program from the site. FYI i am a rookie this may be a simple code but i am trying to think different but no luck.
Here is the problem : >
Problem:
You and the Martian start becoming good friends. There is so much in common between the two of you - an interest in camping on volcanic peaks to hunting for quarters in the swimming pool. One summer afternoon, you and your Martian friend decide to play a game of Echo. The purpose of the game is to echo what the other person is saying. If the Martian says "zboggattyu", you have to reply back with "zboggattyu".
The exercise below, asks you to enter some text on the system console. This text will be stored in the variable 'someText'. This is then copied into another variable called '-echo', which is printed back to the system console. However, we are unable to get the code to compile. You have to get the code to compile, so you can play echo with your Martian friend.
What we expect:
Primarily, we expect you to get the code to compile. Once the code is compiled and run, it will ask you to enter a String on the system console. We expect the same String is echoed (printed) back on the system console.
Hint:
There are some rules governing valid variable names
Learning Outcomes:
After completing this exercise, you should have learned what constitutes a valid variable name.
Here is the code
import java.util.Scanner;
public class Echo {
public static void main(String args[]) {
Scanner scanner = new Scanner(System.in);
System.out.println("Please enter something: ");
String someText = scanner.next();
String echo = "";
///{
//start your coding here
// ?
///}
System.out.println(echo);
}
}
That excersie compiled fine for me even without making any modifications to it... However, the solution they're probably looking for is:
Code:
import java.util.Scanner;
public class Echo {
public static void main(String args[]) {
Scanner scanner = new Scanner(System.in);
System.out.println("Please enter something: ");
String someText = scanner.next();
String echo = "";
echo = someText;
System.out.println(echo);
}
}
Which seems to work fine for me, though to be honest that is a bad way of doing it as you don't actually need the echo variable so you're wasting resources by defining a new variable... a more efficient way of doing it is:
Code:
import java.util.Scanner;
public class Echo {
public static void main(String args[]) {
Scanner scanner = new Scanner(System.in);
System.out.println("Please enter something: ");
String someText = scanner.next();
System.out.println(someText);
}
}
Or you could even go one step further and ignore the someText variable altogether like below:
Code:
import java.util.Scanner;
public class Echo {
public static void main(String args[]) {
Scanner scanner = new Scanner(System.in);
System.out.println("Please enter something: ");
System.out.println(scanner.next());
}
}
Edit: After looking at this some more and seeing that the lesson is about variable names I believe that they actually meant to give you the below code to solve:
Code:
import java.util.Scanner;
public class Echo {
public static void main(String args[]) {
Scanner scanner = new Scanner(System.in);
System.out.println("Please enter something: ");
String someText = scanner.next();
String -echo = "";
System.out.println(-echo);
}
}
Where the line of String -echo =""; would be causing the code not to compile because of the rules governing the naming of variables so the solution would have been to change '-echo' to 'echo' - unfortunately it would seem that they messed they're own exercise up there :laugh:
Thank you for the help developers.
Sent from my SGH-M819N using XDA Free mobile app
Data Transferring Tool for All Android Powered Devices
Data transmission, digital transmission, or digital communications is the physical transfer of data (a digital bit stream or a digitized analog signal) over a point-to-point or point-to-multipoint communication channel. Examples of such channels are copper wires, optical fibers, wireless communication channels, storage media and computer buses. The data are represented as an electromagnetic signal, such as an electrical voltage, radiowave, microwave, or infrared signal. READ MORE
Click to expand...
Click to collapse
There are several ways to transfer data between different type of devices. For Example, from SmartPhones to Another SmartDevices By Bluetooth that devices should connect together. Your App should handle these Processes. Beside using sensors to transfer data between devices there is In-App feature that allows Send/Receive data inside app. For Example Send Data from App on SmartPhone to client side on SmartWatch. In this way the application use system connection and it will handle data and not processes of detecting & connecting to peer(s).
In this thread I will write several Programming Tutorials for Data Transferring in Android.
Then I will use these tutorials to create a Tool for Send/Receive all types of Data to All Android Powered Devices.
android Auto | Tutorial
android Wear | Watch-To-Phone Tutorial | Phone-To-Watch Tutorial
android TV | Tutorial
Server Download/Upload | Tutorial
Bluetooth | Tutorial
NFC | Tutorial
Wifi Direct| Tutorial
I will use Source Code of Current Geeks Empire Open Source Projects as example.
If you want to Contribute in this Project (Tutorials/Tool) Send Message Or Email.
Click to expand...
Click to collapse
Don't Forget to Hit Thanks
XDA:DevDB Information
Data Transferring, Tool/Utility for all devices (see above for details)
Contributors
Geeks Empire
Source Code: https://play.google.com/store/apps/dev?id=6231624576518993324
Version Information
Status: Testing
Created 2015-08-19
Last Updated 2016-11-16
Android Auto [Data Transferring]
{
"lightbox_close": "Close",
"lightbox_next": "Next",
"lightbox_previous": "Previous",
"lightbox_error": "The requested content cannot be loaded. Please try again later.",
"lightbox_start_slideshow": "Start slideshow",
"lightbox_stop_slideshow": "Stop slideshow",
"lightbox_full_screen": "Full screen",
"lightbox_thumbnails": "Thumbnails",
"lightbox_download": "Download",
"lightbox_share": "Share",
"lightbox_zoom": "Zoom",
"lightbox_new_window": "New window",
"lightbox_toggle_sidebar": "Toggle sidebar"
}
Android Auto is a smartphone projection standard developed by Google to allow mobile devices running the Android operating system (version 5.0 "Lollipop" and later) to be operated in automobiles through the dashboard's head unit. Android Auto was announced on June 25, 2014, at Google I/O 2014. The Android Auto mobile app was released on March 19, 2015. READ MORE
Click to expand...
Click to collapse
Android Auto was designed with safety in mind. With a simple and intuitive interface, integrated steering wheel controls, and powerful new voice actions, it's designed to minimize distraction so you can stay focused on the road. READ MORE
Click to expand...
Click to collapse
NOTE: Currently Only Media Player & Short Messaging App allowed on Google Play Store for Android Auto Distribution.
But Soon there will be another category for more Hands-Free App on Android Auto.
Android Auto is now available on these cars
Hyundai Sonata (May 2015) Take it to Service Center & Ask for Upgrade to Android Auto
Skoda Fabia (June 2015)
Skoda Superb (June 2015)
Skoda Octavia (June 2015)
Honda Accord (August 2015)
VW Golf/GTI (September 2015)
Chevrolet Cruze (2016)
& Enabled Hardware are
GPS and high-quality GPS antennas
Steering-wheel controls
Sound system
Directional speakers
Directional microphones
Wheel speed
Compass
Mobile antennas
### Let Start Coding ###
What you need is ability to create Hello World! project
Click to expand...
Click to collapse
You will learn How To Use
BroadcastReceiver, Custom Intent-Filet, Send/Receive Text to AndroidAuto,
Notification & CarExtender-Notification & etc.
Click to expand...
Click to collapse
. from Bottom to Top
First you need to Add XML file as meta-data for AndroidManifest to declare app as AndroidAuto Enabled app for system.
Under the .../res/ create xml folder. inside xml folder create xml resource file: automotive_app_desc.xml and Paste this
Code:
<?xml version="1.0" encoding="utf-8"?>
<automotiveApp>
<uses name="[B]notification[/B]"/>
<!-- [I]notification attribute shows your app is messaging app that only create androidAuto compatible notification[/I] -->
</automotiveApp>
then you need to link this file into manifest under <application />
Code:
<meta-data
android:name="com.google.android.gms.car.application"
android:resource="@xml/automotive_app_desc" /
>
After Installing App, It will notify Android Auto System.
There is 2 parts important for AndroidAuto Notification When TTS read content & when want to Reply Or send reply.
So to handle these parts we should define two BroadcastReceiver in manifest & creating classes.
Code:
<receiver android:name=".[B]MessageReadReceiver[/B]">
<intent-filter>
<action android:name="net.test.cartest.[B]ACTION_MESSAGE_READ[/B]"/>
</intent-filter>
</receiver>
<receiver android:name=".[B]ReplyReceiver[/B]" >
<intent-filter>
<action android:name="net.test.cartest.[B]ACTION_MESSAGE_REPLY[/B]" />
</intent-filter>
</receiver>
Then Create Classes & setup both of them as extends BroadcastReceiver.
Inside Activity class we need to create Android-Auto Compatible Notification.
For Messaging App It should be after users receive new message. I used it for Translator App > geTranslate. It means Notification will show translated text on Car Monitor Read result & use Reply action to reuse translator. However Translator App is not allowed for Android Auto.
Before creating notification we should define Intent Action to invoke BroadcastReceiver previously defined.
Code:
private Intent getMessageReadIntent(int conversationId) {
return new Intent().setAction(READ_ACTION).putExtra(CONVERSATION_ID, id); //[FONT=Impact](Check Source Code)[/FONT]
}
//Conversation ID must be unique.
private Intent getMessageReplyIntent(int conversationId) {
return new Intent().setAction(REPLY_ACTION).putExtra(CONVERSATION_ID, conversationId); //[FONT=Impact](Check Source Code)[/FONT]
}
Creating Notification itself is similar to phone notification But we need to extend it for Cars. to do this we need another component UnreadConversation.Builder
Code:
//This element will handle new how create new notifications
UnreadConversation.Builder unreadConversationBuilder = new UnreadConversation.Builder(sender)
.setLatestTimestamp(timestamp)
.setReadPendingIntent(readPendingIntent)
.setReplyAction(replyIntent, remoteInput);
unreadConversationBuilder.addMessage(message);
Then we should use it for CarExtender()
Code:
new CarExtender().setUnreadConversation(unreadConversationBuilder.build();
Here is Complete Function to define and create Android Auto Compatible Notification
Code:
private void sendNotificationToCar(int conversationId, String sender, String message, long timestamp) {
PendingIntent readPendingIntent = PendingIntent.getBroadcast(
getApplicationContext(),
conversationId,
getMessageReadIntent(conversationId),
PendingIntent.FLAG_UPDATE_CURRENT);
RemoteInput remoteInput = new RemoteInput.Builder(EXTRA_VOICE_REPLY)
.setLabel("Remote Inpute").build();
PendingIntent replyIntent = PendingIntent.getBroadcast(
getApplicationContext(),
conversationId,
getMessageReplyIntent(conversationId),
PendingIntent.FLAG_UPDATE_CURRENT);
UnreadConversation.Builder unreadConversationBuilder = new UnreadConversation.Builder(sender)
.setLatestTimestamp(timestamp)
.setReadPendingIntent(readPendingIntent)
.setReplyAction(replyIntent, remoteInput);
unreadConversationBuilder.addMessage(message);
NotificationCompat.Builder builder = new NotificationCompat.Builder(getApplicationContext())
.setSmallIcon(R.drawable.ic_launcher)
.setLargeIcon(BitmapFactory.decodeResource(getApplicationContext().getResources(), R.drawable.ic_launcher))
.setContentText(message)
.setWhen(timestamp)
.setContentTitle(sender)
.setContentIntent(readPendingIntent)
.extend(new CarExtender().setUnreadConversation(unreadConversationBuilder.build()))
.setColor(Color.RED);
NotificationManagerCompat.from(this).notify(conversationId, builder.build());
}
Now you should handle Receivers. inside MessageReadReceiver you need to define actions to occur after TTS Engine read the content of notification & inside ReplyReceiver How reply actions work, in geTranslate I setup ReplayReceiver to invoke translator to translate what user spoke through car microphone.
Code:
public class [B]ReplyReceiver [/B]extends BroadcastReceiver { //[FONT=Impact](Check Source Code)[/FONT]
@Override
public void onReceive(Context context, Intent intent) {
if (MainActivity.REPLY_ACTION.equals(intent.getAction())) {
CharSequence reply = getMessageText(intent);
//Do What you want...
}
}
private CharSequence getMessageText(Intent intent) {
Bundle remoteInput = RemoteInput.getResultsFromIntent(intent);
if (remoteInput != null) {
return remoteInput.getCharSequence(MainActivity.EXTRA_VOICE_REPLY);
}
return null;
}
}
Up to Here you did what necessary for your app to support Android Auto. If you have access to real AndroidAuto Powered Vehicle Just Install you app and connect it to car & test your application.
But If you don't have access to androidAuto car you need to use simulator.
Code:
Install [COLOR=RoyalBlue][URL="http://forum.xda-developers.com/attachment.php?attachmentid=3444494&stc=1&d=1439998349"][B]CarNotification.APK[/B][/URL][/COLOR]
Code:
Install [B][URL="http://forum.xda-developers.com/attachment.php?attachmentid=3444491&stc=1&d=1439998135"]Simulator APK[/URL][/B] & Go to Setting > Notification Access > Enable Simulator
HOW TO Test Sample
Code:
[B]Open [/B]CarTest [B]Click [/B]on Text Then Click [B]Home [/B]& [B]Run Simulator [/B]> Wait [B]15Seconds [/B]to See Notification
If you find any mistake Or issue in my codes Please inform me.
Click to expand...
Click to collapse
Don't forget to Hit Thanks
Android Wear [Watch-To-Phone][Data Transferring]
Android Wear is a version of Google's Android operating system designed for smartwatches and other wearables.
By pairing with mobile phones running Android version 4.3+, Android Wear integrates Google Now technology and mobile notifications into a smartwatch form factor. It also adds the ability to download applications from the Google Play Store.
Android Wear supports both Bluetooth and Wi-Fi connectivity, as well as a range of features and applications. Watch face styles include round, square and rectangular. Released devices include Motorola Moto 360,the LG G Watch, and the Samsung Gear Live.Hardware manufacturing partners include ASUS, Broadcom, Fossil, HTC, Intel, LG, MediaTek, Imagination Technologies, Motorola, Qualcomm, and Samsung.
In the first six months of availability, Canalys estimates that over 720,000 Android Wear smartwatches were shipped.As of 15 May 2015, Android Wear had between one and five million application installations.
Click to expand...
Click to collapse
### Let Start Coding ###There is just few different between AndroidApp on Phones & AndroidApp on Wearable.
The most important part is Optimizing User-Interface for AndroidWear & then keep in mind which API not available on AndroidWear. These APIs are not available on AndroidWear
android.webkit
android.print
android.app.backup
android.appwidget
android.hardware.usb
NOTE: Do Not set all processes of your app on SmartWatch Module. Cause there is smaller Battery, Memory& weaker Processor.
NOTE: You should use Android Studio to create AndroidWear App.
(However there is tricky way to use eclipse for androidWear But It s better to migrate to AndroidStudio ASAP.)
. from Bottom to Top /Watch Client
When you want to create New Android Application you can choose AndroidWear App Module then AndroidStudio will add necessary AndroidWear Components and Gradle Config automatically.
But you can add AndroidWear Module manually in your current project.
Code:
Go to
File > New > New Module > Select AndroidWear & Next to Finish.
Then you need to add required configuration in your Host Application to Link AndroidWear Module.
Open build.gradle (Module: name-of-project) of Host Application. & Add wearProject to Dependencies
Code:
dependencies{
...
wearApp project{':NAME-OF-PROJECT'}
...
}
Then Save & Sync.
Open AndroidManifest.xml & Add Uses-Features & Uses-Permissions
Code:
<uses-feature android:name="android.hardware.type.watch" />
NOTE: If you want to set new permission that is not in Phone Module you have to set on both Phone & Watch AndroidManifest. (Check Source Code)
There other components are same But for app that want to communicate between Phone & Watch add this meta-data under Application tag
Code:
<meta-data android:name="com.google.android.gms.version"
android:value="@integer/google_play_services_version" />
There is different between phone layout design & watch. There is 3 layout.xml files for each Activity on SmartWatch.
main.xml Just Hold the View >>
<?xml version="1.0" encoding="utf-8"?>
<android.support.wearable.view.WatchViewStub
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools" android:id="@+id/watch_view_stub"
android:layout_width="match_parent" android:layout_height="match_parent"
app:rectLayout="@layout/rectangle_main"
app:roundLayout="@layout/round_main"
tools:context=".WearActivity" tools:deviceIds="wear"></android.support.wearable.view.WatchViewStub>
Click to expand...
Click to collapse
rectangle_main.xml for Rectangle SmartWatch Display that All GUI components must Add here
round_main.xml for Round SmartWatch Display that All GUI components must Add here
Code:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
[B] tools:context=".MainWear"[/B]
android:background="@android:color/white"
[B]tools:deviceIds="wear_square" [/B]>
[B]<!--
You should set context for activity that handle the components and Id to call on main view.
+
All other components like Buttons, TextViews, EditTextx should call here.
-->[/B]
</RelativeLayout>
Now I should add functionality to GUI components in Java.Classes.
To Communicate with Phone First of all I should Initiate GoogleApiClient to Get Node (Phone) connected to watch & get it ready for data communication
Code:
GoogleApiClient client = new GoogleApiClient.Builder(context).addApi(Wearable.API).build();
new Thread(new Runnable() {
@Override
public void run() {
client.blockingConnect(CONNECTION_TIME_OUT_MS, TimeUnit.MILLISECONDS);
NodeApi.GetConnectedNodesResult result = Wearable.NodeApi.getConnectedNodes(client).await();
List<Node> nodes = result.getNodes();
if (nodes.size() > 0) {
nodeId = nodes.get(0).getId();
}
client.disconnect();
}
}).start();
Then I should define WatchViewStub Listener & then other components inside it
Code:
final WatchViewStub stub = (WatchViewStub) findViewById(R.id.watch_view_stub);
stub.setOnLayoutInflatedListener(new WatchViewStub.OnLayoutInflatedListener() {
@Override
public void onLayoutInflated(WatchViewStub stub) {
//Here I should add all other functionalities
}
});
The proper Input Method on Android Wear is Voice Input. The main reason for this is small screens.
To Launch Voice Recognition Engine Call This Method
Code:
private static final int SPEECH_REQUEST_CODE = 0;
private void displaySpeechRecognizer() {
Intent intent = new Intent(RecognizerIntent.ACTION_RECOGNIZE_SPEECH);
intent.putExtra(RecognizerIntent.EXTRA_LANGUAGE_MODEL, RecognizerIntent.LANGUAGE_MODEL_FREE_FORM);
startActivityForResult(intent, SPEECH_REQUEST_CODE);
}
After This I need to get result from engine so i should define onActivityResult
Code:
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (requestCode == SPEECH_REQUEST_CODE && resultCode == RESULT_OK) {
//Here I should Handle Results
List<String> results = data.getStringArrayListExtra(RecognizerIntent.EXTRA_RESULTS);
final String speech = results.get(0).toString();[B]
[COLOR=Red]//Here you can send this data to phone[/COLOR][/B]
}
To send data to app client on phone you can use Wearable.MessageApi.sendMessage()
Code:
if (nodeId != null) {
new Thread(new Runnable() {
@Override
public void run() {
client.blockingConnect(CONNECTION_TIME_OUT_MS, TimeUnit.MILLISECONDS);
Wearable.MessageApi.sendMessage(client, nodeId, MESSAGE, null);
client.disconnect();
}
}).start();
}
. from Bottom to Top /Phone Client
On the Phone Side Open AndroidManifest.xml & Add Service to Handle Received data from Watch with specific <intent-filter/>
<service android:name=".WearReceiver" >
<intent-filter>
<action android:name="com.google.android.gms.wearable.BIND_LISTENER" />
</intent-filter>
</service>
Then Create Class extends of WearableListenerService to get data and add all operation we want.
Code:
public class WearReceiver extends WearableListenerService {
@Override
public void onMessageReceived(MessageEvent messageEvent) {
String getWear = messageEvent.getPath(); [COLOR=Red][B]//Text from Watch[/B][/COLOR]
System.out.println("DEVICE >> " + messageEvent.getPath());
}
}
Done! But You Always you need to Test your App.
If you create Signed APK you can Install it on Phone & It will install on AndroidWear automatically.
NOTE: buildToolsVersion must be same on both Phone & Watch Module.
If you have AndroidWear Device Just Connect it & install it like normal app on phones. It same if you run Emulator But to use emulator you need to do few steps before
First go to Android Virtual Device (AVD) Manager & Create Emulator for both Square & Round Display Watches.
Install AndroidWear App from Google Play Store
In AndroidWear App select Emulator
Open Terminal & Go to ADB directory & type this command
Code:
adb -d forward tcp:5601 tcp:5601
On AndroidWear App Select Connect Emulator
Here is 2 Open Source Projects of Geeks Empire to Help you better for tutorial.
geTranslate
APK + Source Code 1-2-3 (Full) | G Drive
Keep Note
APK + Source Code (Full)
If you find any mistake Or issue in my codes Please inform me.
Click to expand...
Click to collapse
Don't forget to Hit Thanks
Android Wear [Phone-To-Watch][Data Transferring]
Android Wear is a version of Google's Android operating system designed for smartwatches and other wearables.
By pairing with mobile phones running Android version 4.3+, Android Wear integrates Google Now technology and mobile notifications into a smartwatch form factor. It also adds the ability to download applications from the Google Play Store.
Android Wear supports both Bluetooth and Wi-Fi connectivity, as well as a range of features and applications. Watch face styles include round, square and rectangular. Released devices include Motorola Moto 360,the LG G Watch, and the Samsung Gear Live.Hardware manufacturing partners include ASUS, Broadcom, Fossil, HTC, Intel, LG, MediaTek, Imagination Technologies, Motorola, Qualcomm, and Samsung.
In the first six months of availability, Canalys estimates that over 720,000 Android Wear smartwatches were shipped.As of 15 May 2015, Android Wear had between one and five million application installations.
Click to expand...
Click to collapse
### Let Start Coding ###
There is just few different between AndroidApp on Phones & AndroidApp on Wearable.
The most important part is Optimizing User-Interface for AndroidWear & then keep in mind which API not available on AndroidWear. These APIs are not available on AndroidWear
android.webkit
android.print
android.app.backup
android.appwidget
android.hardware.usb
NOTE: Do Not set all processes of your app on SmartWatch Module. Cause there is smaller Battery, Memory& weaker Processor.
NOTE: You should use Android Studio to create AndroidWear App.
(However there is tricky way to use eclipse for androidWear But It s better to migrate to AndroidStudio ASAP.)
WHY Should You Transfer Data to AndroidWear Client?
- Beside using Wear Notification Extender to deliver info to SmartWatch you can transfer data to Watch Client of your App directly. In this way you will be able to do some modification on data before displaying to users on watches OR sharing data securely inside your app.
from Bottom to Top /Watch Client
When you want to create New Android Application you can choose AndroidWear App Module then AndroidStudio will add necessary AndroidWear Components and Gradle Config automatically.
But you can add AndroidWear Module manually in your current project.
Code:
Go to
File > New > New Module > Select AndroidWear & Next to Finish.
Then you need to add required configuration in your Host Application to Link AndroidWear Module.
Open build.gradle (Module: name-of-project) of Host Application. & Add wearProject to Dependencies
Code:
dependencies{
...
wearApp project{':NAME-OF-PROJECT'}
...
}
Then Save & Sync.
Open AndroidManifest.xml & Add Uses-Features & Uses-Permissions
Code:
<uses-feature android:name="android.hardware.type.watch" />
NOTE: If you want to set new permission that is not in Phone Module you have to set on both Phone & Watch AndroidManifest. (Check Source Code)
There other components are same But for app that want to communicate between Phone & Watch add this meta-data under Application tag
Code:
<meta-data android:name="com.google.android.gms.version"
android:value="@integer/google_play_services_version" />
***AndroidWear GUI Guide
There is different between phone layout design & watch. There is 3 layout.xml files for each Activity on SmartWatch.
main.xml Just Hold the View >>
Code:
<?xml version="1.0" encoding="utf-8"?>
<android.support.wearable.view.WatchViewStub
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools" android:id="@+id/watch_view_stub"
android:layout_width="match_parent" android:layout_height="match_parent"
app:rectLayout="@layout/rectangle_main"
app:roundLayout="@layout/round_main"
tools:context=".WearActivity" tools:deviceIds="wear"></android.support.wearable.view.WatchViewStub>
rectangle_main.xml for Rectangle SmartWatch Display that All GUI components must Add here
round_main.xml for Round SmartWatch Display that All GUI components must Add here
Code:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context=".MainWear"
android:background="@android:color/white"
tools:deviceIds="wear_square" >
<!--
You should set context for activity that handle the components and Id to call on main view.
+
All other components like Buttons, TextViews, EditTextx should call here.
-->
</RelativeLayout>
You should set context for activity that handle the components and Id to call on main view.
+
All other components like Buttons, TextViews, EditTextx should call here.
***
I should define an Activity & Service extends WearableListenerService for Wear Module.
The Service must have intent filter to Bind it.
Code:
<action android:name="com.google.android.gms.wearable.BIND_LISTENER" />
Inside the listener you can handle the Received Data But in this example I just get the Intent-Filter-Action & launch the activity by getting the data path
Code:
if(messageEvent.getPath().equalsIgnoreCase("String Intent Filter Action")){
//Start Activity
}
In this example I used simple text as data so to get text data I can setup data like this
Code:
@Override
public void onMessageReceived(MessageEvent messageEvent) {
String receivedTextFromPhoneClient = messageEvent.getData().toString();
}
In case you want to launch an activity you have to setup some implementation to receive data
Code:
public class MainActivity extends Activity implements MessageApi.MessageListener, GoogleApiClient.ConnectionCallbacks{}
& put all necessary override functions
Code:
public void onMessageReceived( final MessageEvent messageEvent ) {}
public void onConnected(Bundle bundle) {}
public void onConnectionSuspended(int i) {}
Inside activity I must initialize the GoogleApiClient to connect to peer and get data
Code:
GoogleApiClient mApiClient = new GoogleApiClient.Builder( this )
.addApi( Wearable.API )
.addConnectionCallbacks( this )
.build();
if( mApiClient != null && !( mApiClient.isConnected() || mApiClient.isConnecting() ) )
mApiClient.connect();
Make Sure to release Listener when quitting the activity
Code:
@Override
protected void onStop() {
if ( mApiClient != null ) {
Wearable.MessageApi.removeListener( mApiClient, this );
if ( mApiClient.isConnected() ) {
mApiClient.disconnect();
}
}
super.onStop();
}
To get Received Data from peer you should do it inside onMessageReceived function //(Check Source Code)
Code:
@Override
public void onMessageReceived( final MessageEvent messageEvent ) {
runOnUiThread( new Runnable() {
@Override
public void run() {
if( messageEvent.getPath().equalsIgnoreCase( WEAR_MESSAGE_PATH ) ) {
mAdapter.add(new String([B]messageEvent.getData()[/B])); //Data from Phone
mAdapter.notifyDataSetChanged();
}
}
});
}
But Where is Data come from?
Let Code the Phone Client to Send Data to SmartWatch.
from Bottom to Top /Phone Client
First of All Don't forget to add <meta-data/> to AndroidManifest.xml under <Application/>
Inside Activity Setup Views as you prefer to get user input & send it to watch.
In this example I set EditText & Button to get input and send data.
Like Watch Client First initialize GoogleApiClient so that I need to add implementation to GoogleApiClient.ConnectionCallbacks & its override functions.
Just Call these three functions at onCreate session.
Code:
private void initGoogleApiClient() {
mApiClient = new GoogleApiClient.Builder( this )
.addApi( Wearable.API )
.build();
mApiClient.connect();
}
Code:
private void init() {
mSendButton.setOnClickListener( new View.OnClickListener() {
@Override
public void onClick(View view) {
String text = mEditText.getText().toString();
if (!TextUtils.isEmpty(text)) {
sendMessage(WEAR_MESSAGE_PATH, text);
}
}
});
}
& final Send function to Watch Client
Code:
private void [COLOR=Red][B]sendMessage[/B][/COLOR]( final String path, final String text ) {
new Thread( new Runnable() {
@Override
public void run() {
NodeApi.GetConnectedNodesResult nodes = Wearable.NodeApi.getConnectedNodes( mApiClient ).await();
for(Node node : nodes.getNodes()) {
MessageApi.SendMessageResult result = Wearable.MessageApi.sendMessage(
mApiClient, node.getId(), path, text.getBytes() ).await();
}
runOnUiThread( new Runnable() {
@Override
public void run() {
mEditText.setText( "" );
}
});
}
}).start();
}
NOTE: There is two parameters that the values & names must be same inside both Phone & Watch Client
The String of Intent-Filter-Action & Message-Path
Code:
private static final String ACTION = "/start_activity";
private static final String WEAR_MESSAGE_PATH = "/message";
NOTE: buildToolsVersion must be same on both Phone & Watch Module.
If you have AndroidWear Device Just Connect it & install it like normal app on phones. It same if you run Emulator But to use emulator you need to do few steps before
First go to Android Virtual Device (AVD) Manager & Create Emulator for both Square & Round Display Watches.
Install AndroidWear App from Google Play Store
In AndroidWear App select Emulator
Open Terminal & Go to ADB directory & type this command
Code:
adb -d forward tcp:5601 tcp:5601
On AndroidWear App Select Connect Emulator
Here is 2 Open Source Projects of Geeks Empire to Help you better for tutorial.
geTranslate
APK + Source Code 1-2-3 (Full)
Keep Note
[URL="http://forum.xda-developers.com/attachment.php?attachmentid=3427464&d=1438637814"]APK + Source Code (Full)[/URL]
NOTE:
If you find any mistake Or issue in my codes Please inform me.
Click to expand...
Click to collapse
Don't forget to Hit Thanks
Android TV [Data Transferring]
Android offers a rich user experience that's optimized for apps running on large screen devices, such as high-definition televisions. Apps on TV offer new opportunities to delight your users from the comfort of their couch...
Android TV apps use the same structure as those for phones and tablets. This similarity means you can modify your existing apps to also run on TV devices or create new apps based on what you already know about building apps for Android.
Click to expand...
Click to collapse
### Let Start Coding ###For TV Apps you must just Focus on GUI. You can use all your codes for Phone App but for Bigger Layout Components.
Also There might be some limitation for Hardware on TV.
This is list of Un-Supported Hardware feature on Android TV
Code:
Touchscreen - android.hardware.touchscreen
Touchscreen emulator - android.hardware.faketouch
Telephony - android.hardware.telephony
Camera - android.hardware.camera
Near Field Communications (NFC) - android.hardware.nfc
GPS - android.hardware.location.gps
Microphone - android.hardware.microphone
Sensors - android.hardware.sensor
. from Bottom to Top
In AndroidManifest.xml you must declare <activity /> for TV class with different Category for Intent Filter & Theme.
Code:
<activity
android:name="com.example.android.TvActivity"
android:label="@string/app_name"
android:theme="@style/[COLOR=Red][B]Theme.Leanback[/B][/COLOR]">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="[COLOR=Red][B]android.intent.category.LEANBACK_LAUNCHER[/B][/COLOR]" />
</intent-filter>
</activity>
& Under <manifest/> declare TV Support Library usage.
Code:
<uses-feature android:name="android.software.leanback"
android:required="false" />
Before/After this declaration you need to configue Hardware feature requirement. For Example:
Code:
...
<uses-feature android:name="android.hardware.touchscreen"
android:required="false" />
...
Also When you want to Add TV support you should design new graphical element as banner for LeanBackLauncher
Under <application /> add
Code:
...
android:banner="@drawable/banner"
...
After this Focus on GUI & Test App. You need to define bigger graphical component that user can see from longer distance compare when look at phone or tablet.
GUI Guide for Android TV App
NOTE: Creating TV Module Using Android Studio Will Add Everything you need for TV App automatically.
If you find any mistake Or issue in my codes Please inform me.
Click to expand...
Click to collapse
Don't forget to Hit Thanks
Server [Upload | Download][Data Transferring]
HOW TO Create Cloud Base App Using Google Cloud
HOW TO Create Cloud Base App Using Custom Server (Coming Soon)
Bluetooth [Data Transferring]
The Android platform includes support for the Bluetooth network stack, which allows a device to wirelessly exchange data with other Bluetooth devices. The application framework provides access to the Bluetooth functionality through the Android Bluetooth APIs. These APIs let applications wirelessly connect to other Bluetooth devices, enabling point-to-point and multipoint wireless features.
Using the Bluetooth APIs, an Android application can perform the following:
Scan for other Bluetooth devices
Query the local Bluetooth adapter for paired Bluetooth devices
Establish RFCOMM channels
Connect to other devices through service discovery
Transfer data to and from other devices
Manage multiple connections
Click to expand...
Click to collapse
Using Bluetooth can be so Helpful in many cases.
Send to & Receive Data from many devices. SmartPhone,any Bluetooth stations & etc.
But If you want to have control of what happen with data sharing with Bluetooth you must learn How to Transfer Data through Bluetooth Socket. For many simple cases Android Provided Intent base Bluetooth Sharing But It is not enough. So I will write here how to use Bluetooth for both situation.
### Let Start Coding ###
Additionally in this tutorial you will learn How to access Files & Converting to byteArray.
. from Bottom to Top /Watch Client
Start New Project & Leave everything to default.
After Sync Gradle Open AndroidManifest.xml to Add Permissions.
Code:
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN"/> //to Enable/Disable Bluetooth
<uses-permission android:name="android.permission.BLUETOOTH"/>
Also for Android API 23+ you should ask for Runtime Permission too.
Code:
if(Build.VERSION.SDK_INT >= 23) {
String[] Permissions = {Manifest.permission.READ_EXTERNAL_STORAGE, Manifest.permission.BLUETOOTH, Manifest.permission.BLUETOOTH_ADMIN};
requestPermissions(Permissions, 10296);
}
Then Open your Bluetooth Activity and Add this Code.
Here is Simple Way to Share File using Bluetooth. Like other Intent sharing Just Invoke its intent-filter
Code:
Intent intent = new Intent();
intent.setAction(Intent.ACTION_SEND);
intent.setType("text/plain");//define type
File f = new File(Environment.getExternalStorageDirectory(), "DIR_TO_FILE");
intent.putExtra(Intent.EXTRA_STREAM, Uri.fromFile(f));
startActivity(intent);
Little More Complex Way is to use Bluetooth Socket.
It finds List of All Paired Bluetooth Devices.
Code:
BluetoothAdapter mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
Set<BluetoothDevice> pairedDevices = mBluetoothAdapter.getBondedDevices();
if (pairedDevices.size() > 0) {
// Loop through paired devices
for (BluetoothDevice device : pairedDevices) {
// Add the name and address to an array adapter to show in a ListView
System.out.println(device.getName() + "\n" + device.getAddress() + "\n\n");
}
}
to connect to another device you need to have its MacAddress.
Code:
String macAddress = new BluetoothDevice().getAddress();
BluetoothDevice [COLOR=Red][B]device [/B][/COLOR]= mBluetoothAdapter.getRemoteDevice(macAddress);
After this you should invoke instance of Socket to start Connection.
NOTE: Nothing should define on Main View. In other hand you have to use Thread(); for background Process.
Code:
Connecting [B]connectThread [/B]= new Connecting([COLOR=Red][B]device [/B][/COLOR]/*Pass Device Information to Thread()*/);
[B]connectThread[/B].start();
Code:
private class Connecting extends Thread {
private final BluetoothSocket [COLOR=Red]bluetoothSocket [/COLOR];
private final BluetoothDevice bluetoothDevice ;
private String mSocketType;
public ConnectThread(BluetoothDevice device) {
bluetoothDevice = device;
[B]BluetoothSocket [/B][COLOR=Red]tmp [/COLOR]= null;
try {
[COLOR=Red]tmp [/COLOR]= device.createRfcommSocketToServiceRecord(MY_UUID_SECURE);
catch (IOException e) {e.printStackTrace();}
[COLOR=Red]bluetoothSocket [/COLOR]= tmp;
}
public void run() {
BluetoothAdapter.getDefaultAdapter().cancelDiscovery();
try {
[COLOR=Red][B]bluetoothSocket.connect();[/B][/COLOR]
} catch (IOException e) {
try {
[COLOR=Red]bluetoothSocket[/COLOR].close();
} catch (IOException e2) {e2.printStackTrace();break;}
return;
}
[COLOR=RoyalBlue][B]//Handle Actions After Connecting.
//Ask to Write & Read Socket Data
// SO HERE you should start another Thread(); to handle [COLOR=Red]Write/Read[/COLOR] Process
[/B][/COLOR][COLOR=RoyalBlue][B][COLOR=Red][COLOR=SeaGreen]globalConnectedThread [/COLOR]= new ConnectedThread(socket, socketType).start();[/COLOR][/B][/COLOR][COLOR=RoyalBlue][COLOR=Black]//pass the socket Information[/COLOR][/COLOR]}
public void cancel() {
try {
bluetoothSocket.close();
} catch (IOException e) {e.printStackTrace();}
}
}
So Check this snippet Carefully and Read Comments
Code:
private class ConnectedThread extends Thread {
private final BluetoothSocket [B]mmSocket[/B];
private final InputStream [COLOR=Red][B]mmInStream[/B][/COLOR];
private final OutputStream [COLOR=Red][B]mmOutStream[/B][/COLOR];
public ConnectedThread(BluetoothSocket socket, String socketType) {
[B]mmSocket [/B]= socket;[COLOR=Red] //you call it on Connecting thread after successful connecting process So pass the socket to this thread[/COLOR]
InputStream [B]tmpIn [/B]= null;
OutputStream [B]tmpOut [/B]= null;
// Get the BluetoothSocket input and output streams
try {
tmpIn = [B]socket[/B].getInputStream();
tmpOut = [B]socket[/B].getOutputStream();
} catch (IOException e) {}
[COLOR=Red][B]mmInStream [/B][/COLOR]= tmpIn;
[COLOR=Red][B]mmOutStream [/B][/COLOR]= tmpOut;
}
public void run() {
byte[] buffer = new byte[1024];
int bytes;
// Keep listening to the InputStream while connected
while (/*define indicator to check if sockets are connected you can do it by using a integer or boolean var*/) {
try {
// Read from the InputStream in loop until connection not lost
bytes = [B]mmInStream[/B].[B][COLOR=Red]read([COLOR=Black]buffer[/COLOR])[/COLOR][/B];
// Send the obtained bytes to the UI Activity OR SAVE Data
//so we call a function save data to file.
saveFile("name_of_file");
//pass read data to views
} catch (IOException e) {
// Start the service over to restart listening mode
this.start();
break;
}
}
}
public void [B]write[/B](byte[] buffer) {//next snippet will show how to use it.
try {
[B]mmOutStream[/B].[COLOR=Red][B]write([COLOR=Black]buffer[/COLOR])[/B][/COLOR];
// Share the sent message back to the UI Activity
} catch (IOException e) {}
}
public void cancel() {
try {
[B]mmSocket[/B].close();
} catch (IOException e) {}
}
}
To Write to Socket from File you must read content of File (that is why we got permission for reading sdcard) and convert it to array of byte and pass to socket. So define ConnectedThread and call write(byte[]) function
Code:
ConnectedThread connectedThread = [COLOR=SeaGreen][B]globalConnectedThread[/B][/COLOR];//this is the value defined after socket connected.
byte[] sendData = [B]readFile[/B]("file_name_on_sdcard").getBytes();//Check out next snippet for readFile Function
[B]connectedThread.[COLOR=Red]write[/COLOR](sendData);[/B]
This Function to read simple text file from sdcard
Code:
public String [B]readFile[/B](String fileName){
String temp = "";
File G = new File(Environment.getExternalStorageDirectory(), fileName);
if(!G.exists()){
System.out.println(fileName + " NOT FOUND");
}
else{
try{
BufferedReader br = new BufferedReader(new FileReader(G));
int c;
temp = "";
while((c = br.read()) != -1){
temp = temp + Character.toString((char)c);
}
}
catch(Exception e){e.printStackTrace();}
}
return temp;//content of file
}
In other hand you must Handle read data on socket and save it.
Code:
public void [B]saveFile[/B](String fileName, String content){
File G = new File(Environment.getExternalStorageDirectory(), fileName);
try {
FileOutputStream fOut = new FileOutputStream(G);
fOut.write((content).getBytes());
fOut.flush();
fOut.close();
}
catch (IOException e) {
e.printStackTrace();
}
}
NOTE: As you may realized this tutorial contained functionality for both Client & Server Side of Bluetooth connection.
So when compiled your test app, install it on 2 devices & pair them.
Download SourceCode & Enjoy
If you find any mistake Or issue in my codes Please inform me
Click to expand...
Click to collapse
Don't forget to Hit Thanks
WiFi Direct | Android Beam [Client|Server][Data Transferring]
Wi-Fi peer-to-peer (P2P) allows Android 4.0 (API level 14) or later devices with the appropriate hardware to connect directly to each other via Wi-Fi without an intermediate access point (Android's Wi-Fi P2P framework complies with the Wi-Fi Alliance's Wi-Fi Direct™ certification program). Using these APIs, you can discover and connect to other devices when each device supports Wi-Fi P2P, then communicate over a speedy connection across distances much longer than a Bluetooth connection. This is useful for applications that share data among users, such as a multiplayer game or a photo sharing application.
The Wi-Fi P2P APIs consist of the following main parts:
Methods that allow you to discover, request, and connect to peers are defined in the WifiP2pManager class.
Listeners that allow you to be notified of the success or failure of WifiP2pManager method calls. When calling WifiP2pManager methods, each method can receive a specific listener passed in as a parameter.
Intents that notify you of specific events detected by the Wi-Fi P2P framework, such as a dropped connection or a newly discovered peer.
You often use these three main components of the APIs together. For example, you can provide a WifiP2pManager.ActionListener to a call to discoverPeers(), so that you can be notified with the ActionListener.onSuccess() and ActionListener.onFailure() methods. A WIFI_P2P_PEERS_CHANGED_ACTION intent is also broadcast if the discoverPeers() method discovers that the peers list has changed.
Click to expand...
Click to collapse
WiFi Direct can be used as main part of data transferring Or you can use it after initial connection with NFC. Android System uses Wifi Direct & Bluetooth to transfer a large amount of data after NFC connection.
### Let Start Coding ###
Wifi Direct has 2 part: Client & Server.
Your app must have contains both parts to be able to transfer data via Wifi Direct.
Both sides need 3 classes: Activity - BroadcastReceiver - Server.
Client Side needs more work to do, so I begin with Wifi Direct - Client
Click to expand...
Click to collapse
Note: Developing for Socket Communication needs vivid vision to understand how it works. Then use all components correctly step by step. So I will explain more about each section. Read Carefully.
. Wifi Direct | Client
Create a Project on Android Studio. Set minSdk to API 14 & Open AndroidManifest.xml to add required permissions and features
Code:
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/>
<uses-permission android:name="android.permission.CHANGE_WIFI_STATE"/>
<uses-permission android:name="android.permission.CHANGE_NETWORK_STATE"/>
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
I don't enter to layout.xml & won't talk about GUI/UX in this tutorial. So you can design your layout and put codes and expanding functionality of your apps.
Click to expand...
Click to collapse
So create 3 classes & register them to Manifest.
ClientActivity
ClientReceiver
ClientService
Open ClientActivity. First, I need a file for sending over Wifi. You can use a file inside your app as assets, from SdCard Or create general FilePicker.
I set an image from /sdcard/Download/ directory. @onCreate(Bundle)
Code:
File [B][COLOR="Red"]fileToSend [/COLOR][/B]= new File(Environment.getExternalStorageDirectory().getPath() + "/Download/shuttle.jpg");
Now, You should initialize instance WifiP2pManager @onCreate(Bundle)
Code:
WifiP2pManager wifiManager = (WifiP2pManager) getSystemService(Context.WIFI_P2P_SERVICE);
Channel wifichannel = wifiManager.initialize(this, getMainLooper(), null);
Then, You should Register a BroadcastReceiver with proper IntentFilter Actions.
Code:
IntentFilter wifiClientReceiverIntentFilter = new IntentFilter();
wifiClientReceiverIntentFilter.addAction(WifiP2pManager.WIFI_P2P_STATE_CHANGED_ACTION);
wifiClientReceiverIntentFilter.addAction(WifiP2pManager.WIFI_P2P_PEERS_CHANGED_ACTION);
wifiClientReceiverIntentFilter.addAction(WifiP2pManager.WIFI_P2P_CONNECTION_CHANGED_ACTION);
wifiClientReceiverIntentFilter.addAction(WifiP2pManager.WIFI_P2P_THIS_DEVICE_CHANGED_ACTION);
registerReceiver([B]ClientReceiver[/B]/*I will edit receiver later*/, wifiClientReceiverIntentFilter);
After this, You should invoke Peer Descovery. Also, It makes device discoverable
Code:
wifiManager.discoverPeers(wifichannel, new WifiP2pManager.ActionListener() {
public void onSuccess() {
//done
}
public void onFailure(int i) {
}});
And wait in ClientReceiver to setup peers. But, It is better to create a function in ClientActivity & pass instance of activity to receiver to access the functions. Check Sample Codes
Code:
public void [B][COLOR="red"]listOfPeers[/COLOR][/B](final WifiP2pDeviceList peers) {
ListView peerView = (ListView) findViewById(R.id.peers_listview);
ArrayList<String> peersStringArrayList = new ArrayList<String>();
for(WifiP2pDevice wd : peers.getDeviceList()) {
peersStringArrayList.add(wd.deviceName);
}
peerView.setClickable(true);
ArrayAdapter arrayAdapter = new ArrayAdapter(this, android.R.layout.simple_list_item_1, peersStringArrayList.toArray());
peerView.setAdapter(arrayAdapter);
peerView.setOnItemClickListener(new OnItemClickListener() {
public void onItemClick(AdapterView<?> arg0, View view, int arg2,long arg3) {
TextView tv = (TextView) view;
WifiP2pDevice device = null;
for(WifiP2pDevice wd : peers.getDeviceList()) {
if(wd.deviceName.equals(tv.getText()))
device = wd;
}
[B][COLOR="red"]if(device != null) {
/*Connect to first Device*/
targetDevice = device;
WifiP2pConfig config = new WifiP2pConfig();
config.deviceAddress = device.deviceAddress;
wifiManager.connect(wifichannel, config, new WifiP2pManager.ActionListener() {
public void onSuccess() {}
public void onFailure(int reason) {}
});
}[/COLOR][/B]
}});}
Open ClientReceiver to request list of peers when proper action occurs Check Sample Codes
Code:
if (WifiP2pManager.WIFI_P2P_PEERS_CHANGED_ACTION.equals(action)) {
manager.requestPeers(channel, new WifiP2pManager.PeerListListener() {
public void onPeersAvailable(WifiP2pDeviceList peers) {
[COLOR="DarkOrchid"]activity[/COLOR].[B][COLOR="red"]listPeers[/COLOR][/B](peers);
}});
}
I set codes for connecting to a peer. But rewrite it here to consider it here in details
Code:
public void connectToPeer(final WifiP2pDevice wifiPeer){
targetDevice = wifiPeer;
/*[COLOR="Red"]Wi-Fi P2p configuration for setting up a connection[/COLOR]*/
WifiP2pConfig config = new WifiP2pConfig();
config.deviceAddress = wifiPeer.deviceAddress;
wifiManager.connect([COLOR="red"]wifichannel[/COLOR], [COLOR="red"]config[/COLOR], new WifiP2pManager.ActionListener() {
public void onSuccess() {}
public void onFailure(int reason) {}
}); }
Now, It is time to perform sending actions in ClientService and for this you should pass required info to service to open socket and write on it...
Port Number
WifiP2pInfo
File
So, After registering a receiver to get information when proper actions occur & getting list of peers, connection stablished & Now you can write data to Socket.OutputStream
Code:
Intent clientServiceIntent = new Intent(this, ClientService.class);
clientServiceIntent.putExtra("fileToSend", [B][COLOR="red"]fileToSend[/COLOR][/B]);
clientServiceIntent.putExtra("port", Integer.valueOf([B][COLOR="red"]port[/COLOR][/B]/*set what you want*/));
clientServiceIntent.putExtra("wifiInfo", [B][COLOR="Red"]wifiInfo[/COLOR][/B]);
startService(clientServiceIntent);
Then Open ClientService & set to get passed data
Code:
port = (Integer) intent.getExtras().get("port");
fileToSend = (File) intent.getExtras().get("fileToSend");
clientResult = (ResultReceiver) intent.getExtras().get("clientResult");
wifiInfo = (WifiP2pInfo) intent.getExtras().get("wifiInfo");
Now you can define socket and try to write data.
Before that you need proper address of target device
Code:
InetAddress [COLOR="RoyalBlue"]targetIP [/COLOR]= [B][COLOR="red"]wifiInfo[/COLOR][/B].groupOwnerAddress;
Now you can open socket and writing data Check Sample Codes
Code:
try {
Socket clientSocket = new Socket([B][COLOR="royalblue"]targetIP[/COLOR][/B], [B][COLOR="Red"]port[/COLOR][/B]);
OutputStream os = clientSocket.getOutputStream();
PrintWriter pw = new PrintWriter(os);
InputStream is = clientSocket.getInputStream();
InputStreamReader isr = new InputStreamReader(is);
BufferedReader br = new BufferedReader(isr);
byte[] buffer = new byte[4096];
FileInputStream fis = new FileInputStream(fileToSend);
BufferedInputStream bis = new BufferedInputStream(fis);
[B][COLOR="RoyalBlue"]while(true) {
int bytesRead = bis.read(buffer, 0, buffer.length);
if(bytesRead == -1) {break;}
[SIZE="4"]os.write(buffer,0, bytesRead);[/SIZE]
os.flush();
}[/COLOR][/B]
fis.close();
bis.close();
br.close();
isr.close();
is.close();
pw.close();
os.close();
clientSocket.close();
}
catch(Exception e) {e.printStackTrace();}
. Wifi Direct | Server
First, Register a BroadcastReceiver with Same IntentFilter Actions to get notification of connection information & specify a Path to store receiving data
Code:
/*this should be a directory*/
File downloadTarget = new File(Environment.getExternalStorageDirectory().getPath() + "/Download/");
Then start service and pass required data and prepare it to listen on socket with same port
Path to Store Data
Port Number
Code:
Intent serverServiceIntent = new Intent(this, ServerService.class);
serverServiceIntent.putExtra("saveLocation", downloadTarget);
serverServiceIntent.putExtra("port", Integer.valueOf(port));
startService(serverServiceIntent);
Now you can accept socket from specified port on server side and listen to read data Check Sample Codes
Code:
try {
[B][COLOR="royalblue"]ServerSocket welcomeSocket = new ServerSocket(port);
Socket socket = welcomeSocket.accept();[/COLOR][/B]
InputStream is = socket.getInputStream();
InputStreamReader isr = new InputStreamReader(is);
BufferedReader br = new BufferedReader(isr);
OutputStream os = socket.getOutputStream();
PrintWriter pw = new PrintWriter(os);
String inputData = "";
String savedAs = "WifiDirectDemo." + System.currentTimeMillis();
File file = new File(saveLocation, savedAs);
byte[] buffer = new byte[4096];
int bytesRead;
FileOutputStream fos = new FileOutputStream(file);
BufferedOutputStream bos = new BufferedOutputStream(fos);
/*[B][COLOR="Red"]Read data from Socket & Write it to a file[/COLOR][/B]*/
while(true)
{
bytesRead = is.read(buffer, 0, buffer.length);
if(bytesRead == -1){break;}
bos.write(buffer, 0, bytesRead);
bos.flush();
}
bos.close();
socket.close();
}
catch(Exception e) {e.printStackTrace();}
Download Source Code & Enjoy
If you find any mistake Or issue in my codes Please inform me
Click to expand...
Click to collapse
Don't forget to Hit Thanks
NFC [Data Transferring][Send File][Write Data to NFC Tag][Read Data from NFC Tag]
Android allows you to transfer large files between devices using the Android Beam file transfer feature. This feature has a simple API and allows users to start the transfer process by simply touching devices. In response, Android Beam file transfer automatically copies files from one device to the other and notifies the user when it's finished.
Click to expand...
Click to collapse
NFC (Near Field Communication) is the best way to transfer important data.
The data can be simple URI of File (Image & etc) from your SdCard, Info of Music Track Or Bank Account Information for transferring money.
And I think, It will be inside all electronic devices in near future.
Read More
Different Usage of NFC | How Secure is NFC
What is NFC Tag
You will learn How to...
I. Transfer Simple File to another Device by NFC
II. Write Data to NFC Tag
III. Read Data from NFC Tag
Click to expand...
Click to collapse
### Let Start Coding ###
. Send a File
Create a Project on Android Studio. Set minSdk to API 16 & Open AndroidManifest.xml to add required permissions and features
Code:
<uses-permission android:name="android.permission.NFC" />
<uses-permission android:name="android.permission.BIND_NFC_SERVICE"/>
<uses-feature android:name="android.hardware.nfc" android:required="true" />
Open layout.xml and add <Button /> & <ImageView/> whereever you like.
Then Open Activity.Java to initialize NfcAdapter & Set URI of Data.
For example, I used a simple ImagePicker to get URI of Image File and set it for NFC transferring.
Code:
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
Intent photoPickerIntent = new Intent(Intent.ACTION_PICK);
photoPickerIntent.setType("image/*");
startActivityForResult(photoPickerIntent, 143);
/*then call [COLOR="Red"]onActivityResult[/COLOR] to get URI of file*/
}
});
After clicking on ImagePicker Button, it will redirect you to select an image. Then you will back to the app and get information of the picked image. Also, you can define Uri imageUri; to store the data and use is later.
Code:
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent imageReturnedIntent) {
super.onActivityResult(requestCode, resultCode, imageReturnedIntent);
switch(requestCode) {
case 143:
if(resultCode == RESULT_OK){
try {
/*this is what app need to set for nfc*/
/*you can also set it to nfcAdapter here*/
[B][COLOR="red"]Uri imageUri[/COLOR];[/B] = imageReturnedIntent.getData();
final InputStream imageStream = getContentResolver().openInputStream(imageUri);
final Bitmap selectedImage = BitmapFactory.decodeStream(imageStream);
imageView.setImageBitmap(selectedImage);
}
catch (FileNotFoundException e) {e.printStackTrace();}
}}}
You can also set the URI to nfcAdapter here. I set when image show to user and He/She get what exactly going on and then by clicking on ImageView confirm & set it to NfcAdapter.
Code:
imageView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
/*NfcAdapter init*/
[B]nfcAdapter = NfcAdapter.getDefaultAdapter(getApplicationContext());[/B]
// Check whether NFC is enabled on device
if(!nfcAdapter.[B]isEnabled[/B]()){
// NFC is disabled, show the settings to enable NFC
Toast.makeText(getApplicationContext(), "Turn On NFC", Toast.LENGTH_SHORT).show();
startActivity(new Intent(Settings.ACTION_NFC_SETTINGS));
}
// Check whether Android Beam feature is enabled on device
else if(!nfcAdapter.[B]isNdefPushEnabled[/B]()) {
// Android Beam is disabled, show the settings to enable Android Beam
Toast.makeText(getApplicationContext(), "Enable Android Beam", Toast.LENGTH_SHORT).show();
startActivity(new Intent(Settings.ACTION_NFCSHARING_SETTINGS));
}
else {
// NFC and Android Beam both are enabled
if (imageUri != null) {
/*URI if File set to NfcAdapter for transfering*/
nfcAdapter.setBeamPushUris(new Uri[]{[B][COLOR="red"]Uri imageUri[/COLOR];[/B]}, MainActivity.this);
}
else {
Intent photoPickerIntent = new Intent(Intent.ACTION_PICK);
photoPickerIntent.setType("image/*");
startActivityForResult(photoPickerIntent, 143);
}
}}});
Now, users should touch their devices and wait for files.
Check out this line.
Code:
nfcAdapter.setBeamPushUris(new Uri[]{[B][COLOR="red"]Uri imageUri[/COLOR];[/B]}, MainActivity.this);
You can call it whenever you want & as you can see, there can be several URI of files.
. Write Data to NFC Tag
There is no additional permissions & features for this. Check If your NFC Tags are Writable.
Initialize NfcAdapter @oncreate(Bundle)
Code:
NfcAdapter = NfcAdapter.getDefaultAdapter(getApplicationContext());
Then add <Intent-filter/> to detect NFC connections. I set filters as a PendingIntent [COLOR="RoyalBlue" [user=815433]@onres[/user]ume()[/COLOR][/B]
[CODE]IntentFilter ACTION_TAG_DISCOVERED = new IntentFilter(NfcAdapter.ACTION_TAG_DISCOVERED);
IntentFilter ACTION_NDEF_DISCOVERED = new IntentFilter(NfcAdapter.ACTION_NDEF_DISCOVERED);
IntentFilter ACTION_TECH_DISCOVERED = new IntentFilter(NfcAdapter.ACTION_TECH_DISCOVERED);
IntentFilter[] nfcIntentFilter = new IntentFilter[]{ACTION_TAG_DISCOVERED, ACTION_NDEF_DISCOVERED, ACTION_TECH_DISCOVERED};
PendingIntent pendingIntent = PendingIntent.
getActivity(this, 0, new Intent(this, getClass()).addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP), 0);
if(nfcAdapter != null) {
[B]nfcAdapter[/B].[COLOR="red"]enableForegroundDispatch[/COLOR](this, pendingIntent, nfcIntentFilter, null);
/*enableForegroundDispatch; This will give give priority to the foreground activity when dispatching a discovered Tag to an application.*/
}
[/CODE]
Now, you can detect when NfcTag touch your device. So you need to initialize NFC.Tag instance & store is in Tag tag;
Code:
@Override
protected void onNewIntent(Intent intent) {
[B][COLOR="red"]tag [/COLOR][/B]= intent.getParcelableExtra(NfcAdapter.EXTRA_TAG);
}
I use ImagePicker Button to perform writing action on press and hold.
I will write String of Image URI to NFC tag.
Code:
picker.setOnLongClickListener(new View.OnLongClickListener() {
@Override
public boolean onLongClick(View view) {
if([B][COLOR="red"]tag [/COLOR][/B]!= null) {
Ndef ndef = Ndef.get([B][COLOR="red"]tag[/COLOR][/B]);
if (ndef != null) {
try {
ndef.connect();
NdefRecord mimeRecord = NdefRecord.createMime("text/plain", [B][COLOR="red"]imageUriString[/COLOR][/B].getBytes());
ndef.writeNdefMessage(new NdefMessage(mimeRecord));
ndef.close();
Toast.makeText(getApplicationContext(), "WriteDone!", Toast.LENGTH_LONG).show();
}
catch (Exception e) {e.printStackTrace();}
}
}return true;}});
It is mandatory for all Android devices with NFC to correctly enumerate Ndef on NFC Forum Tag Types 1-4, and implement all NDEF operations as defined in this class.
Click to expand...
Click to collapse
. Read Data from NFC Tag
Initialize NfcAdapter, define PendingIntent with specific NfcAdapter.ACTIONS @onresume() & initialize NFC.Tag.
When you have instance of Tag then you can Read/Write.
Now, I get String of imageURI from NFC.Tag and set image to ImageView.
Code:
imageView.setOnLongClickListener(new View.OnLongClickListener() {
@Override
public boolean onLongClick(View view) {
if([B][COLOR="red"]tag [/COLOR][/B]!= null) {
Ndef ndef = Ndef.get([B][COLOR="red"]tag[/COLOR][/B]);
try {
ndef.connect();
NdefMessage ndefMessage = ndef.getNdefMessage();
[B]String nfcInfo = new String(ndefMessage.getRecords()[0].getPayload());
[COLOR="red"]imageUri [/COLOR]= Uri.parse(nfcInfo);[/B]
InputStream imageStream = getContentResolver().openInputStream(imageUri);
Bitmap selectedImage = BitmapFactory.decodeStream(imageStream);
imageView.setImageBitmap(selectedImage);
ndef.close();
Toast.makeText(getApplicationContext(), "ReadDone!", Toast.LENGTH_LONG).show();
}
catch (Exception e) {e.printStackTrace();}
} return true; } });
Download Source Code & Enjoy
If you find any mistake Or issue in my codes Please inform me
Click to expand...
Click to collapse
Don't forget to Hit Thanks
Reserved
Reserved
Index of Data Transferring Tutorials
Latest Data Transferring Tutorial
android Auto | Tutorial
android Wear | Watch-To-Phone Tutorial | Phone-To-Watch Tutorial
android TV | Tutorial
Server Download/Upload | Tutorial
Bluetooth | Tutorial
NFC | Tutorial
Wifi Direct| Tutorial
Don't forget to Hit Thanks
Hello,
I create that thread to present you a tutorial learning you to save data with SQLite on Android. This tutorial is also available in video on Youtube :
Learn to save data with SQLite on Android
On Android, there are several solutions to persist data between users’ sessions. One solution is to use a relational database to persist data and then to be able to query easily these data. In standard, Android SDK comes with a SQLite implementation. Biggest advantage of SQLite integration to Android OS is the fact that there is no need to to setup the database. So, no administration of this database. SQLite is embedded in standard and each application can have its SQLite database.
The only job that developers must make is to define SQL tables and statements for creating and updating data. Access to an SQLite database involves accessing the file system. So, it can be a slow operation. To avoid ANR (Application Not Responding) errors, it’s recommended to perform database operations asynchronously.
When an application creates and uses a SQLite database, it will be saved by default in the directory : DATA/data/APP_PACKAGE/databases/FILENAME .
1. Architecture
All classes needed to manage databases in Android SDK are contained in the package android.database . The package android.database.sqlite contains the SQLite specific classes.
SQLite API is centered around 2 main classes :
SQLiteOpenHelper that is an helper class to extend to manage database operations.
SQLiteDatabase that is the base class for working with a SQLite database in Android.
2. SQLiteOpenHelper
When you want to work with a SQLite database in Android, you must extend SQLiteOpenHelper class. In the constructor of your subclass you call the super() method of SQLiteOpenHelper, specifying the database name and the current database version.
You need also to override the following methods :
onCreate() that is called when database is accessed but not yet created.
onUpgrade() called when you choose to increment the version number of the database. In this method you can manage the migration process between two databases versions.
Both methods get and SQLiteDatabase instance in parameter which is the way to communicate with the database.
Furthermore, SQLiteOpenHelper provides 2 methods to get access to an SQLiteDatabase instance object respectively in read and in write modes :
getReadableDatabase() for read mode.
getWriteableDatabase() for write mode.
3. SQLiteDatabase
SQLiteDatabase is the class used to communicate with a SQLite database. It exposes several methods to interact with database like insert(), update() or delete().
In addition, it lets you to make queries via rawQuery() to queries made directly in SQL or via query() method. This last method provides a structured interface for specifying a SQL query.
4. Practice
Now, you know theory about SQLite in Android context. We can put in practice all the concepts. To achieve that, we’re going to make a database with a players table letting us to store NBA players.
To start, we create a simple Player Java POJO :
Code:
public class Player {
private int id;
private String name;
private String position;
private int height;
public Player() {
}
public Player(int id, String name, String position, int height) {
this.id = id;
this.name = name;
this.position = position;
this.height = height;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getPosition() {
return position;
}
public void setPosition(String position) {
this.position = position;
}
public int getHeight() {
return height;
}
public void setHeight(int height) {
this.height = height;
}
@Override
public String toString() {
return name + " - " + position + " - " + height + " cm";
}
}
Then, we must create the SQLiteOpenHelper extended class to manage our application database. Code is here :
Code:
package com.ssaurel.samples.sqlite;
import java.util.LinkedList;
import java.util.List;
import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
public class SQLiteDatabaseHandler extends SQLiteOpenHelper {
private static final int DATABASE_VERSION = 1;
private static final String DATABASE_NAME = "PlayersDB";
private static final String TABLE_NAME = "Players";
private static final String KEY_ID = "id";
private static final String KEY_NAME = "name";
private static final String KEY_POSITION = "position";
private static final String KEY_HEIGHT = "height";
private static final String[] COLUMNS = { KEY_ID, KEY_NAME, KEY_POSITION,
KEY_HEIGHT };
public SQLiteDatabaseHandler(Context context) {
super(context, DATABASE_NAME, null, DATABASE_VERSION);
}
@Override
public void onCreate(SQLiteDatabase db) {
String CREATION_TABLE = "CREATE TABLE Players ( "
+ "id INTEGER PRIMARY KEY AUTOINCREMENT, " + "name TEXT, "
+ "position TEXT, " + "height INTEGER )";
db.execSQL(CREATION_TABLE);
}
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
// you can implement here migration process
db.execSQL("DROP TABLE IF EXISTS " + TABLE_NAME);
this.onCreate(db);
}
public void deleteOne(Player player) {
// Get reference to writable DB
SQLiteDatabase db = this.getWritableDatabase();
db.delete(TABLE_NAME, "id = ?", new String[] { String.valueOf(player.getId()) });
db.close();
}
public Player getPlayer(int id) {
SQLiteDatabase db = this.getReadableDatabase();
Cursor cursor = db.query(TABLE_NAME, // a. table
COLUMNS, // b. column names
" id = ?", // c. selections
new String[] { String.valueOf(id) }, // d. selections args
null, // e. group by
null, // f. having
null, // g. order by
null); // h. limit
if (cursor != null)
cursor.moveToFirst();
Player player = new Player();
player.setId(Integer.parseInt(cursor.getString(0)));
player.setName(cursor.getString(1));
player.setPosition(cursor.getString(2));
player.setHeight(Integer.parseInt(cursor.getString(3)));
return player;
}
public List<Player> allPlayers() {
List<Player> players = new LinkedList<Player>();
String query = "SELECT * FROM " + TABLE_NAME;
SQLiteDatabase db = this.getWritableDatabase();
Cursor cursor = db.rawQuery(query, null);
Player player = null;
if (cursor.moveToFirst()) {
do {
player = new Player();
player.setId(Integer.parseInt(cursor.getString(0)));
player.setName(cursor.getString(1));
player.setPosition(cursor.getString(2));
player.setHeight(Integer.parseInt(cursor.getString(3)));
players.add(player);
} while (cursor.moveToNext());
}
return players;
}
public void addPlayer(Player player) {
SQLiteDatabase db = this.getWritableDatabase();
ContentValues values = new ContentValues();
values.put(KEY_NAME, player.getName());
values.put(KEY_POSITION, player.getPosition());
values.put(KEY_HEIGHT, player.getHeight());
// insert
db.insert(TABLE_NAME,null, values);
db.close();
}
public int updatePlayer(Player player) {
SQLiteDatabase db = this.getWritableDatabase();
ContentValues values = new ContentValues();
values.put(KEY_NAME, player.getName());
values.put(KEY_POSITION, player.getPosition());
values.put(KEY_HEIGHT, player.getHeight());
int i = db.update(TABLE_NAME, // table
values, // column/value
"id = ?", // selections
new String[] { String.valueOf(player.getId()) });
db.close();
return i;
}
}
Database is created in the constructor of the extended class. Players table is created in the onCreate() method thanks to a SQL statement.
In our class, we add methods to add a new player, to delete an existing one, to update and then a method to get all the players in the table. In this last method, we use a Cursor object to iterate on rows and then build equivalent Player instances.
To use our class to create some players then display on a simple ListView, we can use the following code :
Code:
public class MainActivity extends Activity {
private SQLiteDatabaseHandler db;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// create our sqlite helper class
db = new SQLiteDatabaseHandler(this);
// create some players
Player player1 = new Player(1, "Lebron James", "F", 203);
Player player2 = new Player(2, "Kevin Durant", "F", 208);
Player player3 = new Player(3, "Rudy Gobert", "C", 214);
// add them
db.addPlayer(player1);
db.addPlayer(player2);
db.addPlayer(player3);
// list all players
List<Player> players = db.allPlayers();
if (players != null) {
String[] itemsNames = new String[players.size()];
for (int i = 0; i < players.size(); i++) {
itemsNames[i] = players.get(i).toString();
}
// display like string instances
ListView list = (ListView) findViewById(R.id.list);
list.setAdapter(new ArrayAdapter<String>(this,
android.R.layout.simple_list_item_1, android.R.id.text1, itemsNames));
}
}
}
Execution result can be seen here :
{
"lightbox_close": "Close",
"lightbox_next": "Next",
"lightbox_previous": "Previous",
"lightbox_error": "The requested content cannot be loaded. Please try again later.",
"lightbox_start_slideshow": "Start slideshow",
"lightbox_stop_slideshow": "Stop slideshow",
"lightbox_full_screen": "Full screen",
"lightbox_thumbnails": "Thumbnails",
"lightbox_download": "Download",
"lightbox_share": "Share",
"lightbox_zoom": "Zoom",
"lightbox_new_window": "New window",
"lightbox_toggle_sidebar": "Toggle sidebar"
}
SQLite implementation in Android is simple and really powerful. You can now use it in your Android application to persist data.
Don't hesitate to give it a try and give me your feedbacks about this tutorial.
Thanks.
Sylvain
Hey, I have made a preview for SQLite database earlier this month for my friend.
If anyone's interested then it's there at https://www.GitHub.com/Fifa2151/SQLiteTest
Thanks,
Raj.
Sent from my Pixel using Tapatalk
sylsau said:
Hello,
I create that thread to present you a tutorial learning you to save data with SQLite on Android. This tutorial is also available in video on Youtube :
Learn to save data with SQLite on Android
On Android, there are several solutions to persist data between users’ sessions. One solution is to use a relational database to persist data and then to be able to query easily these data. In standard, Android SDK comes with a SQLite implementation. Biggest advantage of SQLite integration to Android OS is the fact that there is no need to to setup the database. So, no administration of this database. SQLite is embedded in standard and each application can have its SQLite database.
The only job that developers must make is to define SQL tables and statements for creating and updating data. Access to an SQLite database involves accessing the file system. So, it can be a slow operation. To avoid ANR (Application Not Responding) errors, it’s recommended to perform database operations asynchronously.
When an application creates and uses a SQLite database, it will be saved by default in the directory : DATA/data/APP_PACKAGE/databases/FILENAME .
1. Architecture
All classes needed to manage databases in Android SDK are contained in the package android.database . The package android.database.sqlite contains the SQLite specific classes.
SQLite API is centered around 2 main classes :
SQLiteOpenHelper that is an helper class to extend to manage database operations.
SQLiteDatabase that is the base class for working with a SQLite database in Android.
2. SQLiteOpenHelper
When you want to work with a SQLite database in Android, you must extend SQLiteOpenHelper class. In the constructor of your subclass you call the super() method of SQLiteOpenHelper, specifying the database name and the current database version.
You need also to override the following methods :
onCreate() that is called when database is accessed but not yet created.
onUpgrade() called when you choose to increment the version number of the database. In this method you can manage the migration process between two databases versions.
Both methods get and SQLiteDatabase instance in parameter which is the way to communicate with the database.
Furthermore, SQLiteOpenHelper provides 2 methods to get access to an SQLiteDatabase instance object respectively in read and in write modes :
getReadableDatabase() for read mode.
getWriteableDatabase() for write mode.
3. SQLiteDatabase
SQLiteDatabase is the class used to communicate with a SQLite database. It exposes several methods to interact with database like insert(), update() or delete().
In addition, it lets you to make queries via rawQuery() to queries made directly in SQL or via query() method. This last method provides a structured interface for specifying a SQL query.
4. Practice
Now, you know theory about SQLite in Android context. We can put in practice all the concepts. To achieve that, we’re going to make a database with a players table letting us to store NBA players.
To start, we create a simple Player Java POJO :
Code:
public class Player {
private int id;
private String name;
private String position;
private int height;
public Player() {
}
public Player(int id, String name, String position, int height) {
this.id = id;
this.name = name;
this.position = position;
this.height = height;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getPosition() {
return position;
}
public void setPosition(String position) {
this.position = position;
}
public int getHeight() {
return height;
}
public void setHeight(int height) {
this.height = height;
}
@Override
public String toString() {
return name + " - " + position + " - " + height + " cm";
}
}
Then, we must create the SQLiteOpenHelper extended class to manage our application database. Code is here :
Code:
package com.ssaurel.samples.sqlite;
import java.util.LinkedList;
import java.util.List;
import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
public class SQLiteDatabaseHandler extends SQLiteOpenHelper {
private static final int DATABASE_VERSION = 1;
private static final String DATABASE_NAME = "PlayersDB";
private static final String TABLE_NAME = "Players";
private static final String KEY_ID = "id";
private static final String KEY_NAME = "name";
private static final String KEY_POSITION = "position";
private static final String KEY_HEIGHT = "height";
private static final String[] COLUMNS = { KEY_ID, KEY_NAME, KEY_POSITION,
KEY_HEIGHT };
public SQLiteDatabaseHandler(Context context) {
super(context, DATABASE_NAME, null, DATABASE_VERSION);
}
@Override
public void onCreate(SQLiteDatabase db) {
String CREATION_TABLE = "CREATE TABLE Players ( "
+ "id INTEGER PRIMARY KEY AUTOINCREMENT, " + "name TEXT, "
+ "position TEXT, " + "height INTEGER )";
db.execSQL(CREATION_TABLE);
}
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
// you can implement here migration process
db.execSQL("DROP TABLE IF EXISTS " + TABLE_NAME);
this.onCreate(db);
}
public void deleteOne(Player player) {
// Get reference to writable DB
SQLiteDatabase db = this.getWritableDatabase();
db.delete(TABLE_NAME, "id = ?", new String[] { String.valueOf(player.getId()) });
db.close();
}
public Player getPlayer(int id) {
SQLiteDatabase db = this.getReadableDatabase();
Cursor cursor = db.query(TABLE_NAME, // a. table
COLUMNS, // b. column names
" id = ?", // c. selections
new String[] { String.valueOf(id) }, // d. selections args
null, // e. group by
null, // f. having
null, // g. order by
null); // h. limit
if (cursor != null)
cursor.moveToFirst();
Player player = new Player();
player.setId(Integer.parseInt(cursor.getString(0)));
player.setName(cursor.getString(1));
player.setPosition(cursor.getString(2));
player.setHeight(Integer.parseInt(cursor.getString(3)));
return player;
}
public List<Player> allPlayers() {
List<Player> players = new LinkedList<Player>();
String query = "SELECT * FROM " + TABLE_NAME;
SQLiteDatabase db = this.getWritableDatabase();
Cursor cursor = db.rawQuery(query, null);
Player player = null;
if (cursor.moveToFirst()) {
do {
player = new Player();
player.setId(Integer.parseInt(cursor.getString(0)));
player.setName(cursor.getString(1));
player.setPosition(cursor.getString(2));
player.setHeight(Integer.parseInt(cursor.getString(3)));
players.add(player);
} while (cursor.moveToNext());
}
return players;
}
public void addPlayer(Player player) {
SQLiteDatabase db = this.getWritableDatabase();
ContentValues values = new ContentValues();
values.put(KEY_NAME, player.getName());
values.put(KEY_POSITION, player.getPosition());
values.put(KEY_HEIGHT, player.getHeight());
// insert
db.insert(TABLE_NAME,null, values);
db.close();
}
public int updatePlayer(Player player) {
SQLiteDatabase db = this.getWritableDatabase();
ContentValues values = new ContentValues();
values.put(KEY_NAME, player.getName());
values.put(KEY_POSITION, player.getPosition());
values.put(KEY_HEIGHT, player.getHeight());
int i = db.update(TABLE_NAME, // table
values, // column/value
"id = ?", // selections
new String[] { String.valueOf(player.getId()) });
db.close();
return i;
}
}
Database is created in the constructor of the extended class. Players table is created in the onCreate() method thanks to a SQL statement.
In our class, we add methods to add a new player, to delete an existing one, to update and then a method to get all the players in the table. In this last method, we use a Cursor object to iterate on rows and then build equivalent Player instances.
To use our class to create some players then display on a simple ListView, we can use the following code :
Code:
public class MainActivity extends Activity {
private SQLiteDatabaseHandler db;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// create our sqlite helper class
db = new SQLiteDatabaseHandler(this);
// create some players
Player player1 = new Player(1, "Lebron James", "F", 203);
Player player2 = new Player(2, "Kevin Durant", "F", 208);
Player player3 = new Player(3, "Rudy Gobert", "C", 214);
// add them
db.addPlayer(player1);
db.addPlayer(player2);
db.addPlayer(player3);
// list all players
List<Player> players = db.allPlayers();
if (players != null) {
String[] itemsNames = new String[players.size()];
for (int i = 0; i < players.size(); i++) {
itemsNames[i] = players.get(i).toString();
}
// display like string instances
ListView list = (ListView) findViewById(R.id.list);
list.setAdapter(new ArrayAdapter<String>(this,
android.R.layout.simple_list_item_1, android.R.id.text1, itemsNames));
}
}
}
Execution result can be seen here :
SQLite implementation in Android is simple and really powerful. You can now use it in your Android application to persist data.
Don't hesitate to give it a try and give me your feedbacks about this tutorial.
Thanks.
Sylvain
Click to expand...
Click to collapse
Awesome guide @sylsau...
Also, do you know how to make a flashify type app but only for a specific zip?
When you want to work with a SQLite database in Android, you must extend SQLiteOpenHelper class. In the constructor of your subclass you call the super() method of SQLiteOpenHelper, specifying the database name and the current database version.
Click to expand...
Click to collapse
You don't actually need to use a subclass of SQLiteOpenHelper you can use the SQliteDatabase's open????? methods.
Furthermore, SQLiteOpenHelper provides 2 methods to get access to an SQLiteDatabase instance object respectively in read and in write modes :
Click to expand...
Click to collapse
Actually, in either case, except if the database cannot be opened, for write, both getReadableDatabase and getWritableDatabase will open a database that can be written to. As per :-
Create and/or open a database. This will be the same object returned by getWritableDatabase() unless some problem, such as a full disk, requires the database to be opened read-only. In that case, a read-only database object will be returned. If the problem is fixed, a future call to getWritableDatabase() may succeed, in which case the read-only database object will be closed and the read/write object will be returned in the future.
Click to expand...
Click to collapse
as per developer.android.com/reference/android/database/sqlite/SQLiteOpenHelper#getReadableDatabase()
On occasions people new to SQLite sometimes wonder why no database exists after they have instantiated the subclass of SQLiteOpenHelper (aka the DatabaseHelper). This is because the database is only created when either getWritableDatabase or getReadableDatabase is called. With a single line added to the constructor, the constructor will create the database (and thus invoke the onCreate method) e.g.
Code:
public SQLiteDatabaseHandler(Context context) {
super(context, DATABASE_NAME, null, DATABASE_VERSION);
this.getWritableDatabse();
}
AUTOINCREMENT is perhaps the most commonly misused keyword (perhaps wrongly named). It does not make the column automatically generate a unique ID. It is INTEGER PRIMARY KEY that does this, as it make the column an alias of the **rowid**.
Rather AUTOINCREMENT compliments INTEGER PRIMARY KEY adding a constraint that the generated ID must be larger that any ID that exists or have existed. However, this is a moot point as it's only when the largest possible ID has been assigned (9223372036854775807) that it comes into play (other than without AUTOINCREMENT a deleted highest ID will be resused). At this point a table with AUTOINCREMENT will then fail with an SQLITE_FULL exception (without AUTOINCREMENT will attempt to assign a free lower ID rather than fail). However, AUTOINCREMENT has overheads (using a limited test I came up with an 8-12% degradation in performance when inserting). This is due to a changed algorithm being used that utilises another table sqlite_sequence that stores the highest allocated ID.
The SQLite documentation states :-
The AUTOINCREMENT keyword imposes extra CPU, memory, disk space, and disk I/O overhead and should be avoided if not strictly needed. It is usually not needed.
Click to expand...
Click to collapse
sqlite.org/autoinc.html
There are a few issues with the code, such as :-
You should always close Cursors when finished with them (not doing so may result in too many databases /database objects open exception ).
Checking a Cursor for null after a Cursor is returned from a call to an SQLiteDatabase method that returns a Cursor serves no purpose. A valid Cursor will always be returned. If there is no data then using a Cursor moveTo????? method will return false is the move cannot be made, alternately the getCount() method will return the number of rows in the Cursor.
If there were now rows in the Players table, the the code would fail with an error when an attempt is made to retrieve data at
Code:
player.setId(Integer.parseInt(cursor.getString(0)));
Issues regarding mis-calculated column offsets can be reduced by taking advantage of the Cursor's **getColumnIndex** method.
As such, as an example, the getPlayer method would be improved if changed to :-
Code:
public Player getPlayer(int id) {
SQLiteDatabase db = this.getReadableDatabase();
Cursor cursor = db.query(TABLE_NAME, // a. table
COLUMNS, // b. column names
" id = ?", // c. selections
new String[] { String.valueOf(id) }, // d. selections args
null, // e. group by
null, // f. having
null, // g. order by
null); // h. limit
Player player = new Player(); //<<<<<<<<<< Always have a Player to return (should check for default player to indicated )
if (cursor.moveToFirst()) {
player.setId(Integer.parseInt(cursor.getString(cursor.getColumnIndex(KEY_ID))));
player.setName(cursor.getString(cursor.getColumnIndex(KEY_NAME)));
player.setPosition(cursor.getString(cursor.getColumnIndex(KEY_POSITION)));
player.setHeight(Integer.parseInt(cursor.getString(cursor.getColumnIndex(KEY_HEIGHT))));
}
cursor.close(); //<<<<<<<<<< Important to close a Cursor
return player;
}