Related
hi all !
I have a little problem..first i am not a very good coder..and second I have a feature that i would like to solve...
from a winmob 5.0 smartphone or pocketpc I would like to set a couple of registry settings.
But I cant figure out how to do it...
What I need is to set a device name in a spcific place in the registry...from the device itself,some kind of gui..the guipart and the rest of the app I have but its the regentry thing that I cant figure out...
first I thought that I could take an xml file and run it frm the program but then I thougnt about a little and what I need is a regentry instead...
I am running the latest .net visual studio 2005
anyone have any tips for me??
Writing to the registry is not that hard, it is done like this in C
HKEY hKey;
DWORD RegKeyDisp, RegKeyType, RegKeySize, dwTemp;
USHORT RegRead;
//Create the registry Key if it don't exist
if (RegCreateKeyEx(HKEY_LOCAL_MACHINE,
TEXT("Software\\Sample\\Test1\\Test2"),
0, NULL, 0,0, NULL, &hKey, &RegKeyDisp) == ERROR_SUCCESS)
{
RegCloseKey(hKey);
}
//Read (Query) the value from the registry
if (RegCreateKeyEx(HKEY_LOCAL_MACHINE,
TEXT("Software\\Sample\\Test1\\Settings"),
0, NULL, 0,0, NULL, &hKey, &RegKeyDisp) == ERROR_SUCCESS)
{
//Get the Value in 'Read This Key'
RegKeyType = REG_DWORD;
RegKeySize = sizeof(DWORD);
RegQueryValueEx(hKey, TEXT("Read This Key"), NULL, &RegKeyType,
(PBYTE)&RegRead, &RegKeySize);
RegCloseKey(hKey);
}
//Write a value to the registry
dwTemp = 42; //Value to be stored in the registry
RegKeyType = REG_DWORD;
RegKeySize = sizeof(DWORD);
if (RegCreateKeyEx(HKEY_LOCAL_MACHINE,
TEXT("Software\\Sample\\Test1\\Test2"),
0, NULL, 0,0, NULL, &hKey, &RegKeyDisp) == ERROR_SUCCESS)
{
RegSetValueEx(hKey, TEXT("WriteKey"),
0, RegKeyType,
(PBYTE)&dwTemp,
RegKeySize);
RegCloseKey(hKey);
}
This has all been cut and chopped out of a working program I have written - I can not guarantee I have made a mistake in choping out irrelevent information - but I hope it makes sense.
If you are not writing in C then I am sure you could declare them or wrap them up with P/Invoke.
nice !
thx alot for a very fast answer!
I will give it a go as soon I get home!!
ok that looked ok...but it turned out that I need to put something in the registry via XML afterall...
is there a way todo that??
take a value(in this case a userid/password) and put it in cm_vpnentries ??
Reading the sdk it want to do it through rapi or cpf file only...
When you click on the sound icon, you get the two sliders and three radio buttons. Anyone know how to set those radio buttons through code (C++) so it gets reflected in the icon display (and actually changes the "profile")?. I'd like to change to vibrate/mute/sound at different times using something like alarmToday to run a small app.
Thanks,
sbl
I wrote a profiles app in .net and C++ here are some code snippets;
You can change the registry values for the volumes (I cant remember which as I wrote it a long time ago), then you need to call the following to have the values applied.
// ProfilesHelper.cpp : Defines the entry point for the DLL application.
//
#include "stdafx.h"
#include "ProfilesHelper.h"
#include <windows.h>
#include <Soundfile.h>
void WINEXPORT SetSoundMode(int mode)
{
// Initialize an empty SNDFILEINFO structure
SNDFILEINFO sndfile = {0};
if (mode == 0)
sndfile.sstType = SND_SOUNDTYPE_ON;
if (mode == 1)
sndfile.sstType = SND_SOUNDTYPE_VIBRATE;
if (mode == 2)
sndfile.sstType = SND_SOUNDTYPE_NONE;
SndSetSound(SND_EVENT_ALL, &sndfile, false);
void AudioUpdateFromRegistry();
}
void WINEXPORT SetSystemVolume(long volume)
{
waveOutSetVolume(NULL, volume);
void AudioUpdateFromRegistry();
}
Hi,
I have a similar need of programatically cycling the sound icon to MUTE VIBRATE and normal volume icon. I can do it successfully on Windows Mobile 5.0. I created a sample application with Visual Studio 2005 for WM 5.0 and I am able to set the icons as i want. But when i tried it for PPC2003 I was not able to compile that. Missing SoudFile.h. Can any one help me to find out how to do the same thing on PPC2003 specifically i-mate devices like PDA2 and PDA2K.
Thanks
With Regards,
Bhagat Nirav K.
i know its a 2 ur old post..but i need help also now
how can i mute sounds using C# in .net 2.0 for WM6
Just forget about this header file...
Operate on HKCU\ControlPanel\Notifications\ShellOverrides\Mode value. It can have 3 states: 0 == normal, 1 == vibrate and 2 == silent. That's all
Probably you need to call AudioUpdateFromRegistry function from coredll.dll.
Or... another method
look at HKCU\ControlPanel\Sounds\RingTone0:Sound, it takes 3 different values:
*none*
*vibrate*
your ringtone name, taken from SavedSound value in the same key.
Hi,
Firstly I know this post is way old, but I thought of responding to it since I was stuck on this topic for a couple of days and couldnt find a way to resolve it.
Also others having this problem will also be redirected here through various search results as I was. So to aid them
1. Meddling with the registry to change the sound profiles didnt work for me. I tried Changing the Mode inHKCU\ControlPanel\Notifications\ShellOverrides\Mode but that didnt work on my phone.
2. What I currently have working is the following code - C# with Pinvoke
public enum SND_SOUNDTYPE
{
On,
File,
Vibrate,
None
}
private enum SND_EVENT
{
All,
RingLine1,
RingLine2,
KnownCallerLine1,
RoamingLine1,
RingVoip
}
[StructLayout(LayoutKind.Sequential)]
private struct SNDFILEINFO
{
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 260)]
public string szPathName;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 260)]
public string szDisplayName;
public SND_SOUNDTYPE sstType;
}
[DllImport("coredll.dll")]
public static extern void AudioUpdateFromRegistry ();
[DllImport("aygshell.dll", SetLastError = true)]
private static extern uint SndSetSound(SND_EVENT seSoundEvent, ref SNDFILEINFO pSoundFileInfo, bool fSuppressUI);
static void SetProfileNormal()
{
SNDFILEINFO soundFileInfo = new SNDFILEINFO();
soundFileInfo.sstType = SND_SOUNDTYPE.On;
uint num = SndSetSound(SND_EVENT.All, ref soundFileInfo, true);
AudioUpdateFromRegistry();
}
static void SetProfileVibrate()
{
SNDFILEINFO soundFileInfo = new SNDFILEINFO();
soundFileInfo.sstType = SND_SOUNDTYPE.Vibrate;
uint num = SndSetSound(SND_EVENT.All, ref soundFileInfo, true);
AudioUpdateFromRegistry();
}
static void SetProfileMuted()
{
SNDFILEINFO soundFileInfo = new SNDFILEINFO();
soundFileInfo.sstType = SND_SOUNDTYPE.None;
uint num = SndSetSound(SND_EVENT.All, ref soundFileInfo, true);
AudioUpdateFromRegistry();
}
Hope this helps
All REMOVED by user request.
How-to
catch going to suspend: http://forum.xda-developers.com/showthread.php?p=2776841#post2776841
* more reserved *
Following up from post getting OT in thread:
ycavan said:
My opengl es init function is called after UpdateWindow(), so I'm still stumped as to why the surface still cannot be created... here's my winmain:
Click to expand...
Click to collapse
You might be missing some of the modern WM6 calls. Here's the relevant code from the working ported version of Graphics for the Masses.
I started with working code from the AppWizard. tabs/spaces is pushing the formatting everywhere:
Code:
int WINAPI WinMain(HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPTSTR lpCmdLine,
int nCmdShow)
{
MSG msg;
HACCEL hAccelTable;
int retval;
// Perform application initialization:
if (!InitInstance(hInstance, nCmdShow))
{
return FALSE;
}
hAccelTable = LoadAccelerators(hInstance, MAKEINTRESOURCE(IDC_APPSTUFF));
// Main message loop:
for(;;)
{
switch(gMinimized_mode)
{
case 0:
game_loop:
while (PeekMessage(&msg, NULL, 0, 0, PM_NOREMOVE) == TRUE)
{
if (GetMessage(&msg, NULL, 0, 0) )
{
if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg))
{
TranslateMessage(&msg);
DispatchMessageW(&msg);
}
}
else
{
retval = (int) msg.wParam;
goto out;
}
}
if (!gMinimized_mode)
{
/* app code */
eglSwapBuffers (eglDisplay, eglWindowSurface);
if(retval == 0)
{
/* app exit code */
goto out;
}
}
break;
default:
while (GetMessage(&msg, NULL, 0, 0) )
{
if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg))
{
TranslateMessage(&msg);
DispatchMessageW(&msg);
}
if (msg.message == WM_PAINT)
{
gMinimized_mode = 0;
goto game_loop;
}
}
retval = (int) msg.wParam;
goto out;
}
}
out:
return retval;
}
ATOM MyRegisterClass(HINSTANCE hInstance, LPTSTR szWindowClass)
{
WNDCLASS wc;
wc.style = CS_HREDRAW | CS_VREDRAW;
wc.lpfnWndProc = WndProc;
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.hInstance = hInstance;
wc.hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_APPSTUFF));
wc.hCursor = 0;
wc.hbrBackground = NULL;//(HBRUSH) GetStockObject(WHITE_BRUSH);
wc.lpszMenuName = 0;
wc.lpszClassName = szWindowClass;
return RegisterClass(&wc);
}
BOOL InitInstance(HINSTANCE hInstance, int nCmdShow)
{
HWND hWnd;
TCHAR szTitle[MAX_LOADSTRING]; // title bar text
TCHAR szWindowClass[MAX_LOADSTRING]; // main window class name
g_hInst = hInstance; // Store instance handle in our global variable
// SHInitExtraControls should be called once during your application's initialization to initialize any
// of the device specific controls such as CAPEDIT and SIPPREF.
SHInitExtraControls();
//LoadString(hInstance, IDS_APP_TITLE, szTitle, MAX_LOADSTRING);
wcscpy(szTitle, appName);
LoadString(hInstance, IDC_OGLESEXFW, szWindowClass, MAX_LOADSTRING);
//If it is already running, then focus on the window, and exit
hWnd = FindWindow(szWindowClass, szTitle);
if (hWnd)
{
// set focus to foremost child window
// The "| 0x00000001" is used to bring any owned windows to the foreground and
// activate them.
SetForegroundWindow((HWND)((ULONG) hWnd | 0x00000001));
return 0;
}
if (!MyRegisterClass(hInstance, szWindowClass))
{
return FALSE;
}
hWnd = CreateWindow(szWindowClass, szTitle, WS_VISIBLE,
CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, NULL, NULL, hInstance, NULL);
if (!hWnd)
{
return FALSE;
}
// When the main window is created using CW_USEDEFAULT the height of the menubar (if one
// is created is not taken into account). So we resize the window after creating it
// if a menubar is present
if (g_hWndMenuBar)
{
RECT rc;
RECT rcMenuBar;
GetWindowRect(hWnd, &rc);
GetWindowRect(g_hWndMenuBar, &rcMenuBar);
rc.bottom -= (rcMenuBar.bottom - rcMenuBar.top);
MoveWindow(hWnd, rc.left, rc.top, rc.right-rc.left, rc.bottom-rc.top, FALSE);
}
ShowWindow(hWnd, nCmdShow);
UpdateWindow(hWnd);
{
/* EGL Setup */
EGLContext ctx;
EGLint majorVersion;
EGLint minorVersion;
//eglDisplay = eglGetDisplay(EGL_DEFAULT_DISPLAY);
eglDisplay = eglGetDisplay(GetDC(hWnd));
eglInitialize(eglDisplay, &majorVersion, &minorVersion);
eglConfig = select_config(eglDisplay, EGL_WINDOW_BIT, 16, 16, 4);
ctx = eglCreateContext(eglDisplay, eglConfig, NULL, NULL);
eglWindowSurface = eglCreateWindowSurface(eglDisplay, eglConfig, hWnd, NULL);
eglMakeCurrent(eglDisplay, eglWindowSurface, eglWindowSurface, ctx);
/* rest of app init */
}
return TRUE;
}
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
int wmId, wmEvent;
PAINTSTRUCT ps;
HDC hdc;
static SHACTIVATEINFO s_sai;
switch (message)
{
case WM_COMMAND:
wmId = LOWORD(wParam);
wmEvent = HIWORD(wParam);
// Parse the menu selections:
switch (wmId)
{
case IDM_HELP_ABOUT:
DialogBox(g_hInst, (LPCTSTR)IDD_ABOUTBOX, hWnd, About);
break;
case IDM_OK:
SendMessage (hWnd, WM_CLOSE, 0, 0);
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
break;
case WM_CREATE:
{
SHMENUBARINFO mbi;
memset(&mbi, 0, sizeof(SHMENUBARINFO));
mbi.cbSize = sizeof(SHMENUBARINFO);
mbi.hwndParent = hWnd;
mbi.nToolBarId = IDR_MENU;
mbi.hInstRes = g_hInst;
if (!SHCreateMenuBar(&mbi))
{
g_hWndMenuBar = NULL;
}
else
{
g_hWndMenuBar = mbi.hwndMB;
}
// Initialize the shell activate info structure
memset(&s_sai, 0, sizeof (s_sai));
s_sai.cbSize = sizeof (s_sai);
}
break;
case WM_PAINT:
hdc = BeginPaint(hWnd, &ps);
// TODO: Add any drawing code here...
EndPaint(hWnd, &ps);
if (!gMinimized_mode) eglSwapBuffer();
break;
case WM_DESTROY:
eglMakeCurrent(NULL, NULL, NULL, NULL);
eglDestroyContext(eglDisplay, eglContext);
eglDestroySurface(eglDisplay, eglWindowSurface);
eglTerminate(eglDisplay);
CommandBar_Destroy(g_hWndMenuBar);
PostQuitMessage(0);
break;
case WM_SIZE:
switch(wParam)
{
case SIZE_MINIMIZED:
gMinimized_mode = 1;
break;
case SIZE_MAXIMIZED: case SIZE_RESTORED: case SIZE_MAXSHOW:
gMinimized_mode = 0;
default:
{
RECT wrect;
GetClientRect(hwnd, &wrect);
/* app resize code */
break;
}
}
break;
case WM_ACTIVATE:
// Notify shell of our activate message
SHHandleWMActivate(hWnd, wParam, lParam, &s_sai, FALSE);
break;
case WM_SETTINGCHANGE:
SHHandleWMSettingChange(hWnd, wParam, lParam, &s_sai);
break;
case WM_KEYDOWN:
{
switch(wParam)
{
case(VK_UP):
break;
case(VK_DOWN):
break;
case(VK_LEFT):
break;
case(VK_RIGHT):
break;
case(VK_RETURN):
break;
}
if (wParam == VK_ESCAPE)
SendMessage(hwnd, WM_CLOSE, 0, 0);
break;
}
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
return 0;
}
I'm using evc4, so I can't build for a THUMB ( wince 5.0 ) device... any way you can recompile the lib for ARMv4?
Otherwise, I'm still linking w/ the Vincent lib.
Anyway, I started a whole new app ( appwizard hello template ) instead of writing the winmain from scratch and I still cannot generate a surface when I have anything but EGL_NONE in the config requests.
One thing I note is that fps is about 47-48, but once again, the texture is not mapped onto the object. If I put it in my folder w/ the Vincent dll, the texture is mapped and the fps is about 11-12.
I'm not quite familiar w/ EGL, but... does anyone know the EGLConfig structure? I tried searching for it to see what the requested width/height per config was so I can debug why the surface just doesn't get created.
anyway, here's my code:
View attachment TestApp2.zip
ycavan said:
I'm using evc4, so I can't build for a THUMB ( wince 5.0 ) device... any way you can recompile the lib for ARMv4?
Click to expand...
Click to collapse
That's ARMv4 or ARMv4i? I'm going to assume latter because that's what most of the dlls seem to be targeting. I'll put this up soon.
Meantime, GetClientRect() on your hwnd and make sure the rect is sane before you pass it on to eglCreateWindowSurface().
Also, Imageon may work better with GLfloat values although it probably kills Vincent (with software emulation on the ARM without native FP support in armv6 by the compiler) with anything but GLfixed. Try some benchmarking to see which works out better.
100000xtimes thanks!
can you make a sourceforce project??
NuShrike said:
That's ARMv4 or ARMv4i? I'm going to assume latter because that's what most of the dlls seem to be targeting. I'll put this up soon.
Meantime, GetClientRect() on your hwnd and make sure the rect is sane before you pass it on to eglCreateWindowSurface().
.
Click to expand...
Click to collapse
ARMv4 is what evc4 targets...
GetClientRect() is giving me very sane values, 268x240... 268 due to the bars up top & below.
I guess I'm just confused as to why Vincent is able to properly create the proper surface while our drivers cannot...
Edit:
//eglDisplay = eglGetDisplay(EGL_DEFAULT_DISPLAY);
eglDisplay = eglGetDisplay(GetDC(hWnd));
eglInitialize(eglDisplay, &majorVersion, &minorVersion);
eglConfig = select_config(eglDisplay, EGL_WINDOW_BIT, 16, 16, 4);
ctx = eglCreateContext(eglDisplay, eglConfig, NULL, NULL);
eglWindowSurface = eglCreateWindowSurface(eglDisplay, eglConfig, hWnd, NULL);
eglMakeCurrent(eglDisplay, eglWindowSurface, eglWindowSurface, ctx);
/* rest of app init */
Click to expand...
Click to collapse
I just realized that you don't provide egl config attribs for for eglCreateWindowSurface or for eglCreateContext... huh. Doing that, the surface and context are created ok, but the texture still isn't loaded... mebbe a problem w/ texture loading.
NuShrike said:
That's ARMv4 or ARMv4i? I'm going to assume latter because that's what most of the dlls seem to be targeting. I'll put this up soon.
Meantime, GetClientRect() on your hwnd and make sure the rect is sane before you pass it on to eglCreateWindowSurface().
Also, Imageon may with better with GLfloat values although it probably kills Vincent (with software emulation on the ARM without native FP support in armv6 by the compiler) with anything but GLfixed. Try some benchmarking to see which works out better.
Click to expand...
Click to collapse
In the words of the great Sammy L.
ENGLISH MOTHAF*CKA DO YOU SPEAK IT!?!
Click to expand...
Click to collapse
cp_kirkley said:
In the words of the great Sammy L.
Click to expand...
Click to collapse
That's just rude, dood... lol
Anywho, it looks like the problem w/ texturing is that our drivers do not seem to support mipmap. By commenting out the linear mipmap flag when loading the texture, we get a fully working opengl es test app. I get about 44 fps.
See for yourselves.
View attachment testapp2.zip
@nushrike, both of your libgles_cm.lib's are designed for the 'THUMB' device. It's ok. I'll just keep on linking to the vincent lib.
Just remember that our driver does not support mipmap.
I think it's eVC is too old then. It's been thumb since WinCE5. Most all the system libs I have to link to insiste on Thumb -- if I set it to ARM, it refuses to link. Hey, that's an idea.. I'll try ARM to see if it generates any .lib.
For hardware, or supported mipmapping (software or not), look up glGenerateMipmapOES(GL_TEXTURE_2D). The tech demo I posted earlier demonstrates this. It's that blurry looking rotating texture behind the checker-board and various other textures.
edit: nope, can't create a dummy .lib by trying to link ARM.
Instead, you can modify Vincent to fake-support the extended functions as you need them and as they exist in the HWA .dll exports list. Here's an example:
Code:
#ifdef __GL_EXPORTS
# define GL_API __declspec(dllexport)
#else
# define GL_API
#endif
#define GL_APICALL GL_API
#define GL_APIENTRY
#ifdef __cplusplus
extern "C" {
#endif
{
GL_APICALL void GL_APIENTRY glGenerateMipmapOES (GLenum target)
{ return; }
#ifdef __cplusplus
}
#endif
NetrunnerAT said:
can you make a sourceforce project??
Click to expand...
Click to collapse
No code to make it a SourceForge project. Now, if it was code to make the drivers, that would be worthy.
ycavan: figured out your bug. You have to pass NULL for configAttrib (the last parameter) for eglCreateContext, and eglCreateWindowSurface. Also, using glGenerateMipmapOES(GL_TEXTURE_2D) with your source, texture works fine now. Same FPS. I'm seeing if converting to GLfloat will speed it up.
edit: float is no speed increase so far, but no decrease either.
ycavan said:
105171
Click to expand...
Click to collapse
How do you get this to run? I've already asked you in PM that WinCE doesn't support the concept of "current working directory" and you use in your code.
I'm sorry about not responding to the pm's... lol, I just didn't see them.
Anyway, just unzip the file on your pc and copy the exe and resources folder to your device and run the exe. Just make sure to keep the exe and resources folder together.
ycavan said:
Anyway, just unzip the file on your pc and copy the exe and resources folder to your device and run the exe. Just make sure to keep the exe and resources folder together.
Click to expand...
Click to collapse
It doesn't work for me after I drop it into the "Program Files" folder because this code is hard-coded for root directory then.
really odd... i ran the program in its own directory w/o a problem... i'm @ work, so i'll check it out when i get home.
it looks like the "resources" directory must be on the root of the device...
I'm working on finding the cwd, but loading resources fails even though it's the correct wd...
You guys can try and see if this works...
Code:
TCHAR tPath[255];
char sPath[255];
// get full path to this executable
GetModuleFileName(NULL,tPath,255);
// find the last '\'
TCHAR* pos = wcsrchr(tPath,'\\');
// end the string after the last '\'
*(pos+1) = '\0';
// copy wide char array to multi-byte string
wcstombs(sPath,tPath,255);
sPath should contain the working directory... but my app is failing the resource loads even though this is the same full path as before...
this can be very intresting for xda flame with goforce? can you build the libs because there is no SDK avialeble, the test app doesn't work message:
OpenGL ES init error:
eglInitialize
jaikben22 said:
this can be very intresting for xda flame with goforce? can you build the libs because there is no SDK avialeble, the test app doesn't work message:
OpenGL ES init error:
eglInitialize
Click to expand...
Click to collapse
EDIT:
Just realized that you're talking about another phone w/ the goforce... lol
You will need to prolly google "goforce opengl es sdk" There should be quite a few out there.
On another note... I figured out the problem with working directory resource loading.
I prefer to make a call once when it's something configuration-related... like storing my current working directory... it seems that evc4 doesn't like it when I do that so I had to update mesh.cpp and texture.cpp to perform the GetModuleFileName calls in each load function.
Here's an updated TestApp2. You just need to keep testapp2.exe and the resources directory together.
View attachment testapp2.zip
Updates include:
+ fullscreen now
+ up/down rotation
+ left/right acceleration -100 to 100
+ spouting of particles
Have fun guys.
The resources directory contains these files that you can change if you want to try different things.
spot.raw - the particle system's balls texture
font.raw - the font texture
knot.gsd - the main object mesh
fire128.tga - the mesh object's texture
lol for goforce there are none zero nada
hi folks,
here is the code and I've seen basically the same thing from several sources, I compare with them all and it doesn't seem to be any problem with it:
Code:
private static String HttpCall(String url) throws URISyntaxException,
IOException {
BufferedReader in = null;
try {
HttpClient client = new DefaultHttpClient();
HttpGet request = new HttpGet();
request.setURI(new URI(url));
HttpResponse response = client.execute(request);
in = new BufferedReader(new InputStreamReader(response.getEntity()
.getContent()));
StringBuffer sb = new StringBuffer("");
String line = "";
String NL = System.getProperty("line.separator");
while ((line = in.readLine()) != null) {
sb.append(line + NL);
}
in.close();
Log.d("Pristina", "URL: " + url);
Log.d("Pristina", sb.toString());
return sb.toString();
} finally {
if (in != null) {
in.close();
}
}
}
I'm pretty much getting an HttpClient, HttpGet and HttpResponse, getting the InputStream out of it and returning it as a String... it was supposed to be piece of cake.
I'm using this to get a JSON object from a Wordpress blog which have the JSON plugin installed.
I tried the URL on my desktop chrome browser and the response is absolutely perfect, no problems. Most of the time the response from this code is fine too, it was working fine a few days ago... but now the response comes incomplete... I mean.. the string out of it is going fine as a JSON object and all of a sudden it's over. It end's as:
Code:
"width" : 500,
"height" : 500
}
}
}, {
"id" : 37525,
"url" :
no if's no buts... it's giving me the correct info and it says it will give me a url and that's it... the string is over!
From stack overflow someone suggested using EntityUtils.toString(response.getEntity()); and I still can't get a valid response. In this blogpost http://blog.dahanne.net/2009/08/16/how-to-access-http-resources-from-android/ the guy shows a few ways of doing an Http request I tried the 1st one and the final string I get it's still incomplete.
I would normally think it's something wrong with the JSONplugging installed on the server.. but the response is just fine from Chrome browser.
any ideas ??? I really don't know what else to do.
up!
anyone? please.... I've been desperate trying to find a solution for days now!
Just to "close" the post.
at the end was a mix of LogCat only showing a mix of X chars and a problem in a completely different point in my parser!
also at the end I found out this:
Code:
ret = new String(EntityUtils.toString(entity));
being a much cleaner code than the stupid loops.
if any forum moderator sees this feel free to close the thread.
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.