Hey XDA, this is my first guide and first proper contribution to the community!
I’m writing this because I've seen many people ask a variation of the question: “How can my app get information from a database?”
This guide is intended for those who have created their first app – it is assumed you have a working development environment and are reasonable comfortable with the Android SDK and Java. I'm also assuming little to no knowledge of PHP and MYSQL
This guide walks you through:
Setting up a database and a PHP script
Testing the server
Accessing it from Android.
To make it relevant, we're going to use data that we might see in an actual app: First & Last Name, Age and Points.
Requirements:
Android Device*
Computer*
Apache/PHP/MySQL Server – I use WAMP (for Windows) (PHP v 5.4)
Postman Rest Client for Google Chrome
(*Both must be connected to the same network!)
This guide will help you setup a local server. If you want to host your script and database online, you will have to purchase paid hosting.
Let's get started!
First off, what is a RESTful service?
According to Wikipedia: A RESTful web API (also called a RESTful web service) is a web API implemented using HTTP and REST principles.
How it works:
{
"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"
}
A breakdown of the steps:
The client makes a request using a HTTP POST to a server
The PHP script queries the MYSQL server
The PHP script gets the SQL data
The PHP script puts the data into an array and assigns keys for the values. The script then outputs the data as a JSON array. JSON (JavaScript Object Notation) is a standard for data exchange, and formats the data in a way both humans and computers can easily read.
The app parses the JSON and displays the data.
Code!
Part 1: The Server
We’re going to start by setting up the server!
Install WAMP server. Leave the settings at the default values.
Start WAMP server and let it come online.
Try and open http://localhost/phpmyadmin/ - if you installed it correctly, you should be greeted by the phpMyAdmin welcome screen. We're going to be using phpMyAdmin to create our database.
Creating the Database:
Create a database called ‘mytestdatabase’. Now click the SQL tab, paste in the following SQL Code and hit run. This will create a test table called ‘users’ and fill it with data.
The table contains 5 columns: id, FirstName, LastName, Age, Points. It has 6 rows of sample data.
SQL Code:
Code:
-- phpMyAdmin SQL Dump
-- version 3.5.1
-- http://www.phpmyadmin.net
--
-- Host: localhost
-- Generation Time: Jun 15, 2013 at 10:07 PM
-- Server version: 5.5.24-log
-- PHP Version: 5.3.13
SET SQL_MODE="NO_AUTO_VALUE_ON_ZERO";
SET time_zone = "+00:00";
/*!40101 SET [user=714032]@old_[/user][email protected]@CHARACTER_SET_CLIENT */;
/*!40101 SET [user=714032]@old_[/user][email protected]@CHARACTER_SET_RESULTS */;
/*!40101 SET [user=714032]@old_[/user][email protected]@COLLATION_CONNECTION */;
/*!40101 SET NAMES utf8 */;
--
-- Database: `MyTestDatabase`
--
-- --------------------------------------------------------
--
-- Table structure for table `users`
--
CREATE TABLE IF NOT EXISTS `users` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`FirstName` text NOT NULL,
`LastName` text NOT NULL,
`Age` int(11) NOT NULL,
`Points` int(11) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1 AUTO_INCREMENT=7 ;
--
-- Dumping data for table `users`
--
INSERT INTO `users` (`id`, `FirstName`, `LastName`, `Age`, `Points`) VALUES
(1, 'John', 'Doe', 25, 61),
(2, 'Glen', 'Willis', 55, 3145),
(3, 'Helen', 'Cook', 35, 1232),
(4, 'Karen', 'Johnson', 20, 6456),
(5, 'Bill', 'Cooper', 60, 3856),
(6, 'Mary', 'Gomez', 30, 5422);
/*!40101 SET CHARACTER_SET_CLIENT [user=714032]@old_[/user]CHARACTER_SET_CLIENT */;
/*!40101 SET CHARACTER_SET_RESULTS [user=714032]@old_[/user]CHARACTER_SET_RESULTS */;
/*!40101 SET COLLATION_CONNECTION [user=714032]@old_[/user]COLLATION_CONNECTION */;
Your database should now look like this:
We’re now ready to move on to the PHP!
Open up your WWW directory (C:\wamp\www) and create a new folder called ‘clientservertest’. In this folder, create a file called ‘login.php’.
Paste the following code into the file. (The PHP code is commented so you can follow what is going on)
PHP:
<?php
#Ensure that the client has provided a value for "FirstNameToSearch"
if (isset($_POST["FirstNameToSearch"]) && $_POST["FirstNameToSearch"] != ""){
#Setup variables
$firstname = $_POST["FirstNameToSearch"];
#Connect to Database
$con = mysqli_connect("localhost","root","", "mytestdatabase");
#Check connection
if (mysqli_connect_errno()) {
echo 'Database connection error: ' . mysqli_connect_error();
exit();
}
#Escape special characters to avoid SQL injection attacks
$firstname = mysqli_real_escape_string($con, $firstname);
#Query the database to get the user details.
$userdetails = mysqli_query($con, "SELECT * FROM users WHERE FirstName = '$firstname'");
#If no data was returned, check for any SQL errors
if (!$userdetails) {
echo 'Could not run query: ' . mysqli_error($con);
exit;
}
#Get the first row of the results
$row = mysqli_fetch_row($userdetails);
#Build the result array (Assign keys to the values)
$result_data = array(
'FirstName' => $row[1],
'LastName' => $row[2],
'Age' => $row[3],
'Points' => $row[4],
);
#Output the JSON data
echo json_encode($result_data);
}else{
echo "Could not complete query. Missing parameter";
}
?>
Testing the Script:
Try accessing http://localhost/clientservertest/login.php from your browser. Do you get this message:
"Could not complete query. Missing parameter"
Then it’s working! The script is looking for a POST variable called “FirstNameToSearch” – we didn't provide any, so it did't work!
To finish testing the script, open the Postman-REST client.
Set it up like so:
Request URL: http://localhost/clientservertest/login.php
Type: POST
Key: FirstNameToSearch
Value: John
Hit send, and you should see this:
Code:
{"FirstName":"John","LastName":"Doe","Age":"25","Points":"61"}
Congrats – your server just returned a result! Try some of the other names in the database (Glen, Helen, Karen, Bill, Mary) and see how their data is returned.
Note: Before we move on to the Android section, we’re going to have to put our WAMP server online. Click the WAMP icon in the taskbar and select 'Put Online'.
Find your computers local network IP address and insert it into the URL like so: http://192.168.1.112/clientservertest/login.php
You should be able to access the script. If this doesn't work, try turning off your firewall - it could be blocking the server.
Part 2: Android
We’re now going to use our Android device to access the web server instead of the Postman client.
I'm not going to go into detail with the boilerplate UI code - I've attached the source code to this post so you can download the project files and browse through them.
Note: Android 3.x+ cannot perform Network operations on the main thread. To solve this, we have to multithread our program. To keep this as simple as possible, we’re going to use an AsyncTask. Again, the code for this can be found in the project download.
Inside of the AsyncTask, we have the most important code - where we create and execute a HTTP POST in Java.
Creating and Executing a HTTP POST in Java:
We have to first setup the name-value pairs for our POST variables. In this case, we use "FirstNameToSearch" as our Key.
Code:
ArrayList<NameValuePair> nameValuePairs = new ArrayList<NameValuePair>();
nameValuePairs.add(new BasicNameValuePair("FirstNameToSearch", strNameToSearch));
The following code sets up connection timeouts (15 seconds) and creates a HttpClient and HttpPost pointing to our url (http://192.168.1.112/clientservertest/login.php)
Code:
//Create the HTTP request
HttpParams httpParameters = new BasicHttpParams();
//Setup timeouts
HttpConnectionParams.setConnectionTimeout(httpParameters, 15000);
HttpConnectionParams.setSoTimeout(httpParameters, 15000);
HttpClient httpclient = new DefaultHttpClient(httpParameters);
HttpPost httppost = new HttpPost("http://192.168.1.112/clientservertest/login.php");
httppost.setEntity(new UrlEncodedFormEntity(nameValuePairs));
The following code executes the POST, gets the result and converts it to a string:
Code:
HttpResponse response = httpclient.execute(httppost);
HttpEntity entity = response.getEntity();
String result = EntityUtils.toString(entity);
Finally, the following code creates a JSON object from the result string and extracts our data:
Code:
// Create a JSON object from the request response
JSONObject jsonObject = new JSONObject(result);
//Retrieve the data from the JSON object
strFirstName = jsonObject.getString("FirstName");
strLastName = jsonObject.getString("LastName");
intAge = jsonObject.getInt("Age");
intPoints = jsonObject.getInt("Points");
That's it. It's so simple!
Where do we take it from here?
This combination of PHP/MYSQL is quite powerful. I'd recommend that you learn more about these technologies and build upon the demo in this guide. PHP Tutorials & MySQL Tutorials
Ideas for practice apps:
Online notes application - Sync your notes to the cloud
Build an Activation Server - Users can activate an app with a key
Feedback
Please feel free to leave any followup questions, comments or suggestions! I'll try my best to respond!
You can find the source code over at GitHub. Have fun! (If you fix a bug, please send a pull request)
Additional Information
Changelog
November 3, 2013
Added a link to the GitHub repository.
June 26, 2013
Updated PHP Code. It's more reliable and uses the newer MySQL APIs. Thanks to @dbarrera & @vijai2011
July 7, 2013
Updated the Android project and added Internet permissions (ClientServerRESTDemo v2.zip)
Thanks for this amazing guide but I have a issue.I have my own table and columns.So I changed your php code according to that and when I do a post query in rest,I get all null.But if I do the same in phpmyadmin,I have results.
This is what I get in postman:
Code:
{"pid":null,"name":null,"UID":null,"mobile":null,"description":null,"created_at":null,"updated_at":null}
attached the output of phpmyadmin.
And my phpcode:
Code:
<?php
#Setup variables
$firstname = $_POST["FirstNameToSearch"];
#Avoid SQL injection attacks
$firstname = mysql_real_escape_string($firstname);
#Connect to Database
$con = mysql_connect("localhost","user","pass");
if (!$con)
{
die('Could not connect');
}
#Select the test database
mysql_select_db("mydb", $con);
#Get the user details from the database
$userdetails = mysql_query("SELECT * FROM mytable WHERE name = '$firstname'");
#Catch any errors
if (!$userdetails) {
echo 'It seems the server is down.Please try later';
exit;
}
#Get the first row of the results
$row = mysql_fetch_row($userdetails);
#Build the result array (Assign keys to the values)
$result_data = array(
'pid' => $row[1],
'name' => $row[2],
'UID' => $row[3],
'mobile' => $row[4],
'description' => $row[5],
'created_at' => $row[6],
'updated_at' => $row[7],
);
#Output the JSON data
echo json_encode($result_data);
?>
Thanks for help.
Edit: just found that your app is missing internet permission in manifest
Code:
<uses-permission android:name="android.permission.INTERNET" />
:thumbup:
Thanks so much for this. I will try it out soon.
Sent from my HTC Explorer A310e using xda app-developers app
vijai2011 said:
Thanks for this amazing guide but I have a issue.I have my own table and columns.So I changed your php code according to that and when I do a post query in rest,I get all null.But if I do the same in phpmyadmin,I have results.
This is what I get in postman:
Code:
{"pid":null,"name":null,"UID":null,"mobile":null,"description":null,"created_at":null,"updated_at":null}
attached the output of phpmyadmin.
Thanks for help.
Click to expand...
Click to collapse
I was able to reproduce the null result - it means that the result was not available in the database. You'll want to double check the value you are passing to the script in Postman. (When I used 'FirstNameToSearch' and 'test' - I got a correct result. However, when I used 'testa', I got a null result.)
You can try adding this into the PHP script to catch this problem:
Code:
#Get the first row of the results
$row = mysql_fetch_row($userdetails);
[B]#Check to see if a result was returned.
if(!$row){
echo 'User does not exist';
exit;
}[/B]
I also noticed a few things in your PHP script:
In your screenshot, your table name appears to be 'Myapp', however in your PHP script, it looks like you are using 'mytable'
When you build the result array at the end, you are trying to access a column that doesn't exist:
This code tries to access an 8th column/index:
Code:
'pid' => $row[1],
'name' => $row[2],
'UID' => $row[3],
'mobile' => $row[4],
'description' => $row[5],
'created_at' => $row[6],
'updated_at' => $row[7],
You only have seven columns, so it should be:
Code:
'pid' => $row[0],
'name' => $row[1],
'UID' => $row[2],
'mobile' => $row[3],
'description' => $row[4],
'created_at' => $row[5],
'updated_at' => $row[6],
vijai2011 said:
Edit: just found that your app is missing internet permission in manifest
Code:
<uses-permission android:name="android.permission.INTERNET" />
Click to expand...
Click to collapse
Good catch - Thanks! I'll update the project asap.
Alkonic said:
I was able to reproduce the null result - it means that the result was not available in the database. You'll want to double check the value you are passing to the script in Postman. (When I used 'FirstNameToSearch' and 'test' - I got a correct result. However, when I used 'testa', I got a null result.)
:snip:
Good catch - Thanks! I'll update the project asap.
Click to expand...
Click to collapse
That is not a error in table name because I just wanted to hide it out here but actually it got revealed in the screenshot .No issues will try your php code and correct my json array too.thanks
Sent from my GT-N7000 using xda app-developers app
For some reason,the problem was caused by the mysql_real_escape_string.I commented that line and it is working now.
vijai2011 said:
For some reason,the problem was caused by the mysql_real_escape_string.I commented that line and it is working now.
Click to expand...
Click to collapse
Now that's interesting.. I've never experienced a problem with that before.
I took a look at the PHP docs and found that mysql_real_escape_string() is depreciated - that could be contributing to the problem. I'll investigate this further and adjust the guide as necessary.
Thanks for sharing your solution!.
Alkonic said:
Now that's interesting.. I've never experienced a problem with that before.
I took a look at the PHP docs and found that mysql_real_escape_string() is depreciated - that could be contributing to the problem. I'll investigate this further and adjust the guide as necessary.
Thanks for sharing your solution!.
Click to expand...
Click to collapse
Maybe you are using ancient php module .BTW Can I also put data into tables using your php and slightly modifying "mysql_query" and using post?Or should I use put along with mysql_query?If later is the solution,could give me the snippet of how a put variable looks?because I dono php and I was waiting for someone to write this guide because before I was connecting to db with JDBS which isnt safe.Thanks and sorry for the trouble.
Edit: I got it to work like I said.But only issue is the created at and updated at time stamp which is not the part of php nor the app.I will correct it from mysql.Thanks once again.Will be happy to trouble you soon lol...No dont take it serious BTW
vijai2011 said:
Maybe you are using ancient php module .BTW Can I also put data into tables using your php and slightly modifying "mysql_query" and using post?Or should I use put along with mysql_query?If later is the solution,could give me the snippet of how a put variable looks?because I dono php and I was waiting for someone to write this guide because before I was connecting to db with JDBS which isnt safe.Thanks and sorry for the trouble.
Edit: I got it to work like I said.But only issue is the created at and updated at time stamp which is not the part of php nor the app.I will correct it from mysql.Thanks once again.Will be happy to trouble you soon lol...No dont take it serious BTW
Click to expand...
Click to collapse
Ha you beat me to it! I'll definitely try and add a section into the guide about updating tables. I appreciate your feedback on the guide, and I'm glad it helped you. Feel free to trouble me
Perfect! Just what i needed for my next week exam!
Sent from my GT-S5830M using Tapatalk 2
Super, thx!
But please tell me what I'm doing wrong.
Code:
HttpResponse response = httpclient.execute(httppost); //throw...
i save errors in log (attached)
pls help ;(
objaa said:
Super, thx!
But please tell me what I'm doing wrong.
Code:
HttpResponse response = httpclient.execute(httppost); //throw...
i save errors in log (attached)
pls help ;(
Click to expand...
Click to collapse
You are doing something on the main thread which actually has to be done in a different thread.I suspect its at line #119.If you show the entire code,somebody might point it out easily for you
Sent from my GT-N7000 using xda app-developers app
vijai2011 said:
You are doing something on the main thread which actually has to be done in a different thread.I suspect its at line #119.If you show the entire code,somebody might point it out easily for you
Sent from my GT-N7000 using xda app-developers app
Click to expand...
Click to collapse
ok, all code:
package com.nsp.obja;
import java.util.ArrayList;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.NameValuePair;
import org.apache.http.client.HttpClient;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.params.BasicHttpParams;
import org.apache.http.params.HttpConnectionParams;
import org.apache.http.params.HttpParams;
import org.apache.http.util.EntityUtils;
import android.os.Bundle;
import android.app.Activity;
import android.util.Log;
import android.view.Menu;
import android.widget.Toast;
public class MainActivity extends Activity {
@override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.myscreen);
post();
}
@override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.main, menu);
return true;
}
void post()
{
try
{
ArrayList<NameValuePair> nameValuePairs = new ArrayList<NameValuePair>();
nameValuePairs.add(new BasicNameValuePair("FirstNameToSearch", "wow"));
//Create the HTTP request
HttpParams httpParameters = new BasicHttpParams();
//Setup timeouts
HttpConnectionParams.setConnectionTimeout(httpParameters, 15000);
HttpConnectionParams.setSoTimeout(httpParameters, 15000);
HttpClient httpclient = new DefaultHttpClient(httpParameters);
HttpPost httppost = new HttpPost("http://www.xda-developers.com/");
httppost.setEntity(new UrlEncodedFormEntity(nameValuePairs));
HttpResponse response = httpclient.execute(httppost);
HttpEntity entity = response.getEntity();
String result = EntityUtils.toString(entity);
Toast.makeText(this, result.length(), Toast.LENGTH_LONG).show();
}
catch(Exception e)
{
Log.e("ClientServerDemoX", "Error: ", e);
Toast.makeText(this, e.getMessage(), Toast.LENGTH_LONG).show();
}
}
}
objaa said:
ok, all code:
<!----Snip!---->
Click to expand...
Click to collapse
Do post() in async because it has to do http request which needs to be done in another thread.
vijai2011 said:
You are doing something on the main thread which actually has to be done in a different thread.I suspect its at line #119.If you show the entire code,somebody might point it out easily for you
Sent from my GT-N7000 using xda app-developers app
Click to expand...
Click to collapse
vijai2011 said:
Do post() in async because it has to do http request which needs to be done in another thread.
Click to expand...
Click to collapse
hoooww :crying::crying:
Thanks, I figured out and got the code page of our glorious forum
I'm having an issue while testing the Query... I'm using Firefox and using RESTClient for debugging... and testing whatever value, db always responds null (See attachment)...
dbarrera said:
I'm having an issue while testing the Query... I'm using Firefox and using RESTClient for debugging... and testing whatever value, db always responds null (See attachment)...
Click to expand...
Click to collapse
Exactly what I experienced first time.Try after commenting the line which prevents mysql injection and see if it works.If you run latest mysql,the chances are probably that its the issue
vijai2011 said:
Exactly what I experienced first time.Try after commenting the line which prevents mysql injection and see if it works.If you run latest mysql,the chances are probably that its the issue
Click to expand...
Click to collapse
Already tried that... No go... Had to write the whole thing using w3schools example code as base... Just resolved a couple minutes ago and completed the project (it can be viewed @ Github:CardManager (App) and cardmanager_json (Web Service, only principal.php is the one handling the whole thing))...
Maybe a good add to the tutorial would be to have a config.php file with the user, passwd, database and table data calling it through require_once()... The DBConexion and DBGestion files (in my github) are supposed to do that, but didn't work either (hence doing the principal.php code all over again)...
dbarrera said:
Already tried that... No go... Had to write the whole thing using w3schools example code as base... Just resolved a couple minutes ago and completed the project (it can be viewed @ Github:CardManager (App) and cardmanager_json (Web Service, only principal.php is the one handling the whole thing))...
Maybe a good add to the tutorial would be to have a config.php file with the user, passwd, database and table data calling it through require_once()... The DBConexion and DBGestion files (in my github) are supposed to do that, but didn't work either (hence doing the principal.php code all over again)...
Click to expand...
Click to collapse
Interesting.. I'll take a look at at your script, revisit the W3 tutorials, and then re-write mine. It's really rudimentary and tends to fail easily. I wanted to write about the config.php, however, I also wanted to keep this guide as simple as possible for newer users. Maybe I'll add in an advanced section.
I'll update the guide in a few days, as I'm right in the middle of exams :/
Thanks for the feedback!
Related
As some people suggested I'm creating an specific thread for TrackMe's web development.
TrackMe is a free GPS/WiFi/Cell ID tracking tool. You can watch your tracks (saved or live) with Google Earth, Google Maps (or any tool that accepts KML or GPX files). It also includes many other options and features.
You can also watch your tracks from the web. This thread is focused on that part.
TrackMe is available for Android, Windows Mobile and Windows Phone 7. Check my signature for support for each version.
Visit TrackMe's main thread
If you are interested you can contribute working on one of the existing viewers or creating one of your own.
OFFICIAL RELEASE
Authors
OpitZle, pammetje, mcross, jcleek and _LEM_ (only TrackMe client<->server communication)
Database
MySQL
Language
PHP
Download
Press here to download the latest version
Screenshots
{
"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"
}
Here is some description for the TrackMe-Server communication
Requests.xxx file
Code:
All requests
------------
Result:1 User correct, invalid password.
Result:2 User did not exist but after being created couldn't be found.
Result:3 User or password not specified.
Result:4 Unable to connect database.
Result:5 Incompatible database.
Action="delete"
---------------
Result:0 OK
Action="deletetrip"
-------------------
Result:0 OK
Result:6 Trip not specified.
Result:7 Trip not found
Action="addtrip"
----------------
Result:0 OK
Result:6 Trip not specified.
Action="renametrip"
------------------
Result:0 OK
Result:6 Trip not specified.
Result:7 New name not specified.
Action="findclosestbuddy"
-------------------------
Result:0|DISTANCE|DATEOCCURRED|USERID
Result:6 User has no positions.
Result:7 No positions from other users found.
Action="gettriplist"
------------------
Result:0|NAME1|DATE2\nNAME2\DATE2\n ...
Result:6 Trip not specified.
Result:7 New name not specified.
Action="geticonlist"
------------------
Result:0|ICON1|ICON2|ICON3 ...
Action="upload"
------------------
Result:0 OK
Result:6 Trip didn't exist and system was unable to create it.
Result:7|SQLERROR Insert statement failed.
Action="sendemail"
------------------
Result:0 OK
Action="updateimageurl"
------------------
Result:0 OK
Action="findclosestpositionbytime"
----------------------------------
Result:0|POSITIONID|DATEOCCURRED
Result:6 Date not specified
Result:7 No position for user found.
Action="findclosestpositionbyposition"
--------------------------------------
Result:0|POSITIONID|DATEOCCURRED|DISTANCE
Result:6 Position not specified
Result:7 No position for user found.
Action="gettripinfo"
--------------------
Result:0|totalmiles|startdate|enddate|totaltime|totalpositions|maxspeed|avgspeed|maxaltitude|avgaltitude|minaltitude
Result:6 Trip not specified
Result:7 Trip not found
Action="gettriphighlights"
--------------------------
Result:0|Latitude1|Longitude1|ImageURL1|Comments1|IconURL1\nLatitude2|Longitude2|ImageURL2|Comments2|IconURL2\n ...
Result:6 Trip not specified
Result:7 Trip not found
I will be adding the rest of the files later...
Plugins
1. TrackMe (NMEA records) to OziExplorer (.plt) converter by tahdor
Description:
Simple perl script which takes the TrackMe (NMEA records) and put them as OziExplorer (.plt) format. Then you can open the .plf record in GpsVp to view the track.
Code:
# TrackMe.To.GpsVp.pl
#
$pll_ifname = "/Program Files/TrackMe/gpspositions.txt";
$pll_ofpath = "/Storage Card/Maps gpxVPTracks/";
#$pll_ifname = "gpspositions.txt";
#$pll_ofpath = "";
$pll_ofname = "gpspositions.plt";
$pll_ofname_date = "";
$maxtrack = 12;
sub pls_open_infile {
local ($pll_ifname, *PLL_F_IN_FPTR) = @_;
if (! open (PLL_F_IN_FPTR, "$pll_ifname")) {
warn "$pll_ifname: $!\n";
return -1;
}
return 0;
}
sub pls_open_outfile_overwrite {
local ($pll_outfname, *PLL_F_OUT_FPTR) = @_;
if (!open (PLL_F_OUT_FPTR, ">$pll_outfname")) {
warn "$pll_outfname: $!\n";
# die "$pll_outfname: $!\n";
return -1;
}
return 0;
}
sub pls_open_outfile_append {
local ($pll_outfname, *PLL_F_OUT_FPTR) = @_;
if (!open (PLL_F_OUT_FPTR, ">>$pll_outfname")) {
warn "$pll_outfname: $!\n";
# die "$pll_outfname: $!\n";
return -1;
}
return 0;
}
if (&pls_open_infile($pll_ifname, PLL_F_IN_FPTR) < 0) {
die "couldn't open output file $pll_ifname\n";
}
if (&pls_open_outfile_overwrite($pll_ofname, PLL_F_OUT_FPTR) < 0) {
die "couldn't open output file $pll_ofname\n";
}
sub output {
if ("$lat_rmc" ne "0") {
print PLL_F_OUT_FPTR sprintf ("%2.7f,%2.7f,0,%3.1f,%d,%s,%s\n", $lat_rmc,
$long_rmc, $alt_gga * 3.2808399, 0, $date, $time_rmc);
}
}
sub latitude {
my ($deg, $min) = unpack "a2a*", $_[0];
my $lat = $deg + $min / 60;
$lat = - $lat if $_[1] =~ /[Ss]/;
return $lat;
}
sub longitude {
my ($deg, $min) = unpack "a3a*", $_[0];
my $long = $deg + $min / 60;
$long = - $long if $_[1] =~ /[Ww]/;
return $long;
}
while ($line = <PLL_F_IN_FPTR>) {
chomp($line);
@field = split /[,*]/, $line;
SWITCH: {
# recommended minimum specific GPS/Transit data
if ($field[0] =~ /GPRMC/) {
# $time_rmc = join ':', unpack "a2" x 3, $field[1];
$time_rmc = join '.', unpack "a2" x 3, $field[1];
$ok_rmc = $field[2];
$lat_rmc = latitude(@field[3..4]);
$long_rmc = longitude(@field[5..6]);
$speed = $field[7];
$cmg = $field[8];
#$date = join '-', unpack "a2" x 3, $field[9];
$date = substr($field[9],4,2) .'-'. substr($field[9],2,2) .'-'. substr($field[9],0,2);
$mvar = $field[10] . $field[11];
# field[12] is checksum
if ($date ne $pll_ofname_date) {
$pll_ofname = $pll_ofpath . "Tm.$date.$time_rmc.plt";
$pll_ofname_date = $date;
if (&pls_open_outfile_overwrite($pll_ofname, PLL_F_OUT_FPTR) < 0) {
die "couldn't open output file $pll_ofname\n";
}
print PLL_F_OUT_FPTR "OziExplorer Track Point File Version 2.1\n";
print PLL_F_OUT_FPTR "WGS 84\n";
print PLL_F_OUT_FPTR "Altitude is in Feet\n";
print PLL_F_OUT_FPTR "Reserved\n";
print PLL_F_OUT_FPTR "0,2,128,,0,0,2,0\n";
print PLL_F_OUT_FPTR "1\n";
}
output();
last SWITCH;
}
# GPS fix data
if ($field[0] =~ /GPGGA/) {
$time_gga = join ':', unpack "a2" x 3, $field[1];
$lat_gga = latitude(@field[2..3]);
$long_gga = longitude(@field[4..5]);
$fixqual = $field[6];
$nsat = $field[7];
$hdop_gga = $field[8];
$alt_gga = $field[9];
# $field[10] is altitude units (always M)
$gheight = $field[11];
# $field[12] is geoid height units (always M)
$DGPS_age = $field[13];
$DGPS_ID = $field[14];
# field[15] is checksum;
last SWITCH;
}
}
}
2. User limitation for web viewer access by Real_Justus
Description:
Limit access to Web viewer for servers running in public mode
I was wondering how I could limit the access to my server. I wanted a group of users to be able to track all TrackMe clients via GoogleMaps.
The attached three files will enable a simple authorization for users. The server has to be installed accessable for everyone (public).
To create the authorziation, you have to edit .htaccess and htpasswd from the ZIP attached as follows:
1: Open .htaccess with the texteditor.
2: In the line "AuthUserFile" you have to add the WHOLE path to .htpasswd.
3: .htpasswd contains the users. The username is just plain text. The password is ht-encrypted. You can encrypt passwords here.
4: When both files are modifyed for your needs, upload them to the TrackMe directory on your server. If this is done correctly, a simple authorization is required for opening the webinterface for your TrackMe-server. All valid users can Track all clients.
If you don´t know the exactly path, upload phpinfo.php from the ZIP attached to the TrackMe directory on your server and open the file in your browser. (http://www.yourserver.com/TrackMe/phpinfo.php). Search for "Document_ Root" The path might be something like: /var/www/user/html/YourTrackMe/Directory. Insert the path in .htaccess
!!!Make sure to delete the phpinfo.php after use!!!
Files: Download
3. Plugin that allows you to assign multiple pictures to a single position by ElHozo
Description:
I've made a change in tod files of the server to allow view of multiple pictures in on dialog ballon. This (at least for me) is very usefull when you want to take many pictures from one place (no position change)
You have to edit to files
in request.php make this change to allow having multiples images in one single position record
Code:
if($action=="updateimageurl")
{
$imageurl = urldecode($_GET["imageurl"]);
$id = urldecode($_GET["id"]);
$iconid='null';
$result=mysql_query("Select ID FROM icons WHERE name = 'Camera'");
if ( $row=mysql_fetch_array($result) )
$iconid=$row['ID'];
/* HOZO 20/Jun/2009 : Esta es la modficacion que permite poner mas de una
* imagen en un solo punto, pone las url una atras de otra saparadas por un
* espacio. la linea que queda comentada es la original.
* Ver en index.php el cambio que las muestra en el globo del dialogo */
// BEGIN ORIGINAL CODE - MUST BE COMMENTED
//mysql_query("update positions set imageurl='$imageurl',fk_icons_id=$iconid where id=$id");
// END ORIGINAL CODE
// BEGIN NEW CODE
mysql_query("update positions set imageurl=CONCAT_WS(' ', imageurl, TRIM('".$imageurl."')), fk_icons_id='".$iconid."' where id='".$id."'");
// END NEW CODE
echo "Result:0";
die();
}
in index.php change this to show multiple images in one dialog balloon
Code:
if($row['ImageURL'])
{
/* HOZO 20/Jul/2009 : Agregar este pedazo de codigo en reemplazo del orginal,
* la ultima linea que esta comentada es la orgiinal.*/
// BEGIN NEW CODE
// Por las dudas me aseguro de eliminar espacios duplicados
$row['ImageURL'] = eregi_replace (" ", " ", $row['ImageURL']);
$row['ImageURL'] = eregi_replace (" ", " ", $row['ImageURL']);
$stFotos = explode (" ", trim($row['ImageURL']));
// Armo la cantidad de columnas y tamaño de la imagen del globo
// de acuerdo a la cantidad de fotos que haya para mostrar
if (count ($stFotos) == 1)
{
$inFotosCols = 1;
$inImgWidth = 200;
}
else
{
$inFotosCols = count ($stFotos);
if ($inFotosCols > 16)
$inFotosCols = 5;
else if ($inFotosCols > 9)
$inFotosCols = 4;
else if ($inFotosCols > 3)
$inFotosCols = 3;
$inImgWidth = ( 400 / $inFotosCols ) - 5;
}
$html .= "<tr><td>";
$html .= "<table>";
$inFotoIndex = 0;
while (list ($inKey, $szData) = each ($stFotos))
{
if ( ($inFotoIndex % $inFotosCols) == 0)
$html .= "<tr>";
$inFotoIndex++;
$html .= "<td><a><img></a></td>";
if ( ($inFotoIndex % $inF";
}
$html .= "</tr>";
$html .= "</table>";
$html .= "</td>";
$html .= "</tr>";
// END NEW CODE
// La que sigue es la linea orignal de codigo
// BEGIN ORIGINAL CODE - MUST BE COMMENTED
//$html .= " <tr><td><a><img></a></td></tr>";
// END ORIGINAL CODE
}
Know issues : The filed imgeurl in DB is limited to 255 chars, is better if you change this value to something bigger to fit more images urls.
I have included a ZIP with the two modified files
Files: Download
Great new thread!
Let the development begin!
With the advice of timoline I'm playing (and I'm trying to understand) with open-flash-chart.
Here is my first try :
When you move the mouse on the graphic, there is a sticker with the speed.
I'm trying to add the altitude on red with the Y axis right. But It doesn't works for now.
Thanks for your advice Timoline .
Is there some sort of ocumentation about the TrackMe <-> Webserver communication? It would be nice if you can write a little about this.
I point as I browsed the source I saw that the password was transmit unencrypted maybe you can change that to md5 or somthing similar?
Btw. Trackme is a great App
Diggen said:
Is there some sort of ocumentation about the TrackMe <-> Webserver communication? It would be nice if you can write a little about this.
I point as I browsed the source I saw that the password was transmit unencrypted maybe you can change that to md5 or somthing similar?
Btw. Trackme is a great App
Click to expand...
Click to collapse
Thanks.
Unfortunately there is not doc about the server-client communication.
However, feel free to ask any questions about that here.
Regarding the encrypted pass... yes, that's in the to-do list (actually it has been there for a long time) but there is something that I want to ask.
Let's suppose that from the client I do $encrypted = encode_md5(password)
And now that's what I send to the server.
On the server side, should I do $pass = decode_md5 ($encrypted) ?
or it's not necessary because I am going to store the pass already encrypted in the database?
And one more thing, what's really the purpose of that encoding? If somebody has access to the encrypted password, wouldn't he be able to do the same things than if it is not encrypted?
I mean if I my password is role392 and the encryped one is sadj2jkfDk43j54... what's really the difference? Both are like regular passwords.
I hope it makes sense.
It makes more or less sense
Normal a webapp stores the password 'encrypted' with md5 in its Database. From the Login it gets clearly send per Post and the server will 'encrypt' it. Then it verfy it against the md5 hash in the db. You will be still able to sniff it but if the DB gets hacked you will only have the md5 values which can't be decoded. Also because of the Post-Method the Pass is not clearly visible in the URL.
Thats not much secruity but a little more. Maybe you can XOR the md5 with the actuall hour/minute at the client side and send it. At server-side you will do the same with the saved md5 an look if they are the same. This is a little more secure but not much.
I hope you understand me and my 'broken' Englisch
For the Docs I will have a look at the code when there is Time because I'am on a tenancy changeover(?).
Language Update
I've updated the dutch part in the language.php from jcleek's web viewer.
See attachment:
I've been working on my own server development, at the moment I have:
1. Weather at the persons current or last postion.
2. Address of the persons current or last position (I think US only)
3. Geofence option that texts whoever the user wants to text when the user has entered a predefined area.
4. Ability to place markers on the map for whatever purpose the user chooses, business, personal or otherwise.
5. The server stores the mileage driven in each state or country if the user chooses for fuel tax or other reasons.
6. Server is not just limited to Trackme, it can also be an alturl for Mologogo, and a custom server for GPSGate. Server still retains all Trackme functionality.
http://fleettracking.fleettrack.net/maps.php
The frontend is still a little "raw", as I am concentrating on the back end for the moment.
Diggen said:
It makes more or less sense
Normal a webapp stores the password 'encrypted' with md5 in its Database. From the Login it gets clearly send per Post and the server will 'encrypt' it. Then it verfy it against the md5 hash in the db. You will be still able to sniff it but if the DB gets hacked you will only have the md5 values which can't be decoded. Also because of the Post-Method the Pass is not clearly visible in the URL.
Thats not much secruity but a little more. Maybe you can XOR the md5 with the actuall hour/minute at the client side and send it. At server-side you will do the same with the saved md5 an look if they are the same. This is a little more secure but not much.
I hope you understand me and my 'broken' Englisch
For the Docs I will have a look at the code when there is Time because I'am on a tenancy changeover(?).
Click to expand...
Click to collapse
Thanks a lot for the detailed explanation!!
I think I will start by encoding it in MD5 only in the database. I may add the XOR later.
Regards
WatskeBart said:
I've updated the dutch part in the language.php from jcleek's web viewer.
See attachment:
Click to expand...
Click to collapse
Thanks for that but I think you forgot to include that attachment.
Go3Team said:
I've been working on my own server development, at the moment I have:
1. Weather at the persons current or last postion.
2. Address of the persons current or last position (I think US only)
3. Geofence option that texts whoever the user wants to text when the user has entered a predefined area.
4. Ability to place markers on the map for whatever purpose the user chooses, business, personal or otherwise.
5. The server stores the mileage driven in each state or country if the user chooses for fuel tax or other reasons.
6. Server is not just limited to Trackme, it can also be an alturl for Mologogo, and a custom server for GPSGate. Server still retains all Trackme functionality.
http://fleettracking.fleettrack.net/maps.php
The frontend is still a little "raw", as I am concentrating on the back end for the moment.
Click to expand...
Click to collapse
hey looks good!! Let me know when you have something to release so I can add your viewer in the first post.
Thanks a lot for your contribution.
and please keep us updated!
Not so detailed but some basics maybe you will have a look at http://en.wikipedia.org/wiki/Md5 or http://en.wikipedia.org/wiki/Sha-1
Another thing, is it possible to have the server running on an selfsigned ssl server ?
The next point is. I've made a list of functions/ requests required by TrackMe on the Serverside.
Can you have a look at it staryon? The other aswell
I hope I made no mistake and all is there also its not very well layouted but better than nothing. Some function doesn't have an errorcode is this right? or not implentet a serverside?
Diggen said:
Not so detailed but some basics maybe you will have a look at http://en.wikipedia.org/wiki/Md5 or http://en.wikipedia.org/wiki/Sha-1
Another thing, is it possible to have the server running on an selfsigned ssl server ?
The next point is. I've made a list of functions/ requests required by TrackMe on the Serverside.
Can you have a look at it staryon? The other aswell
I hope I made no mistake and all is there also its not very well layouted but better than nothing. Some function doesn't have an errorcode is this right? or not implentet a serverside?
Click to expand...
Click to collapse
Unless I missed something it looks ok to me.
I just updated the second post inside this thread with information about the server-client communication.
Please let me know if you need information about a specific parameter.
Cheers
staryon said:
Unless I missed something it looks ok to me.
I just updated the second post inside this thread with information about the server-client communication.
Please let me know if you need information about a specific parameter.
Cheers
Click to expand...
Click to collapse
The only things which are from interest as well are the Date / Long / Lat / .... dataformats, but they are all in the DB like they transmited, if I am right?
Maybe one last thing have you some testing app or something else to test the communication without Trackme and the Phone because my Dataplan is not unlimited. Else I will make manual Request iin the webbrowser.
thanks for your quick responses
EDIT:
Ok found out some Formats
Date - YYYY-MM-DD HH:MM:SS
lon/lat - xx.XXXXXX -
WGS48 ? - but then the dot is moved 2 left and there are no East,West,Nord,South data? Can you help me here with the format?
BatteryStatus in % 0-100
Singalstrengh in dB ? -xxx
CID - X-X-XXX-XXXXX
For these I've no Data because there are no Trips with GPS in the DB, waiting for my X1.
Speed
Angle
Altitude
Diggen said:
Maybe one last thing have you some testing app or something else to test the communication without Trackme and the Phone because my Dataplan is not unlimited. Else I will make manual Request iin the webbrowser.
Click to expand...
Click to collapse
No, I don't have anything. Actually when I was developing the program I used the web browser as well.
Diggen said:
lon/lat - xx.XXXXXX -
WGS48 ? - but then the dot is moved 2 left and there are no East,West,Nord,South data? Can you help me here with the format?
Click to expand...
Click to collapse
The format is in decimal degrees.
Where 32.30642° N = 32.30642
or 122.61458° W = -122.61458
(I mean W and S are negative)
Diggen said:
BatteryStatus in % 0-100
Click to expand...
Click to collapse
Yes
Diggen said:
Singalstrengh in dB ? -xxx
Click to expand...
Click to collapse
yes, dBm.
Diggen said:
CID - X-X-XXX-XXXXX
Click to expand...
Click to collapse
MobileCountryCode-MobileNetworkCode-LocationAreaCode-CellID
Hope it helps!
Diggen said:
EDIT:
Ok found out some Formats
Date - YYYY-MM-DD HH:MM:SS
lon/lat - xx.XXXXXX -
WGS48 ? - but then the dot is moved 2 left and there are no East,West,Nord,South data? Can you help me here with the format?
BatteryStatus in % 0-100
Singalstrengh in dB ? -xxx
CID - X-X-XXX-XXXXX
For these I've no Data because there are no Trips with GPS in the DB, waiting for my X1.
Speed
Angle
Altitude
Click to expand...
Click to collapse
Here is how the GET is given to the server (this is an actual one pulled from my logs). I use CDMA, so I can't use CELLID:
requests.php?a=upload&u=xxxxxxxx&p=xxxxxxx&lat=37.54446&long=-76.8039466666667&do=2009-2-2%209:18:19&tn=02-01-09&alt=-31.4&sp=&ang=77.2&bs=100&db=8
I round the lat and lon to 5 places after the decimal as anything more is just wasted db usage.
Go3Team said:
I round the lat and lon to 5 places after the decimal as anything more is just wasted db usage.
Click to expand...
Click to collapse
You're right, I would like to truncate those values before being sent to the server one day...
staryon said:
Here is some description for the TrackMe-Server communication
Action="upload"
------------------
Result:0 OK
Result:6 Trip didn't exist and system was unable to create it.
Result:7|SQLERROR Insert statement failed.
Click to expand...
Click to collapse
I was wondering why the server was returning bytes to the client, and was trying to track down to see if it was an error. I am corrent when a Result:0 is returned, that everything was done fine?
here is a code, i have a basic login screen and if username is correct it need to swiitch to AndroidTab.java view, i cant write a correct code for it any one can help ?
Code:
[SIZE=2][COLOR=#0000c0][SIZE=2][COLOR=#0000c0]btnLogin[/COLOR][/SIZE][/COLOR][/SIZE][SIZE=2].setOnClickListener([/SIZE][B][SIZE=2][COLOR=#7f0055][SIZE=2][COLOR=#7f0055]new[/COLOR][/SIZE][/COLOR][/SIZE][/B][SIZE=2] OnClickListener() {[/SIZE]
[LEFT][SIZE=2][COLOR=#646464][SIZE=2][COLOR=#646464] @Override[/COLOR][/SIZE][/COLOR][/SIZE]
[LEFT][B][SIZE=2][COLOR=#7f0055][SIZE=2][COLOR=#7f0055] public [/COLOR][/SIZE][/COLOR][/SIZE][/B][B][SIZE=2][COLOR=#7f0055][SIZE=2][COLOR=#7f0055]void[/COLOR][/SIZE][/COLOR][/SIZE][/B][SIZE=2] onClick(View v) {[/SIZE]
[SIZE=2][COLOR=#3f7f5f][SIZE=2][COLOR=#3f7f5f] // Check Login[/COLOR][/SIZE][/COLOR][/SIZE]
[SIZE=2] String username =[/SIZE][SIZE=2][COLOR=#0000c0][SIZE=2][COLOR=#0000c0]etUsername[/COLOR][/SIZE][/COLOR][/SIZE][SIZE=2].getText().toString();[/SIZE][/LEFT]
[/LEFT]
[LEFT][SIZE=2] String password = [/SIZE][SIZE=2][COLOR=#0000c0][SIZE=2][COLOR=#0000c0]etPassword[/COLOR][/SIZE][/COLOR][/SIZE][SIZE=2].getText().toString();[/SIZE][/LEFT]
[LEFT][B][SIZE=2][COLOR=#7f0055][SIZE=2][COLOR=#7f0055] if[/COLOR][/SIZE][/COLOR][/SIZE][/B][SIZE=2](username.equals([/SIZE][SIZE=2][COLOR=#2a00ff][SIZE=2][COLOR=#2a00ff]"User1"[/COLOR][/SIZE][/COLOR][/SIZE][SIZE=2]) && password.equals([/SIZE][SIZE=2][COLOR=#2a00ff][SIZE=2][COLOR=#2a00ff]"pass"[/COLOR][/SIZE][/COLOR][/SIZE][SIZE=2])){[/SIZE]
[LEFT][SIZE=2][COLOR=#0000c0][SIZE=2][COLOR=#0000c0] lblResult[/COLOR][/SIZE][/COLOR][/SIZE][SIZE=2].setText([/SIZE][SIZE=2][COLOR=#2a00ff][SIZE=2][COLOR=#2a00ff]"Login successful."[/COLOR][/SIZE][/COLOR][/SIZE][SIZE=2]);[/SIZE][/LEFT]
[LEFT][SIZE=2][COLOR=#3f7f5f][SIZE=2][COLOR=#3f7f5f]/* ************************************[/COLOR][/SIZE][/COLOR][/SIZE]
[SIZE=2][COLOR=#3f7f5f][SIZE=2][COLOR=#3f7f5f]* ************************************[/COLOR][/SIZE][/COLOR][/SIZE][SIZE=2][COLOR=#3f7f5f]
[SIZE=2][COLOR=#3f7f5f]* [/COLOR][/SIZE]
[SIZE=2][COLOR=#3f7f5f]* [/COLOR][/SIZE]
[SIZE=2][COLOR=#3f7f5f]* CODE TO GO TO AndoidTab VIEW Class[/COLOR][/SIZE]
[SIZE=2][COLOR=#3f7f5f]* [/COLOR][/SIZE]
[SIZE=2][COLOR=#3f7f5f]* ************************************[/COLOR][/SIZE]
[SIZE=2][COLOR=#3f7f5f]**************************************/[/COLOR][/SIZE][/COLOR][/SIZE][/LEFT]
[/LEFT]
[SIZE=2][COLOR=#3f7f5f]
[/COLOR][/SIZE]
[LEFT][SIZE=2]} [/SIZE][B][SIZE=2][COLOR=#7f0055][SIZE=2][COLOR=#7f0055]else[/COLOR][/SIZE][/COLOR][/SIZE][/B][SIZE=2] {[/SIZE]
[LEFT][SIZE=2][COLOR=#0000c0][SIZE=2][COLOR=#0000c0]lblResult[/COLOR][/SIZE][/COLOR][/SIZE][SIZE=2].setText([/SIZE][SIZE=2][COLOR=#2a00ff][SIZE=2][COLOR=#2a00ff]"Login failed. Username and/or password doesn't match."[/COLOR][/SIZE][/COLOR][/SIZE][SIZE=2]);[/SIZE]
[SIZE=2] }[/SIZE]
[SIZE=2] }[/SIZE][/LEFT]
[SIZE=2]});[/SIZE][/LEFT]
(if u full code neccessery i can add it)
Look up Intent & startActivity(); there are alot of examples out there.
jug6ernaut said:
Look up Intent & startActivity(); there are alot of examples out there.
Click to expand...
Click to collapse
i know and im looking for an answer online too,
but atm i really could do with a quick fix lol
one learns nothing if everything is handed to them
With that said xD.
Code:
Intent myIntent = new Intent();
//being package name & activity name
myIntent.setClassName("jug6ernaut.lwp.tetris", "jug6ernaut.lwp.settings.ColorSettings");
startActivity(myIntent);
note that the activity will have to be identified in you manifest file.
also note that if you are not going to use your life first activity anymore you will probably want to remove it from the activity stack so that when you press the back button it doesnt reopen it.
I'm trying to access the hidden API for Phone.class to switch the preferred network mode via reflection.
I know this is not safe for porting to other android versions/roms and requires a system app to hold permission WRITE_SECURE_SETTINGS.
I Already managed to switch data via reflection and toggle gps via Settings.Secure.putString() by moving my app to system partition, so i think this should work too.
Does anyone have experience with reflection and/or root and is willing to help?
Of course I will share all my code/findings here.
Thanks in advance!
Links:
http://stackoverflow.com/questions/8607263/android-4-0-4g-toggle
http://stackoverflow.com/questions/5436251/how-to-access-setpreferrednetworktype-in-android-source
You can achieve this with a simple sqlite3 query instead of bothering with java reflection
Here's how :
In a terminal :
To enable 2G only
Code:
su
sqlite3 /data/data/com.android.providers.settings/databases/settings.db
insert into global values(null, 'preferred_network_mode', 1);
.exit
To enable 2G & 3G :
Code:
su
sqlite3 /data/data/com.android.providers.settings/databases/settings.db
insert into global values(null, 'preferred_network_mode', 0);
.exit
In java :
Example to enable 2G only (just replace the 1 with a 0 in the insert into to enable 2G & 3G)
Code:
try {
Process process = null;
process = Runtime.getRuntime().exec("su");
DataOutputStream os = new DataOutputStream(
process.getOutputStream());
os.writeBytes("sqlite3 /data/data/com.android.providers.settings/databases/settings.db" + "\n");
os.writeBytes("insert into global values(null, 'preferred_network_mode', 1);" + "\n");
os.writeBytes(".exit" + "\n");
os.writeBytes("exit\n");
os.flush();
process.waitFor();
} catch (IOException e) {
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
}
It might require a reboot to be active though, not sure
If it doesn't work on your rom (tested on cm10.1), it might be a different row name, try investigating your settings.db like this :
Code:
sqlite3 /data/data/com.android.providers.settings/databases/settings.db
.tables [COLOR="Green"]// returns all the tables[/COLOR]
select * from [I]tablename[/I]; [COLOR="green"]// in cm10.1 it's in [I]global[/I] but otherwise look in [I]secure[/I] and/or [I]system[/I]
// this command returns all the rows from the selected table, the names are usually explicit, so you should find the setting you're looking for[/COLOR]
Then, when you found it :
Code:
insert into [I]tablename[/I] values(null, [I]'row name'[/I], value);
Thanks, this is very helpful!
I think you are right, one will need to tell the system to update the settings or send a broadcast that this setting has changed (like for airplane mode).
I will look at the CM sources to find out what i can do.
superkoal said:
Thanks, this is very helpful!
I think you are right, one will need to tell the system to update the settings or send a broadcast that this setting has changed (like for airplane mode).
I will look at the CM sources to find out what i can do.
Click to expand...
Click to collapse
Glad it helped.
If you manage to find a way to make the settings read the db without a reboot, please share your findings, would be very useful.
Androguide.fr said:
Glad it helped.
If you manage to find a way to make the settings read the db without a reboot, please share your findings, would be very useful.
Click to expand...
Click to collapse
Of course i will!
Buddy... you can toggle network mode with intent, no root needed, nor permissions!!
Here´s entire sample:
Code:
package com.serajr.togglenetworkmode;
import android.os.Bundle;
import android.provider.Settings.Secure;
import android.provider.Settings.SettingNotFoundException;
import android.app.Activity;
import android.content.Intent;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
public class MainActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Button bnt = (Button) findViewById(R.id.button1);
bnt.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
// toggle
toggleNetworkMode();
}
});
}
private void toggleNetworkMode() {
int mode = getCurrentNetworkMode() + 1;
if (mode > 2) {
mode = 0;
}
// 0 = 3G_ONLY
// 1 = 3GSM_ONLY
// 2 = 3G_PREFERRED
// change mode
Intent intent = new Intent("com.android.phone.CHANGE_NETWORK_MODE");
intent.putExtra("com.android.phone.NEW_NETWORK_MODE", mode);
sendBroadcast(intent);
}
private int getCurrentNetworkMode() {
try {
int current = Secure.getInt(getContentResolver(), "preferred_network_mode");
return current;
} catch (SettingNotFoundException ex) {
return 0;
}
}
}
Try it and tell me later!!!
Thanks, but this will only work if your phone.apk has an exported receiver for this intent.
This is only the case if you or your rom dev modded it to be so.
Some custom ROMs also have this kind of "mod/bug" in the power widget, allowing you to toggle gps.
I tried it with slim bean 4.2 build 3 on i9000 and as expected it didn't work (and so will most likely in CM10.1 too).
Just too good to be true!
If youre following the root method Use RootTools for that
sak-venom1997 said:
If youre following the root method Use RootTools for that
Click to expand...
Click to collapse
Thanks, I already do.
Really helpful library!
And yes, I will do it by sql injection to settings.db
I only need to figure out what broadcasts have to be sent / methods called and if this is possible.
Argh I am kinda lost in CM sources.
Can anyone point me to the place where this is handled?
I managed to toggle flight mode by using this in a SU shell:
Code:
if(settingenabled)
{
executeCommand("settings put global airplane_mode_on 1");
executeCommand("am broadcast -a android.intent.action.AIRPLANE_MODE --ez state true");
}
else
{
executeCommand("settings put global airplane_mode_on 0");
executeCommand("am broadcast -a android.intent.action.AIRPLANE_MODE --ez state false");
}
Interstingly enough it didn't work if i manually injected the value and then sent the broadcast.
I have no idea why.
So atm I'm trying to do the same thing for preferred_network_mode. I can write the value to settings.db, but I just don't know what i have to do to make the system apply the setting yet.
EDIT: shell binary "settings" only works in android 4.2 (and probably upwards)
So i finally found it in CM Sources.
Seems like we need to get the currently running Instance of the Phone class to notify it about the change.
Also seems like we are back to reflection, as this is an internal system class.
I also killed com.android.phone process after updating the setting, but that didn't change anything.
EDIT:
1st step towards reflection:
I figured out that i need a jar file from the cm build to use system classes via reflection.
Does anyone have more information about this?
superkoal said:
So i finally found it in CM Sources.
Seems like we need to get the currently running Instance of the Phone class to notify it about the change.
Also seems like we are back to reflection, as this is an internal system class.
I also killed com.android.phone process after updating the setting, but that didn't change anything.
EDIT:
1st step towards reflection:
I figured out that i need a jar file from the cm build to use system classes via reflection.
Does anyone have more information about this?
Click to expand...
Click to collapse
Just a far fetched guess, but maybe try to add /system/framework/framework.jar from your rom to your app's build path
Is a reboot that unacceptable in your case ?
Maybe it's just the challenge, but i wanna change it like every other setting
A reboot is very far from what i call comfortable
superkoal said:
Maybe it's just the challenge, but i wanna change it like every other setting
A reboot is very far from what i call comfortable
Click to expand...
Click to collapse
Yeah, I get what you mean^^^
Here's a good (but old, 2009^^) official Android tutorial from the Android devs on reflection if it can help you : http://android-developers.blogspot.fr/2009/04/backward-compatibility-for-android.html
So i got some news
1. This setting is heavily protected by android system and can only be modified by the phone process itself
2. Some roms have a modified phone apk listening to a broadcast, enabling 3rd party apps to toggle
http://forum.xda-developers.com/showthread.php?t=1731187
3. His Majesty Chainfire started an app project which could amongst other features also toggle 2g/3g, but he gave up development. Regarding 2g/3g he implemented several methods, one of them being RIL injection, which he described as a really hard hack and highly experimental.
http://forum.xda-developers.com/showthread.php?t=807989
Seems this task is not that easy
But i found out that tasker can toggle 2g/3g on my phone (running slim bean 4.2.2), this is when i found out about the modified phone.apk from 2). So I'mcomfortable that it's working on my phone and i can at least develop an app for myself
Sent from my GT-I9000 using xda app-developers app
Debugging apps
This is my multi-post guide on debugging apps.
Android provides its own ways of debugging:
Logcat
The most important aspect when we talk about debugging. Whenever your app crashes, you can find the reason for that in the logs (as long as you install it from your IDE).
However, we can also use it for other ways of debugging. It can help to understand how the app acts and why it does so.
Toasts
Toasts are these small pop-ups you can see in many apps. You can use them for debugging as well.
Debugger in Eclipse or Android Studio
Eclipse and Android Studio have their own debuggers. You can execute the source code step by step and see all variable values during that.
AVDs
If a user reports that the layout does not look great on his device, you can check it using an AVD, often refered to as an emulator. It can help to understand how the app will look on other devices.
Using Google and posting on XDA
Google can help you very much with your problem. If you still cannot figure out what the reasons for your problem are, you can profit by the XDA community power. However, we need some information in order to help you.
This was featured on the XDA portal on June 29, 2013.
Logcat
As I have already stated, logcats are the most important aspect of debugging on Android.
You can use them to get error messages when your app crashed or to print your own debugging information.
Understand error messages
This is one example of an error message you can get:
Code:
06-15 12:45:02.205 805-805/? E/AndroidRuntime: FATAL EXCEPTION: main
java.lang.RuntimeException: Unable to start activity ComponentInfo{de.nikwen.myapplication/de.nikwen.myapplication.MainActivity}: java.lang.NullPointerException
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2180)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2230)
at android.app.ActivityThread.access$600(ActivityThread.java:141)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1234)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loop(Looper.java:137)
at android.app.ActivityThread.main(ActivityThread.java:5041)
at java.lang.reflect.Method.invokeNative(Native Method)
at java.lang.reflect.Method.invoke(Method.java:511)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:793)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:560)
at dalvik.system.NativeStart.main(Native Method)
Caused by: java.lang.NullPointerException
at de.nikwen.myapplication.MainActivity.doSomething(MainActivity.java:21)
at de.nikwen.myapplication.MainActivity.onCreate(MainActivity.java:17)
at android.app.Activity.performCreate(Activity.java:5104)
at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1080)
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2144)
... 11 more
The code producing the error:
Code:
package de.nikwen.myapplication;
import android.os.Bundle;
import android.app.Activity;
import android.view.Menu;
import android.widget.Button;
public class MainActivity extends Activity {
Button myButton;
@[B][/B]Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
doSomething();
}
private void doSomething() {
myButton.setText("Crashing here");
}
}
Now we are going to analyse the error.
The second line is the most important one. It tells you which kind of error ocurred. In this case it is a NullPointerException (NPE).
Code:
java.lang.RuntimeException: Unable to start activity ComponentInfo{de.nikwen.myapplication/de.nikwen.myapplication.MainActivity}: java.lang.[COLOR="Red"]NullPointerException[/COLOR]
The first thing to do now is searching for this type of error. When does it occur?
You come up with something like this:
Thrown when an application attempts to use null in a case where an object is required. These include:
Calling the instance method of a null object.
Click to expand...
Click to collapse
(Source: http://docs.oracle.com/javase/1.4.2/docs/api/java/lang/NullPointerException.html)
Now you know what kind of Exception was thrown. You can get a lot of other useful information from the logcat:
Usually, you get some more information about the error. There should be an explanation why the Exception was thrown:
Code:
java.lang.RuntimeException: [COLOR="Red"]Unable to start activity[/COLOR] ComponentInfo{de.nikwen.myapplication/de.nikwen.myapplication.MainActivity}: java.lang.NullPointerException
It also prints the stacktrace. The stacktrace contains all methods which have been invoked, the last one on the top.
In this case one exception caused another one. In most cases, the one which caused the other one is the one which is interesting for you.
Code:
Caused by: java.lang.NullPointerException
In this case the one which was thrown first is a NPE as well.
Let us have a closer look at the stacktrace:
Code:
[COLOR="Red"]at de.nikwen.myapplication.MainActivity.doSomething(MainActivity.java:21)
at de.nikwen.myapplication.MainActivity.onCreate(MainActivity.java:17)[/COLOR]
at android.app.Activity.performCreate(Activity.java:5104)
at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1080)
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2144)
The ones which are important for you usually are the ones with your package name (the red ones).
At the end of the line it tells you in which file and in which line the error occured.
Code:
at de.nikwen.myapplication.MainActivity.onCreate[COLOR="Red"](MainActivity.java:17)[/COLOR]
Here it is just the part of the code where the other method is called. So we look at the other one. (This most often happens when there are two of your methods. For that reason have a look at the upper one first.)
Code:
at de.nikwen.myapplication.MainActivity.doSomething[COLOR="Red"](MainActivity.java:21)[/COLOR]
It tells us MainActivity.java, line 21.
That is the line:
Code:
myButton.setText("Crashing here");
Remember, we got a NullPointerException. The only variable that can get null in that line is myButton. In fact we initialise it nowhere.
So this is our new onCreate method:
Code:
@[B][/B]Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
myButton = (Button) findViewById(R.id.my_button);
doSomething();
}
We solved the error. :victory:
Logcat - part II
Your own debug messages
We can also output our own debugging information that way:
Code:
Log.d("myTag", "myMessage");
or
Code:
Log.e("myTag", "myErrorMessage");
Error messages (produced with Log.e) are shown in red.
The tag can be used for filtering.
It can help us to check specific values when it does not return the right output:
Code:
Log.d("start", "hey");
String[] myStringArray = new String[] {"Hello world", "Debugging is fun (normally not ;))", "XDA is great!!!"};
String all = "Words: ";
for (String s: myStringArray) {
Log.d("String s", s);
all = all + s + ", ";
Log.d("new all", all);
}
Log.d("status", "done");
The output:
Code:
06-15 13:15:03.014 1347-1347/de.nikwen.myapplication D/start: hey
06-15 13:15:03.014 1347-1347/de.nikwen.myapplication D/String s: Hello world
06-15 13:15:03.014 1347-1347/de.nikwen.myapplication D/new all: Words: Hello world,
06-15 13:15:03.014 1347-1347/de.nikwen.myapplication D/String s: Debugging is fun (normally not ;))
06-15 13:15:03.014 1347-1347/de.nikwen.myapplication D/new all: Words: Hello world, Debugging is fun (normally not ;)),
06-15 13:15:03.014 1347-1347/de.nikwen.myapplication D/String s: XDA is great!!!
06-15 13:15:03.014 1347-1347/de.nikwen.myapplication D/new all: Words: Hello world, Debugging is fun (normally not ;)), XDA is great!!!,
06-15 13:15:03.014 1347-1347/de.nikwen.myapplication D/status: done
You can also use the log if one of your methods does not finish:
Code:
for (int i = 0; i < 20; i--) {
//do something
Log.d("i", String.valueOf(i));
}
Log.d("status", "1");
myButton.setText("New text");
Log.d("status", "2");
Log.d("status", "done");
You can figure out the reason for logical errors by printing the required information to the logs.
If there are Exceptions you want to handle but you still want the error standard Java error message discussed in the previous post, use the printStackTrace() method of the class Exception:
Code:
try {
Thread.sleep(5000);
} catch(Exception e) {
e.printStackTrace();
//do something here
}
Toasts
Toasts are these little windows you can see in many apps.
{
"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"
}
(Source: http://developer.android.com/images/toast.png)
You can also use them for debugging. You can output your values using Toasts instead of logcat messages.
Using Toasts for debugging is a great option, if you want to actually see the values when you test on a real device with no computer around, e.g. on the train.
It is also helpful if you want a customer to test your app. Using toasts, you can show him that something went wrong.
Do it that way:
Code:
Toast.makeText(getApplicationContext(), "My message", Toast.LENGTH_LONG).show();
(Do not forget the .show()!)
If you invoke this in an Activity, you can also do this:
Code:
Toast.makeText(this, "My message", Toast.LENGTH_LONG).show();
The disadvantage is that they are just shown for a short period of time.
Debugger in Eclipse
The debugger of Eclipse is a very productive tool. It allows you to see the values of the variables in real time. Additionally, you will be able to see and control exactly what your app is doing while it is running.
If we want to start our app in debug mode, we have to run it using the debug button.
To set or delete breakpoints we can right-click on the space left to the source code and select "Toggle Breakpoint". You will notice a blue dot next to the line. When the app is running, it will stop executing the code at the position of the breakpoint. The line with the breakpoint will not be executed.
When you run the app now, it will show a dialog which says that it is waiting for the debugger to attach.
You will also see a pop-up asking you to launch the debug perspective. Hit "Yes".
The code of your app will be executed, but it will stop before executing the line with your breakpoint. The current line which is not executed yet will be marked green.
Now you can decide what should be done next. The proper controls can be found in the debug perspective we opened before.
The one on the left will run the code until the next breakpoint will be reached. This will be the next thing we see if we define another breakpoint and press the "Resume" button:
The red button will disconnect the debugger. If you do this, your app will go on running as if no debugger had ever been attached.
Let us talk about the other buttons. The left one will make your program execute the current line and if it is a method, it will step into this method and you will be able to debug the other method step by step, too. It is called "Step into". The debugger will not be able to step into methods which are not defined by you or any library you added to your project.
If you want the debugger to execute the next line but (if the line is a method) not to step into the method, use the second button called "Step over". It will directly go to the next line.
If you are in a method and want to step out of it, use the third button. That means that it will leave the current method. Note that it will still run all code of the current method. Its name is "Step return".
In the picture above the buttons would cause this:
The "Step into" button will continue with the first line of the doSomething() method.
The "Step over" button will run the doSomething() and pause afterwards.
The "Step return" method will run the onCreate() method until its end and will pause then.
Now let us see how we can get the value of variables.
All variables used in the current method will be shown in the debugger view.
If there is an array, you can view its childs by clicking on the arrow next to it.
You can also view the fields of an object by clicking on the arrow.
If you hover of a variable in the code view, you will see its value, too.
I think that now you see why I call the debugger a productive tool.
And if I had shown you everything you can do with it, it would have been to long to post it on XDA. The ones I showed you are just the basic functions of the debugger.
Debugger in Android Studio
The debugger of Android Studio is a very powerful tool which allows you to see the values of the variables while your app is running on the emulator or a real device. Additionally, you will be able to see and control exactly what it is doing in real time.
In order to debug an app we need to start it using the debug button.
Then we can define or delete breakpoints by clicking on the space left to the source code. A red dot will appear there. When running the app, it will stop executing the code at the position of the breakpoint. The line with the breakpoint will not be executed.
When you run the app now, it will show a dialog which says that it is waiting for the debugger to attach.
The debug view will be opened automatically.
The code of your app will be executed, but it will stop before executing the line with your breakpoint. The current line which is not executed yet will be marked blue.
Now you can control how your app should continue. The proper controls can be found in the debug view.
The one on the left will run the code until the next brekpoint will be reached. It is called "Resume program execution". The other four buttons are for smaller steps. This will be the next thing we see if we define another breakpoint and press the "Resume program execution" button:
Let us talk about the other four buttons. The left one will make your program execute the current line and go to the next line. It is called "Step over". If you do this, it will not step into other methods and debug them.
If you want the debugger to step into the method, use the second button called "Step into". The debugger will not be able to step into methods which are not defined by you or any library you added to your project.
If you are in a method and want to step out of it, use the fourth button. That means that it will leave the current method. Note that it will still run all code of the current method. Its name is "Step out" (Surprise :laugh.
In the picture above the buttons would cause this:
The "Step over" button will run the doSomething() and pause afterwards.
The "Step into" button will continue with the first line of the doSomething() method.
The "Step out" method will run the onCreate() method until its end and will pause then.
Now we talk about getting variable values.
All variables available in the current method will be shown in the debugger view.
If there is an array, you can view its childs by clicking on the plus next to it.
You can also view the fields of an object by clicking on the plus.
To see a certain value you can also hover above the variable in your code.
After that you see that the debugger is a very powerful tool.
And this is just the beginning. You can do even more complex actions with it.
AVDs
I guess that nearly every developer for Android knows them and uses them: AVDs or emulators
They are great for testing your apps on other screen sizes or platform versions.
Before releasing your app, test them on different screen sizes and (most important) on different versions of Android. Some methods are just available on new API versions and crash on old versions. Another thing to mention: If your layout is designed for large screens, it might look bad on small ones. In the same manner phone layout often look ugly on a tablet.
Creating AVDs: Official documentation
ROOTING an AVD: Guide by Androguide.fr
Using Google
Google or any other search engine is great help when you debug your app. Nearly every error which occurs for you has already been experienced by others.
If you want to find out why your app crashes, search for this:
Android <the name of the Exception> <the method which causes the Exception>
Click to expand...
Click to collapse
If it is no Android specific Exception (e.g. a NumberFormatException when using Integer.parseInt("27")), replace "Android" by "Java".
If no method which is written by you is causing the crash, use keywords like these:
Android <the name of the Exception> <the name of the superclass> <when the crash occurs>
Click to expand...
Click to collapse
Example for these could be:
Android TextView setText CalledFromWrongThreadException
Android IllegalStateException Fragment orientation change
Java NumberFormatException Integer.parseInt
Click to expand...
Click to collapse
Often it is the best way to search for the message you find in the logs, e.g.
java.lang.RuntimeException: Unable to start activity ComponentInfo{}:
android.support.v4.app.Fragment$InstantiationException: Unable to instantiate fragment:
make sure class name exists, is public, and has an empty constructor that is public
Click to expand...
Click to collapse
Posting on XDA
This should be your last option. Try everything you can before posting here. Though posting your problem on XDA is nothing you should be ashamed of.
However, please follow these steps to ensure that we are able to help you properly:
Give us your code.
Do not worry. You do not have to publish all of your code but the relevant parts. That means the class which causes the Exception. If you can exclude some methods to be the reason for the Exception, you do not need to post them. But do this only if you are sure that they are not important for us.
Post a logcat.
As you can see in post #2, logcats are the most important information on the bug and the reasons why it crashes.
Describe the problem.
Tell us when the code crashes. That might be that it crashes when you change the screen orientation, press the menu or back key or when a particular method is invoked. Many of us do not execute your code but just look at it in the browser. Hence we need to know when the code crashes.
Liked this tutorial?
Check out my interactive tutorial on how to use the command line.
very very useful thanks
matt95 said:
very very useful thanks
Click to expand...
Click to collapse
You are welcome.
I am glad that you like it.
Btw, I will update it soon.
Updated the guide:
Post #8
Post #9
This is very helpful! When I was reading this in my book it was a little confusing but now I get it.
FlyLikeAGS3 said:
This is very helpful! When I was reading this in my book it was a little confusing but now I get it.
Click to expand...
Click to collapse
Great. :good:
Posted the guide for the debugger of Android Studio.
Post #6
Added the Eclipse debugging tutorial.
Now I am done.
Post #5
I vote for another sticky by Nikwen!
Zatta said:
I vote for another sticky by Nikwen!
Click to expand...
Click to collapse
Thanks.
If it is a sticky, people will see it. If not, nobody will see it due to the number of new posts every day.
(@mark manning)
I'll look at it later on when I get a sec
Great guide!
I need a way to search in StorageFiles with dynamically pattern, which comes from a TextBox. The directive "Windows.Storage.Search" doesnt exist in windows phone 8.1 runtime, as i saw. Now my question is, how can i do this in alternative way?
The only way to do it with WP 8.1 since Microsoft ALWAYS fails to implement the important things are to query using LINQ.
Ex:
Code:
var result = (await Windows.Storage.ApplicationData.Current.LocalFolder.GetFilesAsync(Windows.Storage.Search.CommonFileQuery.OrderByName)).
Where(x => x.Name.
Contains(x => txtBox.Text));
That's about all you can do pretty much. (Thanks Microsoft).
Thank you for the example. But it wont work for me, it shows me the following error(s):
Code:
A local variable named 'x' cannot be declared in this scope because it would give a different meaning to 'x', which is already used in a 'parent or current' scope to denote something else
and
Code:
Cannot convert lambda expression to type 'string' because it is not a delegate type
Thats really odd from Microsoft, that they havent implementet the search function like in WinRT (Windows Store App).
The first error is pretty simple. You already have the variable named "x" and it would be very bad if compiler didn't give you that error.
Change the name of the variable to something else that you don't use in that scope and it will work.
And for second problem, try this one:
Code:
private List<string> Result()
{
var result = ((List<Windows.Storage.Search.CommonFileQuery>)Windows.Storage.ApplicationData.Current.LocalFolder.GetFilesAsync(Windows.Storage.Search.CommonFileQuery.OrderByName)).Where(x => x.ToString().Contains(txtBox.Text));
return result as List<string>;
}
private async Task<List<string>> ResultAsync()
{
return await Task.Run(() => Result()).ConfigureAwait(continueOnCapturedContext: false);
}
You should call ResultAsync method and get the result in this way:
Code:
List<string> myList = ResultAsync().Result;
That's not going to work. You can't cast a StorageFile as a string.
To fix my code (simple lambda typo)
Code:
var result = (await Windows.Storage.ApplicationData.Current.LocalFolder.GetFilesAsync(Windows.Storage.Search.CommonFileQuery.OrderByName)).
Where(x => x.Name.
Contains(txtBox.Text));
if(result.Any())
{
// Do shtuff
}
Also, you should never access the .Result of an async task because you never know if it completed yet.
Ok, first error is done, but the second error is still here
Code:
Cannot convert lambda expression to type 'string' because it is not a delegate type
You are missing the point of the TAP (Task Async Pattern).
Both main thread and async method will be in execution in the same time. When the async method finish his work, main thread will stop and catch the result trough the Result property.
TAP is the recommended way of asynchronous programming in C#. The only thing with TAP is to use ConfigureAwait method in non-console type of apps to avoid deadlock.
Sooner or later you will get the result from TAP method. Nothing will get in the conflict with the main thread.
Oh wait, @andy123456 I updated my response. I forgot String.Contains ISNT a lambda .
@Tonchi91, I know all about the TAP. I've been using it since it was CTP. I've seen the awkward situations with threading in WP .
Now... if he did
Code:
List<string> myList;
ResultAsync().ContinueWith(t=> { myList = t.Result; });
I wouldn't be worried .
Ok the errors are gone, but the debugger show me the following exception:
Code:
Value does not fall within the expected range
Is this search method case-sensitive? I tried with an exact input in the TextBox.
Hmmm. Let's see your full code.
its actually only for testing, so i added your code to a button (asnyc) and will show the output in a textBlock.
Code:
private async void buttonTest_Click(object sender, RoutedEventArgs e)
{
//Result();
var result = (await Windows.Storage.KnownFolders.CameraRoll.GetFilesAsync(Windows.Storage.Search.CommonFileQuery.OrderByName)).
Where(x => x.Name.
Contains(textBox_test.Text));
if (result.Any())
{
// Do shtuff
textBlock_test.Text = result.ToString();
}
}
The error is coming from here
Code:
var result = (await Windows.Storage.KnownFolders.CameraRoll.GetFilesAsync(Windows.Storage.Search.CommonFileQuery.OrderByName))
andy123456 said:
its actually only for testing, so i added your code to a button (asnyc) and will show the output in a textBlock.
Code:
private async void buttonTest_Click(object sender, RoutedEventArgs e)
{
//Result();
var result = (await Windows.Storage.KnownFolders.CameraRoll.GetFilesAsync(Windows.Storage.Search.CommonFileQuery.OrderByName)).
Where(x => x.Name.
Contains(textBox_test.Text));
if (result.Any())
{
// Do shtuff
textBlock_test.Text = result.ToString();
}
}
The error is coming from here
Code:
var result = (await Windows.Storage.KnownFolders.CameraRoll.GetFilesAsync(Windows.Storage.Search.CommonFileQuery.OrderByName))
Click to expand...
Click to collapse
Oh Camera Roll.. You MIGHT need to have the capability to view the camera roll enabled. I forget what it's called, but you need a specific cap in order to view from there. Also, I would try to see if you can use a generic folder instead.
I would try Windows.Storage.ApplicationData.Current.LocalFolder.GetFilesAsync() as your method after the await just to test whether you can read correctly.
Yes but in wp8.1 runtime app, there arent caps anymore. The capability for access to the pictures is simply calles pictures library and is enabled. I have tested it as you said, but it gives me the same exception.
A quick tip: another way to do this is to use the Win32 C runtime API. You can, for example, use the FindFirst/NextFile functions (http://msdn.microsoft.com/en-us/library/windows/desktop/aa364418(v=vs.85).aspx) which support searches using wildcards (* and ? characters in the first parameter). These functions are wrapped in my NativeLibraries classes, but are also just publicly available for third0party developers to call from their own C++ DLLs.
Alternatively, you can use the .NET System.IO.Directory class, which has functions like EnumerateFiles(String path, String searchPattern). This is probably the better way to do it, actually.
Of course, if you want these operations to not block the current thread, you'll need to explicitly put them in their own thread or async function.
EDIT: This also assumes you have read access to the relevant directories. You application data directory works fine, for example (you can get its path from the relevant StorageFolder object). Other directories that can be accessed via WinRT functions may go through a broker function instead of being directly readable.
The point is, that i have an array with filenames. Now i need the StorageFile files which contains these filenames. My idea was to search for these files and return the files as StorageFile, so i can work with these. Or is there a simpler / another way?
http://msicc.net/?p=4182 <-- try this
Thank you, i have already done this and its working. But how can i compare the Files to read, with already read files and take only the not yet read files?