From one mountain predator to another.. My View on transition from Snow Leopard to Mountain Lion

Last night it finally happened. I got Mountain Lion! The new OS X update popped up in the App Store, having waited for the time since it was announced during Apple’s earnings report. With a 4mbps connection, it took around 4 hours to download and install in 30 minutes. I must say this was most seamless OS update I have ever seen on a computer. After a day spending with it, the $20 upgrade seems total worth it!

I have a 13″ Macbook Pro Early 2011 edition bought during August. It came with Mac OS X Snow Leopard and I did forget to get the free upgrade to OS X Lion. Snow Leopard was rock solid, may I dare say it’s the best of both worlds – in aesthetics and in UNIX. Very soon after the purchase MBP is my favorite machine!

I wanted to upgrade to Mountain Lion for a few reasons

  • A geek myself, so will vouch for anything that’s new
  • Apps Compatibility
  • It’s just $20!
  • Snow Leopard and Mountain Lion – both live in the mountains.. 😀

So how’s it?

For starters installation was seamless (wished I had a faster internet connection, people in the US downloaded the same in half an hour). There was a bit of stutter at the app store, came as “this installation file is invalid” or similar. Anyways the download went through and installed in around 30 minutes I believe, I slept off half way through. The next day I got up and was greeted by a metal grey screen login screen. There was the setup of iCloud and automatic updates and the new look. All my settings and files were as it was before. So to reiterate, the most seamless update!

Performance wise, it’s pretty good. But I suspect it takes a little more memory than Snow Leopard did. Good thing I did double the RAM Apple provided.

One thing that’s missing is the expose. Instead it’s the Mission Control, where all apps appear in one place with full screen apps occupying the top row. It’s sort of a soup of applications grouped together in one area and full screen apps occupy their own space. Though the Mission Control is ok, I can’t figure the concept of full screen. I mean I can’t switch to a full screen app from a non-full screen app using Command-Tab.

There’s this Launchpad, apps menu which is inspired by iOS. It looks nice and good way to reach to an app. Every feature has been tweaked, the new multi-finger gestures are a 50:50, re-touched UI, and the minor things. I’ll list out a few tweaks and niggles that caught my eye

  • While in a full-screen app go to launchpad and come back, sometimes the dock remains there, had to go to launchpad and back again to hide it (Oh yeah I put the dock on the left hidden, gives more space to cramped 13″ screen)
  • Finder has now the option of “All My Files” which shows all files in groups – documents, music, videos etc. I don’t find it useful, spotlight search is very powerful enough.
  • I like the twitter integration in the notification bar. Can’t wait for Facebook integration!
  • The 3-finger swipe for going back while browsing a web page is now a 2-finger swipe. It can be issue as it’s used for scrolling too. Three finger swipe to left or right brings full-screen apps in view.
  • Now lot more animations, scrolling animations look more natural and iOS like.

There are lots more changes, Ars Technica comprehensive review has touched on almost all aspects.

Is the upgrade worth it? Totally, if you ask me. It may not be a radical change, but some of the small new features are good (haven’t used reminders, notes, iCloud etc) and the existing features haven’t much. I as developer am concerned on few things like terminal, operability etc which thankfully is not being hampered. Plus with this new update all the new apps and updates are compatible, I can tweet right off my notification bar. Some things I miss, the Expose Spaces, might be the only reason you as a Snow Leopard user might not want to upgrade. Other than that, there’s no reason why you should not be downloading it already!

PS: Few links to help you tame this new beast

http://lifehacker.com/5928799/everything-you-need-to-know-about-os-x-mountain-lion

http://www.engadget.com/2012/07/25/apple-os-x-mountain-lion-10-8-review/

Advertisements

Android and Google App Engine – Build a simple message board app

Right. I have been building apps for sometime now and I realize I haven’t posted anything related in my blog! So this is my first tutorial based on Android and GAE, and really hope to put some more posts on other super cool technologies I have been learning. So this can be part 1 to a series of posts.

Today I’m gonna show how you can build a simple Android app that can connect to the server hosted on Google’s servers through Google App Engine(GAE). I will focus more on Android here and the connectivity will be done via localhost. It’s pretty easy to build sites on app engine and they have exhaustive tutorials for that.

