JavaCreateFolderDemo.java
package com.pmstation.api.demo;
import java.rmi.RemoteException; import javax.xml.rpc.ServiceException; import com._4shared.api.servlet.services.DesktopApp.DesktopApp; import com._4shared.api.servlet.services.DesktopApp.DesktopAppServiceLocator; public class CreateFolderDemo extends Demo { public static void main(String[] args) { try { DesktopApp da = new DesktopAppServiceLocator().getDesktopApp(); long res = da.createNewFolder(LOGIN, PASSWORD, -1, "newfolder"); switch ((int)res) { case 0: System.out.println("Folder creation failed"); break; case -1: System.out.println("Folder already exists"); break; case -2: System.out.println("Attempted to create folder in recycled parent"); break; default: System.out.println("Folder created"); break; } } catch (ServiceException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (RemoteException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } DeleteFileDemo.java
package com.pmstation.api.demo;
import java.rmi.RemoteException; import javax.xml.rpc.ServiceException; import com._4shared.api.AccountItem; import com._4shared.api.servlet.services.DesktopApp.DesktopApp; import com._4shared.api.servlet.services.DesktopApp.DesktopAppServiceLocator; import com.pmstation.api.local.URLGetter; public class DeleteFileDemo extends Demo { public static void main(String[] args) { boolean success = false; try { DesktopApp da = new DesktopAppServiceLocator().getDesktopApp(); AccountItem[] items = da.getAllItems(LOGIN, PASSWORD); for(AccountItem item : items) { if(!item.isDirectory() && item.getName().endsWith(".tst")) { da.deleteFile(LOGIN, PASSWORD, item.getId()); da.deleteFile(LOGIN, PASSWORD, item.getId()); } } System.out.println("Test files removed"); success = true; } catch (ServiceException e) { e.printStackTrace(); } catch (RemoteException e) { e.printStackTrace(); } finally { if (!success) System.out.println("Delete failed."); } } } Demo.java
package com.pmstation.api.demo;
public abstract class Demo { public static final String LOGIN = "test@api.4shared.com"; //you can indicate your client by replacing demo_api in password. Or just rip all from start to !!: from this public static final String PASSWORD = "!!!demo_api!!:4sharedapi"; } DownloadDemo.java
package com.pmstation.api.demo;
import java.io.BufferedInputStream; import java.io.BufferedOutputStream; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.net.MalformedURLException; import java.net.URL; import java.rmi.RemoteException; import javax.xml.rpc.ServiceException; import com._4shared.api.AccountItem; import com._4shared.api.servlet.services.DesktopApp.DesktopApp; import com._4shared.api.servlet.services.DesktopApp.DesktopAppServiceLocator; import com.pmstation.api.local.URLGetter; /** * Demonstrates file download. There should be something on user account for * this demo to work. * * @author Ivan Volzhev * */ public class DownloadDemo extends Demo { public static final int MB = 1024 * 1024; public static void main(String[] args) { boolean success = false; try { DesktopApp da = new DesktopAppServiceLocator().getDesktopApp(); AccountItem[] items = da.getAllItems(LOGIN, PASSWORD); for (AccountItem accountItem : items) { if (accountItem.isDirectory() || accountItem.getSize() >= 5 * MB) continue; String link = da.getDirectLink(LOGIN, PASSWORD, accountItem.getDownloadLink()); //accountItem.getDownloadLink()); if (!StringUtils.isEmpty(link) && link.startsWith("http")) { URL url = new URL(link); BufferedInputStream bis = new BufferedInputStream(url .openStream()); File file = new File(accountItem.getName()); BufferedOutputStream bos = new BufferedOutputStream( new FileOutputStream( file)); int b = 0; while ((b = bis.read()) != -1) { bos.write(b); } System.out.println(file.getAbsolutePath() + " saved"); da.downloadFinished(LOGIN, PASSWORD, accountItem.getId()); success = true; break; } } } catch (ServiceException e) { e.printStackTrace(); } catch (RemoteException e) { e.printStackTrace(); } catch (MalformedURLException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } if (!success) System.out.println("Read failed."); } } EnumerateItemsDemo.java
package com.pmstation.api.demo;
import java.rmi.RemoteException; import java.util.Arrays; import java.util.Collections; import java.util.Comparator; import javax.xml.rpc.ServiceException; import com._4shared.api.AccountItem; import com._4shared.api.servlet.services.DesktopApp.DesktopApp; import com._4shared.api.servlet.services.DesktopApp.DesktopAppServiceLocator; /** * Demonstrates a way to get user's root folder items. * * @author Ivan Volzhev * */ public class EnumerateItemsDemo extends Demo { public static void main(String[] args) { try { DesktopApp da = new DesktopAppServiceLocator().getDesktopApp(); AccountItem[] items = da.getAllItems(LOGIN, PASSWORD); if (items == null || items.length == 0) { System.out.println("Empty root folder or failed login"); } else { Arrays.sort(items, new Comparator<AccountItem>() { public int compare(AccountItem arg0, AccountItem arg1) { if (arg0.isDirectory() && !arg1.isDirectory()) return -1; if (!arg0.isDirectory() && arg1.isDirectory()) return 1; return arg0.getName().compareTo(arg1.getName()); } }); for (AccountItem item : items) { System.out.println(item.getName() + (item.isDirectory() ? "/" : "")); } } } catch (ServiceException e) { e.printStackTrace(); } catch (RemoteException e) { e.printStackTrace(); } } } EnumerateItemsDemo2.java
package com.pmstation.api.demo;
import java.rmi.RemoteException; import java.util.Arrays; import java.util.Comparator; import javax.xml.rpc.ServiceException; import com._4shared.api.AccountItem; import com._4shared.api.servlet.services.DesktopApp.DesktopApp; import com._4shared.api.servlet.services.DesktopApp.DesktopAppServiceLocator; /** * Demonstrates user folder tree traversing * * @author Ivan Volzhev * */ public class EnumerateItemsDemo2 extends Demo { private static final int PAD_SIZE = 2; public static void main(String[] args) { try { DesktopApp da = new DesktopAppServiceLocator().getDesktopApp(); System.out.println(LOGIN + " folder contains:"); if(!listTree(da, -1, 0)) System.out.println("Is empty"); } catch (ServiceException e) { e.printStackTrace(); } catch (RemoteException e) { e.printStackTrace(); } } /** * * @param da DesktopApp service object * @param dirId from which to start * @param level recursion level for pretty printing * @return true if found something * @throws RemoteException */ private static boolean listTree(DesktopApp da, long dirId, int level) throws RemoteException { AccountItem[] items = da.getItems(LOGIN, PASSWORD, dirId); boolean res = false; if (items != null && items.length != 0) { Arrays.sort(items, new Comparator<AccountItem>() { public int compare(AccountItem arg0, AccountItem arg1) { if (arg0.isDirectory() && !arg1.isDirectory()) return -1; if (!arg0.isDirectory() && arg1.isDirectory()) return 1; return arg0.getName().compareTo(arg1.getName()); } }); for (AccountItem item : items) { System.out.println(StringUtils.padRight("", level * PAD_SIZE) + item.getName() + (item.isDirectory() ? "/" : "")); if (item.isDirectory()) listTree(da, item.getId(), level + 1); } res = true; } return res; } } LoginDemo.java
package com.pmstation.api.demo;
import java.rmi.RemoteException; import javax.xml.rpc.ServiceException; import com._4shared.api.servlet.services.DesktopApp.DesktopApp; import com._4shared.api.servlet.services.DesktopApp.DesktopAppServiceLocator; import com.pmstation.api.local.URLGetter; /** * Demonstrates usage of web service's login method. * * * Created: Mon Mar 29 12:00:17 2010 * * @author Ivan Volzhev * @version 1.0 */ public final class LoginDemo extends Demo { public static void main(final String[] args) { DesktopApp da = null; try { da = new DesktopAppServiceLocator().getDesktopApp(); String res = da.login(LOGIN, PASSWORD); if (StringUtils.isEmpty(res)) { System.out.println("Login succesfull"); } else { System.out.println("Login failed: " + res); } boolean res1 = da.isAccountActive(LOGIN, PASSWORD); if(res1) System.out.println("logged in"); else System.out.println("WTF???"); } catch (ServiceException e) { e.printStackTrace(); } catch (RemoteException e) { e.printStackTrace(); } } } RecycleBinDemo.java
package com.pmstation.api.demo;
import java.rmi.RemoteException; import javax.xml.rpc.ServiceException; import com._4shared.api.AccountItem; import com._4shared.api.servlet.services.DesktopApp.DesktopApp; import com._4shared.api.servlet.services.DesktopApp.DesktopAppServiceLocator; import com.pmstation.api.local.URLGetter; public class RecycleBinDemo extends Demo { public static void main(String[] args) { boolean success = false; try { DesktopApp da = new DesktopAppServiceLocator().getDesktopApp(); AccountItem[] items = da.getRecycleBinItems(LOGIN, PASSWORD); for(AccountItem item : items) { da.deleteFile(LOGIN, PASSWORD, item.getId()); } System.out.println("Recycle bin was cleaned"); success = true; } catch (ServiceException e) { e.printStackTrace(); } catch (RemoteException e) { e.printStackTrace(); } finally { if (!success) System.out.println("Delete failed."); } } } RenameDemo.java
package com.pmstation.api.demo;
import java.rmi.RemoteException; import javax.xml.rpc.ServiceException; import com._4shared.api.servlet.services.DesktopApp.DesktopApp; import com._4shared.api.servlet.services.DesktopApp.DesktopAppServiceLocator; public class RenameDemo extends Demo { public static void main(String[] args) { try { DesktopApp da = new DesktopAppServiceLocator().getDesktopApp(); long res = da.renameFile(LOGIN, PASSWORD, 270142691l, "building.jpg"); switch ((int)res) { case -1: System.out.println("File with such name exists"); break; case 0: System.out.println("Rename failed"); default: System.out.println("Rename done"); break; } } catch (ServiceException e) { e.printStackTrace(); } catch (RemoteException e) { e.printStackTrace(); } } } SignupDemo.java
package com.pmstation.api.demo;
import java.rmi.RemoteException; import javax.xml.rpc.ServiceException; import com._4shared.api.servlet.services.DesktopApp.DesktopApp; import com._4shared.api.servlet.services.DesktopApp.DesktopAppServiceLocator; import com.pmstation.api.local.URLGetter; public class SignupDemo { public static void main(String[] args) { DesktopApp da; try { da = new DesktopAppServiceLocator().getDesktopApp(); // !!!!!!! put your own values here !!!!!! String res = da.signup("somenonsense@somewhere.com", "anothernonsense"); if (StringUtils.isEmpty(res)) System.out.println("Username registered"); else System.out.println("Registration failed: " + res); System.out.println(res); } catch (ServiceException e) { e.printStackTrace(); } catch (RemoteException e) { e.printStackTrace(); } } } StringUtils.java
package com.pmstation.api.demo;
/** * Various string manipulations * * * Created: Mon Mar 29 13:11:26 2010 * * @author Ivan Volzhev * @version 1.0 */ public class StringUtils { public static boolean isEmpty(String string) { return string == null || string.isEmpty(); } public static String padRight(String string, int toLen) { return padRight(string, toLen, ' '); } public static String padRight(String string, int toLen, char withChar) { if (string.length() >= toLen) return string; StringBuilder sb = new StringBuilder(string); while (sb.length() < toLen) { sb.append(withChar); } return sb.toString(); } } UploadDemo.java
package com.pmstation.api.demo;
import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InputStream; import java.io.UnsupportedEncodingException; import java.math.BigInteger; import java.net.URL; import java.rmi.RemoteException; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.util.Random; import javax.xml.rpc.ServiceException; import org.apache.http.HttpEntity; import org.apache.http.HttpResponse; import org.apache.http.client.ClientProtocolException; import org.apache.http.client.HttpClient; import org.apache.http.client.methods.HttpPost; import org.apache.http.entity.mime.MultipartEntity; import org.apache.http.entity.mime.content.FileBody; import org.apache.http.entity.mime.content.InputStreamBody; import org.apache.http.entity.mime.content.StringBody; import org.apache.http.impl.client.DefaultHttpClient; import com._4shared.api.servlet.services.DesktopApp.DesktopApp; import com._4shared.api.servlet.services.DesktopApp.DesktopAppServiceLocator; import com.pmstation.api.local.URLGetter; public class UploadDemo extends Demo { private static final int MB = 1024*1024; private static final int KB = 1024; private static final int SIZE = 500 * KB; /** * @param args */ public static void main(String[] args) { boolean success = false; try { DesktopApp da = new DesktopAppServiceLocator().getDesktopApp(); String name = genName(); final long len = SIZE; if(!da.hasRightUpload()) { System.out.println("Upload is currently disabled"); return; } long newId = da.uploadStartFile(LOGIN, PASSWORD, -1, name, len); if(newId < 0) { System.out.println("Unable to reserve file id for file"); return; } int dcId = (int) da.getNewFileDataCenter(LOGIN, PASSWORD); String sessKey = da.createUploadSessionKey(LOGIN, PASSWORD, -1); String upload = da.getUploadFormUrl(dcId, sessKey); //URL uploadUrl = new URL(upload); //uploadUrl.openConnection().getOutputStream() InputStream is = getRandomStream(); HttpClient client = new DefaultHttpClient(); HttpPost post = new HttpPost(upload); MultipartEntity me = new MultipartEntity(); StringBody rfid = new StringBody(""+newId); StringBody rfb = new StringBody(""+0); InputStreamBody isb = new InputStreamBody(is, name) { public long getContentLength() {return len; } }; me.addPart("resumableFileId", rfid); me.addPart("resumableFirstByte", rfb); me.addPart("FilePart", isb); post.setEntity(me); HttpResponse resp = client.execute(post); HttpEntity resEnt = resp.getEntity(); String res = da.uploadFinishFile(LOGIN, PASSWORD, newId, md5Digest); if(StringUtils.isEmpty(res)) { success = true; System.out.println("File uploaded"); } else { System.out.println("Failed: "+res); } //da.uploadCancelFile(LOGIN, PASSWORD, newId); } catch (ServiceException e) { e.printStackTrace(); } catch (RemoteException e) { e.printStackTrace(); } catch (UnsupportedEncodingException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (ClientProtocolException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } finally { if (!success) System.out.println("Upload failed."); } } private static String md5Digest = null; private static void printMd5Digest() { System.out.println("MD5: " + md5Digest); } private static Random r = new Random(); private static char[] alphabet = new char[] {'a','b','c','d','e','f','g','h','i','j', 'k','l','m','n','o','p','q','r','s','t', 'u','v','w','x','y','z' }; private static String genName() { char[] res = new char[8]; for (int i = 0; i < res.length; i++) { res[i] = alphabet[r.nextInt(26)]; } return new String(res)+".tst"; } static byte[] md5 = null; private static InputStream getRandomStream() { //ByteArrayOutputStream baos = new ByteArrayOutputStream(3*MB); byte[] b = new byte[SIZE]; /*for (int i = 0; i < 3*MB/512; i++) { r.nextBytes(b); try { baos.write(b); } catch (IOException e) { } }*/ r.nextBytes(b); MessageDigest md5; try { md5 = MessageDigest.getInstance("MD5"); } catch (NoSuchAlgorithmException e) { return null; } md5.update(b); UploadDemo.md5 = md5.digest(); BigInteger bigInt = new BigInteger(1, UploadDemo.md5); md5Digest = bigInt.toString(16); return new ByteArrayInputStream(b); } } PHPFile upload process:
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml"> <head> <meta content="text/html; charset=utf-8" http-equiv="Content-Type" /> <title>Upload</title> </head> <body> <form action="<?echo $PHP_SELF;?>" method="post" enctype="multipart/form-data"> <input name="myfile" type="file" /><br /> <input name="submit" type="submit" value="Upload to 4shared" /> </form> <? //User credentials on 4shared $user_login = "a5642551@nepwk.com"; $user_password = "123123"; if (isset ($_POST['submit'])) { $fileLocal = $_FILES['myfile']['tmp_name']; // This is the entire file that was uploaded to a temp location. $fileName = $_FILES['myfile']['name']; $fileSize = $_FILES['myfile']['size']; $client = new SoapClient("https://api.4shared.com/jax3/DesktopApp?wsdl", array( "cache_wsdl" => WSDL_CACHE_DISK, "trace" => 1, "exceptions" => 0 ) ); $client->yourFunction(); //Let's look if we have enough free space in user's account $freeSpace = $client->getFreeSpace($user_login, $user_password); //Check upload limit for user $maxSize = $client->getMaxFileSize($user_login, $user_password); if ($fileSize > $maxSize) die("<b>Error: Your file is too big</b>"); else if ($fileSize > $freeSpace) die("<b>Error: Not enough space in account</b>"); //Get Session Key for file upload. -1 means uploading file into root folder $session = $client->createUploadSessionKey($user_login, $user_password, -1); //Get datacenter $datacenter = $client->getNewFileDataCenter($user_login, $user_password, -1); if ($datacenter <= 0) die("<b>Error: Something went wrong</b>"); //Get Upload Url $uploadUrl = $client->getUploadFormUrl($datacenter, $session); //Reserve fileId for our upload $fileId = $client->uploadStartFile($user_login, $user_password, -1, $fileName, $fileSize); //Send file and params via post request $post_params = array( 'resumableFileId' => $fileId, 'resumableFirstByte' => 0, 'FilePart' => '@'.$fileLocal ); $ch = curl_init(); curl_setopt($ch, CURLOPT_URL, $uploadUrl); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); curl_setopt($ch, CURLOPT_POST, true); curl_setopt($ch, CURLOPT_POSTFIELDS, $post_params); $req = curl_exec($ch); curl_close($ch); $finish = $client->uploadFinishFile($user_login, $user_password, $fileId, md5_file($fileLocal)); if($finish == "") {echo "File uploaded";} } ?> </body> </html> iOSEvery application in iOS can use 4s.io service. The client can interact with 4s.io server via SOAP protocol, by means of calling up 4s.io API functions. These functions may be used to request for the content of your account, get the necessary links to files, and edit the file structure and the directory properties on the server. The full description of the api is provided here. The SOAP-protocol may be checked here. To call up 4s.io api-function, it's necessary to send the XML-message to server http://api.4shared.com/jax3/DesktopApp SOAP. The common template for the message is the following:
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsd="http://www.w3.org/2001/XMLSchema"
<br>xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<soapenv:Body> <function xmlns="http://api.soap.shared.pmstation.com/"> <arg0 xmlns="">login</arg0> <arg1 xmlns="">password</arg1> <arg2 xmlns="">arg1</arg2> ... </function> </soapenv:Body> </soapenv:Envelope> - where "function" is the name of the called function. Having indicated the function, one should enlist its arguments. There're only 2 arguments in almost all 4s.io api-functions - these are login and password. In response to the call, 4s.io server will return the results of the function, the SOAP-message in XML-format:
<S:Envelope xmlns:S="http://schemas.xmlsoap.org/soap/envelope/">
<S:Body> <ns2:functionResponse xmlns:ns2="http://api.soap.shared.pmstation.com/"> <return> result </return> </ns2: functionResponse> </S:Body> </S:Envelope> - where "result" is the result of calling up the "function". As an example, we need to get the content of the directory. To do this, we need to call the function "getItems". Its first 2 arguments are, as usual, login and password. The 3rd one is the ID of the requested directory. The body of the relative SOAP-message will be as follows:
<getItems xmlns="http://api.soap.shared.pmstation.com/">
<arg0 xmlns="">username@server.com</arg0> <arg1 xmlns="">*********</arg1> <arg2 xmlns="">123456789</arg2> </getItems> In response, 4s.io server will return the array with the elements of directory:
<item>
<date>(date of file creation)</date> <directory>(true - if file is a directory, otherwise - false)</directory> <downloadCount>(the number of file downloads)</downloadCount> <downloadLink>(link for the file-download from 4shared-server)</downloadLink> <id>(file ID)</id> <md5>(md5-hash)</md5> <name>(name)</name> <parentId>(parent directory ID)</parentId> <size>(size)</size> ... </item> Let's consider the example of the simple application, which allows the user to browse the content of his/her account. The 1st thing, which should be done, when starting the program, is to authorize (login) on 4s.io server by means of calling up the function "login". If authorization is passed successfully, the next step will be the request for the root account directory by means of calling up the function "getRoot". The calling result will be the structure "item", described above. To store received information, I've created subclass File:
@interface File : NSObject
{ NSUInteger _id; NSUInteger _parentId; NSString *_name; NSUInteger _size; NSString *_link; BOOL _dir; NSArray *_content; } ... @end Now that we have the root directory ID, we can use the function "getItems", to get its content, and then the content of any subdirectory. To create the XML-formatted message and the parsing of received result, it's possible to use any XML - parser. In the demo-sample, I've used GDataXML-parser from Google Data APIs Objective-C Client Library. The function to generate the message may be as follows:
+ (NSURLRequest*)soapRequestWithFunction:(NSString*)function andParams:(NSArray*)params
{ GDataXMLElement *xmlFunction = [GDataXMLNode elementWithName:function]; GDataXMLNode *xmlNS = [GDataXMLNode namespaceWithName:nil stringValue:@"http://api.soap.shared.pmstation.com/"]; [xmlFunction addNamespace:xmlNS]; for (NSUInteger i = 0; i < [params count]; i++) { NSString *elementName = [NSString stringWithFormat:@"arg%lu", i]; GDataXMLElement *arg = [GDataXMLNode elementWithName:elementName]; NSString *param = (NSString*)[params objectAtIndex:i]; [arg setStringValue:(NSString*) param]; GDataXMLNode *attrib = [GDataXMLNode attributeWithName:@"xmlns" stringValue:@""]; [arg addAttribute:attrib]; [xmlFunction addChild:arg]; } GDataXMLElement *body = [GDataXMLNode elementWithName:@"soapenv:Body"]; [body addChild:xmlFunction]; GDataXMLElement *soapEnvelope = [GDataXMLNode elementWithName:@"soapenv:Envelope"]; GDataXMLNode *soapNS1 = [GDataXMLNode namespaceWithName:@"soapenv" stringValue:@"http://schemas.xmlsoap.org/soap/envelope/"]; GDataXMLNode *soapNS2 = [GDataXMLNode namespaceWithName:@"xsd" stringValue:@"http://www.w3.org/2001/XMLSchema"]; GDataXMLNode *soapNS3 = [GDataXMLNode namespaceWithName:@"xsi" stringValue:@"http://www.w3.org/2001/XMLSchema-instance"]; [soapEnvelope setNamespaces:[NSArray arrayWithObjects:soapNS1, soapNS2, soapNS3, nil]]; [soapEnvelope addChild:body]; GDataXMLDocument *doc = [[GDataXMLDocument alloc] initWithRootElement:soapEnvelope]; [doc setVersion:@"1.0"]; [doc setCharacterEncoding:@"UTF-8"]; NSData *data = [doc XMLData]; [doc release]; NSString *dataLen = [NSString stringWithFormat:@"%lu", [data length]]; NSURL *url = [NSURL URLWithString:@"http://api.4shared.com/jax3/DesktopApp"]; NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url]; [request addValue:@"text/xml; charset=utf-8" forHTTPHeaderField:@"Content-Type"]; [request addValue:@"" forHTTPHeaderField:@"SOAPAction"]; [request addValue:dataLen forHTTPHeaderField:@"Content-Length"]; [request setHTTPMethod:@"POST"]; [request setHTTPBody: data]; return request; } The content of the directory is shown in the table (class FilesViewController). I show its name and size in the cell, related to the file accordingly:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{ ... cell.accessoryType = file.isDir ? UITableViewCellAccessoryDisclosureIndicator : UITableViewCellAccessoryNone; cell.imageView.image = file.isDir ? [UIImage imageNamed:@"dir.png"] : nil; cell.textLabel.text = file.name; cell.detailTextLabel.text = file.isFile ? [NSString stringWithFormat:@"%.2f Kb", (CGFloat) file.size / 1024.0] : nil; ... } All content is opened via tap in the cell with directory. The page with the following link is opened in the browser via tap on the file:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{ ... if (file.isDir) { FilesViewController *fvc = [[FilesViewController alloc] initWithFile:file]; [self.navigationController pushViewController:fvc animated:YES]; [fvc release]; } else { [[UIApplication sharedApplication] openURL:[NSURL URLWithString:file.link]]; } ... } The demo-sample can be downloaded here. To start the sample application, enter demo-Prefix.pch in the file, enter your login and password from the site 4s.io (macros LOGIN and PASSWORD). Symbian (QT)Every application can interact with 4s.io server via SOAP protocol, by means of calling up 4s.io API functions. These functions may be used to request for the content of your account, get the necessary links to files, and edit the file structure and the directory properties on the server. The full description of the api is provided here. The SOAP-protocol may be checked here. As an SDK for creating applications for mobile devices, let's take ""okia Qt SDK 1.0", which can be got here. This package also includes IDE "Qt Creator", which we will use. In the beginning, let's create the new project with type
"Mobile application Qt" and name it 4s.io. In the visual editor we'll add
"List widget" in the main window, which will include the elements of the root catalogue, downloaded from
4s.io server. In the class MainWindow constructor, in a line To work with SOAP protocol in a more convenient way, we'll take initial codes from the library QtSOAP and insert them into the project, as this library isn't included into mobile version. In addition to that, in order to make the application work with network in Symbian devices, it's necessary to add the following line in the file 4s.io.pro in the part, devoted to the specific Symbian settings:
-
Symbian { ... TARGET.CAPABILITY += NetworkServices ... } - Then, we'll add and initialize members in class MainWindow:
-
class MainWindow : public QMainWindow { ... QNetworkAccessManager nam; QtSoapHttpTransport http; ... }; MainWindow::MainWindow(QWidget *parent) : ... nam(this), http(this), ... { ... connect(&http, SIGNAL(responseReady()), SLOT(getResponse())); http.setNetworkAccessManager(&nam); http.setAction(""); http.setHost("api.4shared.com", 80); ... } - - which realize (implement) the transfer of our future requests. The slot getResponse() will be invoked, when the answers to our requests are received. To download the list of files and subdirectories of the root directory, it's necessary to perform 3 requests in series:
-
class MainWindow : public QMainWindow { ... private slots: void getResponse(); private: QtSoapMessage initStdSoapMessage(const QString &methodName); void authorize(); void authorizeHandler(const QtSoapType &res); void getRoot(); void getRootHandler(const QtSoapType &res); void getItems(long id); void getItemsHandler(const QtSoapType &res); enum Operation {unknown, authorize_op, getRoot_op, getItems_op}; private: ... Operation operation; QString login, password; long rootId; }; void MainWindow::getResponse() { Operation op = operation; operation = unknown; // Get the response, check for error. const QtSoapMessage &resp = http.getResponse(); if(resp.isFault()) { QMessageBox::warning(NULL, "Warning", "SOAP: query failed (" + resp.faultString().value().toString() + ")"); return; } // Extract the return value from this method response, check for errors. const QtSoapType &res = resp.returnValue(); if(!res.isValid()) { QMessageBox::warning(NULL, "Warning", "SOAP: invalid return value"); return; } switch(op) { case authorize_op: authorizeHandler(res); break; case getRoot_op: getRootHandler(res); break; case getItems_op: getItemsHandler(res); break; case unknown: break; } } QtSoapMessage MainWindow::initStdSoapMessage(const QString &methodName) { QtSoapMessage request; request.setMethod(QtSoapQName(methodName, "http://api.soap.shared.pmstation.com/")); request.addMethodArgument("arg0", "", login); request.addMethodArgument("arg1", "", password); return request; } void MainWindow::authorize() { QtSoapMessage request = initStdSoapMessage("isExistsLoginPassword"); operation = authorize_op; http.submitRequest(request, "/jax3/DesktopApp"); } void MainWindow::authorizeHandler(const QtSoapType &res) { bool auth = res.value().toBool(); if(auth) { // QMessageBox::information(NULL, "Authorization", "Authorized"); getRoot(); } else QMessageBox::warning(NULL, "Warning", "Invalid user name or password"); } void MainWindow::getRoot() { QtSoapMessage request = initStdSoapMessage("getRoot"); operation = getRoot_op; http.submitRequest(request, "/jax3/DesktopApp"); } void MainWindow::getRootHandler(const QtSoapType &res) { rootId = res["id"].toInt(); getItems(rootId); } void MainWindow::getItems(long id) { QtSoapMessage request = initStdSoapMessage("getItems"); request.addMethodArgument("arg2", "", id); operation = getItems_op; http.submitRequest(request, "/jax3/DesktopApp"); } void MainWindow::getItemsHandler(const QtSoapType &res) { for(int i=0; i<res.count(); i++) { const QtSoapType &item = res[i]; if(item["removed"].toBool()) continue; QString name; if(item["directory"].toBool()) name = " <dir> "; name += item["name"].toString(); QListWidgetItem *itm = new QListWidgetItem(name); itm->setSizeHint(QSize(0, 50)); ui->listWidget->addItem(itm); } } - Demo-sample can be downloaded here. To start the sample application, insert your login and password from 4s.io in the file MainWindow.cpp (variable login and password). Android
Service 4s.io is a usual web-service, WSDL description of which is here: https://api.4shared.com/jax3/DesktopApp?wsdl. You can read more about web-services and WSDL' here: http://ru.wikipedia.org/wiki/%D0%92%D0%B5%D0%B1-%D1%81%D0%BB%D1%83%D0%B6%D0%B1%D0%B0, http://ru.wikipedia.org/wiki/WSDL. Short description of API functions can found here: API documentation. To work with SOAP API, you can use direct SOAP-calling, but it's more convenient to use another library for request parsing. For instance, you can use free library: http://code.google.com/p/ksoap2-android/. Development of applications for Android is possible on any platform, which allows Andrid SDK setup. System requirements can be checked here: http://developer.android.com/sdk/requirements.html. You can download Android SDK here: http://developer.android.com/sdk/index.html, setup instructions are available here: http://developer.android.com/sdk/installing.html. The recommended environment for development is Eclipse (http://www.eclipse.org/downloads/), with the plugin for Android (http://developer.android.com/sdk/eclipse-adt.html). Any device with OS Android, or an emulator of the device, included into Android SDK, is suitable for starting the developed application. Detailed instruction for beginners in app development can be found here: http://developer.android.com/guide/developing/index.html. Resource http://developer.android.com/index.html comprises all necessary information for developers. The most essential (fundamental) notions are described in the article: http://developer.android.com/guide/topics/fundamentals.html. Step-by-step instruction of the simple app for 4s.io is displayed below. 1) Install a Platform (http://developer.android.com/resources/tutorials/hello-world.html). 2) Create an AVD (http://developer.android.com/resources/tutorials/hello-world.html). 3) Create a New Android Project (http://developer.android.com/resources/tutorials/hello-world.html).
4) To insert changes, needed to make the minimum 4s.io-application work. To link the ksoap2 library to the project, it's necessary to create subcatalog libs in the catalogue Hello4s.io and place jar-archive of this library there. Then, choose Project > Options in menu Eclipse, and after that select subsection Java Build Path and tab Libraries. Press the button Add JARs and display the path to the library.
With the help of menu File > New > Class, let's create 2 subclasses: AccountItem, representing the user's file or catalogue on 4s.io server, and ForSharedService, with the help of which 4s.io API remote methods are called up. AccountItem is used to desereal data from the network and provides convenient methods to receive information about the file/catalogue, for example, getName() and getDownloadLink() return the filename and the download link accordingly.
package com.example.hello4shared;
import org.ksoap2.serialization.SoapPrimitive; /** * 4shared account item, representing a file or a directory description data. * Implementation includes network serialization functionality. */ public class AccountItem { private long id; private String name; private boolean directory; public AccountItem() { } public long getId() { return id; } public String getName() { return name; } public boolean isDirectory() { return directory; } public String toString() { return directory ? "[" + name + "]" : name; } public void setProperty(String propName, Object value) { if (value == null) { return; } if (value instanceof SoapPrimitive) { value = ((SoapPrimitive) value).toString(); } if (propName.equals("id")) { if (value instanceof Long) id = ((Long) value).longValue(); else id = Long.parseLong(value.toString()); } if (propName.equals("directory")) { directory = convertBoolean(value); } else if (propName.equals("name")) { name = convertString(value); } } private boolean convertBoolean(Object val) { if (val == null) return false; if (val.toString().toLowerCase().equals("true")) return true; else return false; } private String convertString(Object str) { if (!(str instanceof String)) return null; if (((String) str).equals("String{}")) return ""; else return (String) str; } } ForSharedService is the interlayer for the convenient call of remote methods and hides the code of interaction with technology SOAP.
package com.example.hello4shared;
import org.ksoap2.SoapEnvelope; import org.ksoap2.serialization.PropertyInfo; import org.ksoap2.serialization.SoapObject; import org.ksoap2.serialization.SoapSerializationEnvelope; import org.ksoap2.transport.AndroidHttpTransport; import org.ksoap2.transport.Transport; import java.util.Hashtable; import java.util.Vector; /** * 4shared API local layer */ public class ForSharedService { private String mUsername; private String mPassword; private static final String SERVICE_URL = "http://api.4shared.com/jax3/DesktopApp"; private static final String SERVICE_NAMESPACE = "http://api.soap.shared.pmstation.com/"; /** * Constructs this object for an account * @param username Username of the account * @param password Password of the account */ public ForSharedService(String username, String password) { mUsername = username; mPassword = password; } /** * Login with current account */ public void login() throws Exception { String methodName = "login"; SoapObject rpc = getRpc(methodName); SoapSerializationEnvelope envelope = getEnvelope(rpc); Transport ht = getHttpTransport(); String result; ht.call(SERVICE_URL + "/" + methodName, envelope); result = (envelope.getResponse()).toString(); if (!isEmptyString(result)) throw new Exception(result); } /** * Get account's root folder * @return Item representing the root directory */ public AccountItem getRoot() throws Exception { String soapAction = SERVICE_URL + "/getRoot"; SoapObject rpc = getRpc("getRoot"); SoapSerializationEnvelope envelope = getEnvelope(rpc); Transport ht = getHttpTransport(); ht.call(soapAction, envelope); return getAccountItem((SoapObject) envelope.getResponse()); } /** * Get contents of a directory * @param dirId Directory's identifier * @return Array of items representing contents of the directory */ public AccountItem[] getItems(long dirId) throws Exception { String methodName = "getItems"; SoapObject rpc = getRpc(methodName); rpc.addProperty("arg2", new Long(dirId)); SoapSerializationEnvelope envelope = getEnvelope(rpc); Transport ht = getHttpTransport(); ht.call(SERVICE_URL + "/" + methodName, envelope); Object result = envelope.getResponse(); return getAccountItems(result); } private Transport getHttpTransport() { Transport ht = new AndroidHttpTransport(SERVICE_URL); ht.setXmlVersionTag("<?xml version=\"1.0\" encoding=\"UTF-8\"?>"); ht.debug = true; return ht; } private SoapObject getRpc(String methodName) { SoapObject rpc = new SoapObject(SERVICE_NAMESPACE, methodName); rpc.addProperty("arg0", mUsername); rpc.addProperty("arg1", mPassword); return rpc; } private SoapSerializationEnvelope getEnvelope(SoapObject rpc) { SoapSerializationEnvelope envelope = new SoapSerializationEnvelope(SoapEnvelope.VER11); envelope.bodyOut = rpc; envelope.encodingStyle = SoapSerializationEnvelope.XSD; return envelope; } private AccountItem getAccountItem(SoapObject item) { return deserialize(new AccountItem(), item); } @SuppressWarnings("unchecked") private AccountItem[] getAccountItems(Object response) { if (response == null) return null; if (!(response instanceof SoapObject)) return null; Vector items = new Vector(); for (int i = 0; i < ((SoapObject) response).getPropertyCount(); i++) { Object item = ((SoapObject) response).getProperty(i); if (item instanceof SoapObject) { items.addElement(deserialize(new AccountItem(), (SoapObject) item)); } } AccountItem[] accountItems = new AccountItem[items.size()]; items.copyInto(accountItems); return accountItems; } @SuppressWarnings( { "unchecked", "deprecation" }) private AccountItem deserialize(AccountItem newItem, SoapObject item) { Hashtable ht = new Hashtable(); PropertyInfo propInfo = new PropertyInfo(); for (int n = 0; n < item.getPropertyCount(); n++) { item.getPropertyInfo(n, ht, propInfo); newItem.setProperty(propInfo.name, item.getProperty(n)); } return newItem; } private static boolean isEmptyString(String string) { return (string == null || string.equals("") || string.toLowerCase().equals("string{}") || string.toLowerCase().equals("anytype{}")); } } The classes SoapObject, SoapSerializationEnvelope, AndroidHttpTransport - are included into the library ksoap2. SoapObject, SoapSerializationEnvelope serve to parse soap-requests, whereas AndroidHttpTransport provides transport to call up the requests. The call up of the function itself is realized by the method call (), of the class AndroidHttpTransport. The service's answer can be received by means of calling up the method getResponse() of the class SoapSerializationEnvelope. The functions, transferred to the service, are defined by calling up the method addProperty of the class SoapObject as a pair "parameter name - value". Let's create a window (activity) to display the list of user's files and catalogues from 4s.io server. To do that, let's create the new class (File > New > Class), derivative from ListActivity, in Eclipse environment, and call it AccountContents. Let's edit AccountContents.java, by redefining the method onCreate, called up by means of initializing the window (activity).
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState); // Use a ListAdapter that will map an array of account's root folder items to TextViews setListAdapter(new ArrayAdapter<AccountItem>(this, android.R.layout.simple_list_item_1, Hello4shared.rootContents)); } The class ArrayAdapter establishes accordance (correspondence) between the list elements of the user's interface and the list of files and catalogues on 4s.io server, which will be uploaded into the static variable rootContents of the class Hello4s.io. In the method onCreate of the main window (activity) of the application, the code of which is in the file Hello4s.io.java, let's create the elements of the user's interface for insertion and transfer of the user's name (login) and 4s.io account password to the server.
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState); // Prepare vertical linear layout LinearLayout mainLayout = new LinearLayout(this); mainLayout.setLayoutParams(new LayoutParams(LayoutParams.FILL_PARENT, LayoutParams.FILL_PARENT)); mainLayout.setOrientation(LinearLayout.VERTICAL); // Create views TextView loginView = new TextView(this); loginView.setText("login:"); TextView passwordView = new TextView(this); passwordView.setText("password:"); final EditText loginEdit = new EditText(this); final EditText passwordEdit = new EditText(this); passwordEdit.setInputType(InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_PASSWORD); Button loginButton = new Button(this); loginButton.setText("Submit"); loginButton.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { String username = loginEdit.getText().toString(); String password = passwordEdit.getText().toString(); // If username and password entered correctly if (username.length() != 0 && password.length() != 0) { // Start login procedure login(username, password); } } }); // Add views to layout mainLayout.addView(loginView); mainLayout.addView(loginEdit); mainLayout.addView(passwordView); mainLayout.addView(passwordEdit); mainLayout.addView(loginButton); // Set the layout as activity main layout setContentView(mainLayout); } The handler of the press-button loginButton checks the properness of inserted data and, if succeeds, calls up the method login (), which starts the process of checking the user's data on 4s.io server, and also requests the list of files and catalogues of the account root catalogue.
private void login(final String username, final String password) {
mProgressDialog = ProgressDialog.show(this, "", "Please wait...", true); mProgressDialog.setCancelable(false); // We should call network calls on separate thread not to freeze the application UI new Thread() { @Override public void run() { // Create 4shared API local layer to call remote methods ForSharedService fss = new ForSharedService(username, password); try { // Call login procedure fss.login(); // Get account's root folder AccountItem rootItem = fss.getRoot(); // Request contents of the root folder rootContents = fss.getItems(rootItem.getId()); // If we are here, this means all the operations succeeded loginSucceeded(); } catch (Exception e) { // One of the operations has failed loginFailed(e.getMessage()); } } }.start(); } If the name (login) and password correspond to the account in 4s.io database, and network connection has been set successfully, the call up of the method loginSucceeded () opens the window (en. activity), which displays the list of user's files and catalogues, which has been stored into the static variable rootContents.
private void loginSucceeded() {
// Application UI transitions should be performed on the main thread mHandler.post(new Runnable() { @Override public void run() { mProgressDialog.dismiss(); // Move to next activity to show list of account's root folder contents Intent intent = new Intent(Hello4shared.this, AccountContents.class); startActivity(intent); } }); } The last thing, which should be changed is the configuration of the project in the file AndroidManifest.xml. In the section <manifest> let's request the permit to get the access of the application to the Internet:
<uses-permission android:name="android.permission.INTERNET"></uses-permission>
In the section <application>, let's add the window(activity) AccountContents: <activity android:name=".AccountContents" android:label="@string/app_name"> </activity> and define the portrait orientation for the window (activity) Hello4shared: android:configChanges="keyboardHidden|orientation" android:screenOrientation="portrait" The full code of the demo-application is attached (http://dc243.4shared.com/download/l8R381Nw/androidsamples.zip).
BlackberrySystem requirements: - 32-bit Windows® XP, Windows Vista® or Windows 7 (64-bit versions require 32-bit Java® and Eclipse) - Eclipse 3.6 Helios - PC with processor Intel® Pentium® 4 or higher (2.5 GHz or higher, 2 GB RAM, 1.5 GB free space on hard drive) - Java SE Development Kit (JDK) 6, update 10 or newer. Setup: - Download and install 32-bit version of JAVA (http://www.java.com/ru/download/). - Download and install 32-bit version of Eclipse (http://www.eclipse.org/downloads/packages/eclipse-classic-362/heliossr2). - Download and install Blackberry plugin for Eclipse (http://us.blackberry.com/developers/javaappdev/javaplugin.jsp). - Install Blackberry SDK. To do this, to start Eclipse and check menu Help - Install New Software... In a line with the software address to insert http://www.blackberry.com/developers/jar/3.6/java/.
Service 4s.io is a usual web-service, WSDL description of which is here: https://api.4shared.com/jax3/DesktopApp?wsdl. You can read more about web-services and WSDL here: http://ru.wikipedia.org/wiki/%D0%92%D0%B5%D0%B1-%D1%81%D0%BB%D1%83%D0%B6%D0%B1%D0%B0, http://ru.wikipedia.org/wiki/WSDL. Short description of API functions can found here: API documentation. To work with SOAP API, you can use direct SOAP-calling, but it's more convenient to use another library for request parsing. For instance, you can use free library: (http://code.google.com/p/ksoap2-android/), which uses Java Microedition, working on Blackberry OS). In the beginning, it's necessary to create the new project in Eclipse (menu File-New-Project, in the list of project types - select Blackberry Project) and call it Hello4Shared. In addition to that, for the right setup on the device of library ksoap2, you should create the 2nd project, where to place the library. You should define the type of the 2nd project Library and add the link to this project in the main application. To work with the service, it's necessary to add subclass ForSharedService. The standard call of the function 4s.io API will be the following:
protected static final String SERVICE_URL = "http://api.4shared.com/jax3/DesktopApp";
protected static final String SERVICE_NAMESPACE = "http://api.soap.shared.pmstation.com/"; /** * Enter check of the already existing user * @param username - user name * @param password - password * @throws Exception */ public void login(String username, String password) throws Exception { String methodName = "login"; SoapObject rpc = new SoapObject(SERVICE_NAMESPACE, methodName); rpc.addProperty("arg0", userName); rpc.addProperty("arg1", password); SoapSerializationEnvelope envelope = new SoapSerializationEnvelope(SoapEnvelope.VER11); envelope.bodyOut = rpc; envelope.encodingStyle = SoapSerializationEnvelope.XSD; HttpTransport ht = new HttpTransport(SERVICE_URL); ht.setXmlVersionTag("<?xml version=\"1.0\" encoding=\"UTF-8\"?>"); ht.debug = true; String result; ht.call(SERVICE_URL + "/" + methodName, envelope); result = (envelope.getResponse()).toString(); if (!isEmptyString(result)) throw new Exception(result); } The classes SoapObject, SoapSerializationEnvelope, HttpTransport - are included in the library ksoap2. SoapObject, SoapSerializationEnvelope serve to parse soap-requests, whereas HttpTransport provides transport for the call of requests. The call of service functions itself is established by the method), of the class HttpTransport. The service answer can be received by means of calling the method getResponse() of the class SoapSerializationEnvelope. The parameters of functions, transferred to the service, are defined by calling up the method addProperty of the class SoapObject as a pair "parameter name - value". Now, let's add the class of the main screen into the app, where we will place 2 text fields to enter the user's name (login) and password, and the button to call up the method login of service, used to enter: public class MainAppScreen extends MainScreen implements FieldChangeListener.
{
/** * Window Title */ private LabelField _title; /** * Enter-Login Field */ private EmailAddressEditField _userNameField; /** * Enter-Password Field */ private PasswordEditField _passwordField; /** * Enter Button */ private ButtonField _loginButton; /** * Constructor */ public MainAppScreen() { super(); _title = new LabelField("Hello4Shared"); setTitle(_title); _userNameField = new EmailAddressEditField("Login or email: ", ""); add(_userNameField); _passwordField = new PasswordEditField("Password: ", ""); add(_passwordField); _loginButton = new ButtonField("Login", Field.FOCUSABLE); _loginButton.setChangeListener(this); add(_loginButton); } } After that, let's add the handler of the press-button in the following way:
/*
* The Handler of the Press-Button */ public void fieldChanged(Field field, int context) { if (field == _loginButton) { login(); } } And the exact request to 4s.io service in the method login, by means of using the class ForSharedService, described above:
private void login(){
ForSharedService service = new ForSharedService(_userNameField.getText(), _passwordField.getText()); // Logging into account try { service.login(); }catch (Exception e) { Dialog.alert(e.getMessage()); } } In case of invalid login or password, the user will receive the error report. The full code of the demo-application is attached (http://dc315.4shared.com/download/bjixhTFj/blackberrysamples.zip). |