Java tutorial
/* * Copyright (C) 2010 Lado Kumsiashvili * * This file is part of websms-connector myphone.ge * * This program is free software; you can redistribute it and/or modify it under * the terms of the GNU General Public License as published by the Free Software * Foundation; either version 3 of the License, or (at your option) any later * version. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more * details. * * You should have received a copy of the GNU General Public License along with * this program; If not, see <http://www.gnu.org/licenses/>. */ package org.herrlado.websms.connector.myphone; import java.io.UnsupportedEncodingException; import java.net.URLEncoder; import java.text.SimpleDateFormat; import java.util.Calendar; import org.apache.http.HttpResponse; import org.apache.http.client.methods.HttpGet; import org.apache.http.client.methods.HttpPost; import org.apache.http.client.utils.URLEncodedUtils; import org.apache.http.entity.StringEntity; import org.apache.http.impl.client.DefaultHttpClient; import org.apache.http.message.BasicHeader; import org.apache.http.protocol.HTTP; import org.json.JSONException; import org.json.JSONObject; import android.content.Context; import android.content.Intent; import android.content.SharedPreferences; import android.preference.PreferenceManager; import android.util.Log; import de.ub0r.android.websms.connector.common.Connector; import de.ub0r.android.websms.connector.common.ConnectorSpec; import de.ub0r.android.websms.connector.common.ConnectorSpec.SubConnectorSpec; import de.ub0r.android.websms.connector.common.Utils; import de.ub0r.android.websms.connector.common.WebSMSException; /** * Receives commands coming as broadcast from WebSMS. * * @author lado */ public class ConnectorMyphone extends Connector { /** Tag for debug output. */ private static final String TAG = "WebSMS.myphone.ge"; /** Login URL, to send Login (POST). */ private static final String LOGIN_URL = "https://myaccount.myphone.ge"; /** Send SMS URL(POST) / Free SMS Count URL(GET). */ private static final String SMS_URL = "https://myaccount.myphone.ge/ajax/sms/send.php"; /** Get Balance - > {"balance":"16.28","livesupporturl":"livesupport_on"} **/ private static final String BALANCE_URL = "https://myaccount.myphone.ge/ajax/account/getbalance.php"; /** Encoding to use. */ //private static final String ENCODING = "ISO-8859-1"; /** HTTP Header User-Agent. */ private static final String FAKE_USER_AGENT = "Mozilla/5.0 (X11; Linux x86_64; rv:13.0) Gecko/20100101 Firefox/13.0.1"; /** This String will be matched if the user is logged in. */ private static final String MATCH_LOGIN_SUCCESS = "control_panel_logout"; /** * Pattern to extract free sms count from sms page. Looks like. */ // private static final Pattern BALANCE_MATCH_PATTERN = Pattern.compile( // "<span id=\"balance\">(.+?)</span>", Pattern.DOTALL); private static final String PAGE_ENCODING = "UTF-8"; // 16.03.2010 07:10:00 private static final ThreadLocal<SimpleDateFormat> format = new ThreadLocal<SimpleDateFormat>() { @Override protected SimpleDateFormat initialValue() { return new SimpleDateFormat("dd.MM.yyyy HH:mm:ss"); }; }; /** * {@inheritDoc} */ @Override public final ConnectorSpec initSpec(final Context context) { final String name = context.getString(R.string.myphone_name); final ConnectorSpec c = new ConnectorSpec(name); c.setAuthor(// . context.getString(R.string.myphone_author)); c.setBalance(null); c.setCapabilities(ConnectorSpec.CAPABILITIES_UPDATE | ConnectorSpec.CAPABILITIES_SEND | ConnectorSpec.CAPABILITIES_PREFS); c.addSubConnector("myphone.ge", c.getName(), SubConnectorSpec.FEATURE_CUSTOMSENDER | SubConnectorSpec.FEATURE_SENDLATER); return c; } /** * {@inheritDoc} */ @Override public final ConnectorSpec updateSpec(final Context context, final ConnectorSpec connectorSpec) { final SharedPreferences p = PreferenceManager.getDefaultSharedPreferences(context); if (p.getBoolean(Preferences.ENABLED, false)) { if (p.getString(Preferences.PASSWORD, "").length() > 0) { connectorSpec.setReady(); } else { connectorSpec.setStatus(ConnectorSpec.STATUS_ENABLED); } } else { connectorSpec.setStatus(ConnectorSpec.STATUS_INACTIVE); } return connectorSpec; } /** * b This post data is needed for log in. * * @param username * username * @param password * password * @return array of params * @throws UnsupportedEncodingException * if the url can not be encoded */ private static String getLoginPost(final String username, final String password) throws UnsupportedEncodingException { final StringBuilder sb = new StringBuilder(); sb.append("user="); sb.append(URLEncoder.encode(username, PAGE_ENCODING)); sb.append("&pass=").append(URLEncoder.encode(password, PAGE_ENCODING)); sb.append("&redirect=").append("/"); sb.append("&login=").append(URLEncoder.encode("/?", PAGE_ENCODING)); return sb.toString(); } /** * These post data is needed for sending a sms. * * @param ctx * {@link ConnectorContext} * @return array of params * @throws Exception * if an error occures. */ private String getSmsPost(final ConnectorContext ctx) throws Exception { final StringBuilder sb = new StringBuilder(); final String[] to = ctx.getCommand().getRecipients(); String delimiter = ","; for (int i = 0; i < to.length; ++i) { if (i == to.length - 1) { delimiter = ""; } String rn = Utils.getRecipientsNumber(to[i]); if (!rn.startsWith("8")) { rn = Utils.national2international(ctx.command.getDefPrefix(), Utils.getRecipientsNumber(rn)); } sb.append(rn).append(delimiter); } // data: { // rcpt: this.target, // sender: sender, // sendernum: $pick(sendernum, ''), // message: text, // schedule: (this.options.schedule || ''), // parentid: (parentid || ''), // parenttype: (parenttype || ''), // eventtype: (eventtype || ''), // send: true // }, final StringBuilder sb1 = new StringBuilder(); sb1.append("rcpt="); // sb1.append(URLEncoder.encode(sb.toString(), PAGE_ENCODING)); String sender = Utils.getSender(ctx.getContext(), ctx.getCommand().getDefSender()); sender = URLEncoder.encode(sender, PAGE_ENCODING); sb1.append("&sender="); sb1.append("&sendernum=").append(sender); sb1.append("&message="); sb1.append(URLEncoder.encode(ctx.getCommand().getText(), PAGE_ENCODING)); final long sendLater = ctx.getCommand().getSendLater(); sb1.append("&schedule="); if (sendLater > 0) { final Calendar cal = Calendar.getInstance(); cal.setTimeInMillis(sendLater); cal.set(Calendar.SECOND, 0); final String late = format.get().format(cal.getTime()); sb1.append(URLEncoder.encode(late, PAGE_ENCODING)); } sb1.append("&parentid="); sb1.append("&parenttype="); sb1.append("&eventtype="); sb1.append("&send=true"); final String post = sb1.toString(); Log.d(TAG, "request: " + post); return post; } /** * Login to arcor. * * @param ctx * {@link ConnectorContext} * @return true if successfullu logged in, false otherwise. * @throws WebSMSException * if any Exception occures. */ private boolean login(final ConnectorContext ctx) throws WebSMSException { try { final SharedPreferences p = ctx.getPreferences(); final HttpPost request = createPOST(LOGIN_URL, getLoginPost(p.getString(Preferences.USERNAME, ""), p.getString(Preferences.PASSWORD, ""))); request.setHeader("Referer", "https://myaccount.myphone.ge/auth/login/?redirect=/&lang="); final HttpResponse response = ctx.getClient().execute(request); final String cutContent = Utils.stream2str(response.getEntity().getContent()); if (cutContent.indexOf(MATCH_LOGIN_SUCCESS) == -1) { throw new WebSMSException(ctx.getContext(), R.string.error_pw); } notifyBalance(ctx, ctx.getClient()); } catch (final Exception e) { throw new WebSMSException(e.getMessage()); } return true; } /** * Create and Prepare a Post Request. Set also an User-Agent * * @param url * http post url * @param urlencodedparams * key=value pairs as url encoded string * @return HttpPost * @throws Exception * if an error occures */ private static HttpPost createPOST(final String url, final String urlencodedparams) throws Exception { final HttpPost post = new HttpPost(url); post.setHeader("User-Agent", FAKE_USER_AGENT); post.setHeader(new BasicHeader(HTTP.CONTENT_TYPE, URLEncodedUtils.CONTENT_TYPE)); post.setEntity(new StringEntity(urlencodedparams)); return post; } /** * {@inheritDoc} */ @Override protected final void doUpdate(final Context context, final Intent intent) throws WebSMSException { final ConnectorContext ctx = ConnectorContext.create(context, intent); this.login(ctx); } /** * Sends an sms via HTTP POST. * * @param ctx * {@link ConnectorContext} * @return successfull? * @throws WebSMSException * on an error */ private boolean sendSms(final ConnectorContext ctx) throws WebSMSException { try { final HttpResponse response = ctx.getClient().execute(createPOST(SMS_URL, this.getSmsPost(ctx))); final boolean sent = this.afterSmsSent(ctx, response); if (sent) { notifyBalance(ctx, ctx.getClient()); } return sent; } catch (final Exception ex) { throw new WebSMSException(ex.getMessage()); } } private void notifyBalance(final ConnectorContext ctx, final DefaultHttpClient client) { try { final HttpResponse respone = client.execute(new HttpGet(BALANCE_URL)); if (respone.getStatusLine().getStatusCode() != 200) { Log.w(TAG, "notifyBalance: " + respone.getStatusLine()); return; } final JSONObject jo = new JSONObject(Utils.stream2str(respone.getEntity().getContent())); final String balance = jo.getString("balance"); this.getSpec(ctx.getContext()).setBalance(balance2sms(balance, ctx)); } catch (final Exception ex) { Log.w(TAG, "notifyBalance: " + ex.getMessage()); this.getSpec(ctx.getContext()).setBalance("?"); } } /** * If the option show_sms_amount == true, balance ist divided through 0.06 * otherwise l is attached (for lari) als suffix * * @param balance * @param ctx * @return */ private static String balance2sms(final String balance, final ConnectorContext ctx) { if (ctx.getPreferences().getBoolean(Preferences.SHOW_SMS_AMOUNT, false) == false) { return balance + "l"; } try { return "" + (int) (Double.valueOf(balance) / 0.06f); } catch (final NumberFormatException nfe) { Log.w(TAG, "Can not parse amount"); return balance + "l"; } } /** * Handles content after sms sending. * * @param ctx * {@link ConnectorContext} * @param response * HTTP Response * @return true if arcor returns success * @throws Exception * if an Error occures */ private boolean afterSmsSent(final ConnectorContext ctx, final HttpResponse response) throws Exception { final String body = Utils.stream2str(response.getEntity().getContent()); Log.d(TAG, "response: " + body); if (body == null || body.length() == 0) { throw new WebSMSException("response.empty");// TODO } JSONObject jo = null; try { jo = new JSONObject(body); } catch (final JSONException e) { throw new WebSMSException("respone.nojson"); } if (jo.has("result")) { jo = jo.getJSONObject("result"); } final int faultCode = jo.getInt("faultCode"); final String faultString = jo.getString("faultString"); if (faultCode != 200) { throw new WebSMSException(faultString); } return true; } /** * Push SMS Free Count to WebSMS. * * @param ctx * {@link ConnectorContext} * @param content * conten to investigate. */ // private void notifyFreeCount(final ConnectorContext ctx, // final String content) { // final Matcher m = BALANCE_MATCH_PATTERN.matcher(content); // String term = null; // if (m.find()) { // term = m.group(1); // term = balance2sms(term, ctx); // } else { // // Log.w(TAG, content); // term = "?"; // } // this.getSpec(ctx.getContext()).setBalance(term); // } /** * {@inheritDoc} */ @Override protected final void doSend(final Context context, final Intent intent) throws WebSMSException { final ConnectorContext ctx = ConnectorContext.create(context, intent); if (this.login(ctx)) { this.sendSms(ctx); } } }