Anyways the application we are going to build today is a Message board, that is anyone with some username can post a message to the public. Imagine that to be a simplified version of the Twitter. I’m not adding any authentication here (Might come up in future posts) and in trying to keep it simple I have not included exception handling and checks, which btw are very very important. Possibly I will update this post with these additions. Plus I deeply appreciate anyone who can suggest on how to improve this tutorial.

What you would be needing today on your machine is Eclipse with Android ADT plugin and Android SDK in the machine, the Google App Engine Launcher and well a text editor to edit the python and other files in the app engine application. So let’s begin!

Step 1 – Build the app engine application:

Today we are going to build a simple REST based application. If you are using Google App Engine Launcher, then you can create a new application from it’s options and automatically builds you the app.yaml . 

Edit the main.py. Create two models – Message and User.

class User(db.Model):
	# username
	username = db.StringProperty(required=True)
	
	def toDict(self):
		user = {}
		user["username"] = self.username
		return user

class Message(db.Model):
	# Message Content
	content = db.TextProperty(required=True)
	
	# Date Created
	created = db.DateTimeProperty(auto_now_add=True)
	
	# Owner
	owner = db.ReferenceProperty(User,collection_name='messages')
	
	def toDict(self):
		message = {}
		message["content"] = self.content
		message["date_created"] = convert_date(self.created)
		message["owner"] = self.owner.username
		return message

Now create REST handlers for these models.

class UserHandler(webapp2.RequestHandler):
	def post(self, id):
		#logging.info(self.request.get('username'))
		data = self.request.get('username')#json.loads(self.request.body)
		user = User.all().filter('username',data).get()
		if user == None:
			user = User(username=data)
			user.put()
		return self.response.out.write(json.dumps(user.toDict()))
		

class MessageHandler(webapp2.RequestHandler):
	def post(self, id):
		try:
			data = json.loads(self.request.body)
		except ValueError:
			data = {}
			request = self.request
			data['username'] = request.get('username')
			data['content'] = request.get('content')
		except:
			return self.error('403')
		user = User.all().filter('username',data['username']).get()
		logging.info(data['username'])
		if user:
			message = Message(content=data['content'],owner=user)
			message.put()
			return self.response.out.write(json.dumps(message.toDict()))
		return self.error('403')
	
	def get(self,id):
		if id != '':
			message = Message.get_by_id(int(id))
			if message:
				return self.response.out.write(json.dumps(message.toDict()))
			return self.error(403)
		try:
			data = json.loads(self.request.body)
			messages = User.all().filter('username',data['username']).get().messages
		except:
			messages = Message.all()
		messages_to_send = []
		for message in messages:
			messages_to_send.append(message.toDict())
		return self.response.out.write(json.dumps(messages_to_send))
	
	def put(self,id):
		if id != '':
			data = json.loads(self.request.body)
			message = Message.get_by_id(int(id))
			if message:
				message.content = data['content']
				message.put()
				return self.response.out.write(json.dumps(message.toDict()))
		return self.error(403)
	
	def delete(self,id):
		if id != '':
			message = Message.get_by_id(int(id))
			message.delete()

What’s left here is the main handler and if you had like, a test handler with a test html that has forms to test the app handlers.

Once done, check the app out using the preview option and see if everything works fine.

We have a working application, now we move on to android.

Step 2 – Build Android app:

We will have an Android app that will have a ListView to display all the messages and a button which will lead the user to create a message.

First the Main Activity will have a ListView that is populated by the messages from the app engine app you have just created. It also has a button that sends an intent to another activity that creates a new message. I called this app Post a Message that you can find on my github.

public class PaM_Main extends ListActivity {
	Handler handler;
	
	
	private OnClickListener postMessageListener = new OnClickListener() {
		
		public void onClick(View v) {
			Intent postMessageIntent = new Intent(PaM_Main.this, PaM_New_Post.class);
			startActivity(postMessageIntent);
		}
	};
	
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.pam_main);
        
        handler = new Handler();
        Button postButton = (Button) findViewById(R.id.post_button);
        postButton.setOnClickListener(postMessageListener);
    }
    
    @Override
    protected void onResume() {
    	super.onResume();
    	new GetMessages().execute("");
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        getMenuInflater().inflate(R.menu.pam_main, menu);
        return true;
    }

    class GetMessages extends AsyncTask<String, Void, String> {

		@Override
		protected String doInBackground(String... params) {
			String uri = "http://10.0.2.2:8090/messages";
			HttpClient httpClient = new DefaultHttpClient();
			HttpGet httpGet = new HttpGet(uri);
			//Log.d("loltale:params", Integer.toString(params.length));
			
			try {
				HttpResponse response = httpClient.execute(httpGet);
				HttpEntity entity = response.getEntity();
				
				if(entity != null) {
					return EntityUtils.toString(entity);
				}
			} catch (UnsupportedEncodingException e) {
				e.printStackTrace();
			} catch (ClientProtocolException e) {
				e.printStackTrace();
			} catch (IOException e) {
				e.printStackTrace();
			}
			return null;
		}
		
		@Override
		protected void onPostExecute(String result) {
			super.onPostExecute(result);
			Log.d("PaM:result:", result);
			try {
				JSONArray messages = new JSONArray(result);
				ArrayList<PaM_Message> list = new ArrayList<PaM_Message>();
				if (messages != null) { 
				   int len = messages.length();
				   for (int i=0;i<len;i++) { 
				    list.add(new PaM_Message(messages.get(i).toString()));
				   } 
				}
				
				final ArrayList<PaM_Message> mList = list;
				handler.post(new Runnable() {
					
					public void run() {
						setListAdapter(new MessagesAdapter(getApplicationContext(), mList));
					}
				});
			} catch (JSONException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
    	
    }
    
    class MessagesAdapter extends ArrayAdapter<PaM_Message> {
    	private List<PaM_Message> messages;
    	
    	public MessagesAdapter(Context context, List<PaM_Message> messages) {
    		super(context, android.R.layout.simple_list_item_2, messages);
    		this.messages = messages;
    	}
    	
    	@Override
    	public View getView(int position, View convertView, ViewGroup parent) {
    		TwoLineListItem view;
    		
    		if(convertView == null) {
    			LayoutInflater inflater = (LayoutInflater)getApplicationContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
                view = (TwoLineListItem)inflater.inflate(android.R.layout.simple_list_item_2, null);
    		}
    		else {
    			view = (TwoLineListItem)convertView;
    		}
    		PaM_Message data = messages.get(position);
            view.getText1().setText(data.getMessage());
            view.getText2().setText(data.getUser());
    		return view;
    	}
    }
} 

The New Message Activity will have two edit boxes and a button which will create a new message. Also note that I am creating a user of the username first and then post the message.

public class PaM_New_Post extends Activity {
	EditText contentEdit, usernameEdit;
	
	private OnClickListener postMessageListener = new OnClickListener() {
		
		public void onClick(View v) {
			String content = contentEdit.getText().toString();
			String username = usernameEdit.getText().toString();
			Log.d("PaM:content:username:",content + ":" + username);
			if(!content.equals("") && !username.equals("")) {
				String user_result = new PaM_API().postUser(username);
				try {
					JSONObject message_result = new JSONObject( new PaM_API().postMessage(content, username) );
					if ( message_result.getString("content").equals(content) ) {
						Toast.makeText(getApplicationContext(), "Message Successfully posted!", Toast.LENGTH_LONG).show();
						contentEdit.setText("");
						usernameEdit.setText("");
					}
					else {
						Toast.makeText(getApplicationContext(), "Something wrong... 😦 ", Toast.LENGTH_LONG).show();
					}
				} catch (JSONException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
				
			}
			else {
				Toast.makeText(getApplicationContext(), "Some fields missing", Toast.LENGTH_LONG).show();
			}
		}
	};
	
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.new_post);
        
        contentEdit = (EditText)findViewById(R.id.messageContentEdit);
        usernameEdit = (EditText)findViewById(R.id.usernameEdit);
        ((Button) findViewById(R.id.postNewMessageButton)).setOnClickListener(postMessageListener);
    }
}

Few things to note, if you are testing on your system, you need to put http://10.0.2.2:/ to the uri the android app will connect to the GAE app. And do remember to change the url when the app is deployed to google app engine.

That’s it! You can run the android app and check it out. Plus if you deploy you GAE app, then anyone can access your app and post on the message board! The code’s on github

Hope this post was helpful. In the next series of posts, I will try to show a little more complex apps plus use some awesome frameworks like Backbone.js, Knockout.js, Node.js and many more to make some really cool apps!

Note: There will be a lot of revisions to this. This is my first attempt in writing a tutorial, so will be glad to know I can make this post better. Suggestions welcome!