Tuesday, December 20, 2011

Android: OAUTH

Pretty cool article:
http://nilvec.com/implementing-client-side-oauth-on-android/

Monday, December 19, 2011

S Pen SDK 1.5

This sounds interesting, I will have to check it out later.


We are happy to announce the release of the S Pen SDK 1.5, providing a higher level of flexibility, precision and control to your GALAXY Note applications.

New features included in version 1.5 of the SDK are:

● Zoom
New and improved Zoom in the S Pen SDK 1.5, facilitates increased precision in your app. With 50x magnification, a whole new level of detail is available, providing unparalleled control over your work.

● Panning
Also added to this update is Panning, allowing a user to change the X, Y coordinate values of the "CanvasView"

● Add Background
S Pen SDK 1.5 now offers fully customisable letter/note background images based on "CanvasView". Customise the background of your letter/notes to express your unique style.

● PenSettinginfo Class
Directly select the Pen and Eraser to give your users a higher level of flexibility and control.





http://innovator.samsungmobile.com/down/cnts/toolSDK.detail.view.do?platformId=1&cntsId=10210

Monday, December 5, 2011

Eclipse: installing SVN Subclipse plugin


1) Install Eclipse 
2) Help > Install New Software


3) Add new URL http://subclipse.tigris.org/update_1.6.x



4) Name the plug-in  "SVN Subclipse 1.6 plugin"
5) Proceed with default installation steps

Installing Eclipse (Indigo) for Android Development

Go to Website and download "Eclipse IDE for Java EE Developers", most new Macs with Intel i7 CPU are 64 bit.

http://www.eclipse.org/downloads/


Saturday, December 3, 2011

Mac OS Lion - colorful icons sidebar with SIMBL plugin

Are you missing COLOR ICONS in your new Mac OS "Lion"?






Here is an easy tutorial:


1. Download ZIP file http://bit.ly/ColorfulSidebar and expand it.

2. Install SIMBL from the expanded folder.

3.  Move the ColorfulSidebar.bundle into the following SIMBL plugin folder:
$ cd /Library/Application\ Support/SIMBL/Plugins 

uki:Plugins uki$ ls

ColorfulSidebar.bundle

4. Restart the Finder through the Terminal: 
$ killall Finder









This is an edited post from original article by Natalia on CyberWalkAbout.com

Tuesday, November 29, 2011

Installing Android SDK



1. Open website:
http://developer.android.com/sdk/index.html

2. Download:
Mac OS X (intel) android-sdk_r15-macosx.zip

3. Install in directory that is easy to find later:
/Applications/Android/android-sdk-macosx

4. Configure Eclipse to see that directory. If you are using Eclipse we assume you you have a plugin installed.

Eclipse > Preferences > Android



Saturday, November 26, 2011

Eclipse: installation of plugin for Android Development Tools (ADT)

Updated on September 15, 2014

When you install the newest Eclipse (at the time of this writing Luna 4.4), it does not have any Android-specific tools.






Android Development Tools (ADT) Eclipse plugin

Step 1) Provide Google Android repository

  • install: Help > Install New Software...
  • URL: https://dl-ssl.google.com/android/eclipse/
  • name: Android Development Tools (ADT) plugin


Step 2) Select all packages to install 

and click NEXT..


Step 3) Accept ALL license agreements

Step 4) Make sure that Eclipse is pointing to Android SDK you want to use






Install SVN plugin Subclipse for Eclipse Indigo

I am re-installing Eclipse often enough that I decided to post this one more time:

Removing Subclipse from Eclipse

I got the following error while installing subclipse 1.6 after trying 1.8 (which failed to sync my projects):


Your original request has been modified.
  "CollabNet Merge Client" will be ignored because a newer version is already installed.
  "Subclipse (Required)" will be ignored because a newer version is already installed.
  "Subclipse Integration for Mylyn 3.x (Optional)" will be ignored because it is already installed.
  "Subversion Client Adapter (Required)" will be ignored because a newer version is already installed.
  "Subversion JavaHL Native Library Adapter (Required)" will be ignored because a newer version is already installed.
  "Subversion Revision Graph" will be ignored because a newer version is already installed.
Cannot complete the install because of a conflicting dependency.
  Software being installed: SVNKit Client Adapter (Not required) 1.6.15 (org.tigris.subversion.clientadapter.svnkit.feature.feature.group 1.6.15)
  Software currently installed: Subclipse (Required) 1.8.3 (org.tigris.subversion.subclipse.feature.group 1.8.3)
  Only one of the following can be installed at once:
    Subversion Client Adapter 1.8.0 (org.tigris.subversion.clientadapter 1.8.0)
    Subversion Client Adapter 1.6.12 (org.tigris.subversion.clientadapter 1.6.12)
  Cannot satisfy dependency:
    From: SVNKit Client Adapter (Not required) 1.6.15 (org.tigris.subversion.clientadapter.svnkit.feature.feature.group 1.6.15)
    To: org.tigris.subversion.clientadapter [1.6.12,1.7.0)
  Cannot satisfy dependency:
    From: Subclipse (Required) 1.8.3 (org.tigris.subversion.subclipse.feature.group 1.8.3)
    To: org.tigris.subversion.clientadapter [1.8.0,1.9.0)





Here are the steps I took to uninstall subclipse:

Eclipse > Help > Install New Software > Available Software Sites ...

select subclipse and click "Remove"



Open Terminal; list and delete all subvesion jar files

$ cd /Applications/ide/eclipse/plugins [specific to your system]

$ ls | grep org.tigris.subversion | xargs rm

Even that did not allow full uninstallation and i decided to re-install Eclipse.


Wednesday, November 16, 2011

Mobile Apps: Customer Feedback



There is several ways to get feedback from your customers.

The basic principle I have learned in Marines goes like this: if one of the Marines does not know how to do something, there is a good chance that some other platoon member does not know it either, hence the Drill Instructor screams at all them. 

Bouncing your app against the real audience is expensive and time consuming, therefore observations should be keen and not lightly disregarded as “user is stupid, they did that wrong", developers like to say that way too often. Keep in mind the principle outlined in book “Don’t Make Me Think” .

Here are few of the customer feedback methods we use:

  • Give the app to a typical user (a child for kid’s game, etc.) who has never interacted with the app and observe the interaction without any explanation of how things are supposed to work. Take good notes and fix the issues immediately, once the user figur out the difficult part the chance is lost. 
  • Provide eMail feedback inside the application. Many people would be surprised how few apps have  support email that is easily accessed from mobile applications. Also, many app developers who have not used that method might be surprised how many emails they will get and you do want to be in touch with your users. Many of them will try to help with suggestions, some with thank you, and most will vent --  users that cannot find an easy way to vent to you personally, will do that in market review and you will loose your rating. 
  • Read the reviews of your app. Try to fix problems and after releasing the fix make sure that several people write positive counter-review thanking you for fixing the problem
  • Have Facebook page for your app that is easy to access from mobile.
  • Have Twitter account for your app that is easy to access from mobile.
  • Have Google+ page for your app that is easy to access from mobile.
It is OK if people criticize you on your Facebook, Google+, or Twitter -- you can manage it, however negative reviews and ratings cause irreparable damage.

To read more download "Roadmap for Mobile Business" at http://bit.ly/CyberBook

Tuesday, November 15, 2011

Mac: Forcing Delete Trash


I switched computer few time and copied a lot of files between. As result I got Trash full of files that I could not delete. The solution was to create a "Temp Trash" folder on my desktop, copy files from Trash to this new folder and Delete them again.





UN-Secure Delete of Trash

Sometimes you want to delete a lot of files files from Trash folder, but it takes forever using "Secure Empty Trash".

In this case is it easier and faster to click OPTION KEY button and "Empty Trash", the delete is not secure which means someone could do forensics on your Hard Drive and restore some of the files if they were in the sections not overwritten by newer data, but for most people who are not quite paranoid the speedy delete maybe a good option.




Changing Permission of the files to "Read and Write"


uki: ~ $ sudo chmod 666 /Users/uki/.Trash/*.*

Saturday, November 12, 2011

Mac OS X 10.7.2 "Lion" - where is my Java?! #%!

Apple decided that Java is not is not important enough to be installed on your new Mac , not nice!

I have heard the rummors that Steve did not love us, the Android crowd. I am sure it is not true, after all I am using Droid2 with keyboard and Galaxy Tab 7, both of which are not products that are offered by Apple. Denial? OK, I am reading his biography, he did hate us.

When you try to open apps like Eclipse you get an "almost" friendly pop-up:


Unfortunately for me, the installation of the Java did not happen, I tried several times.

I tried "java" from Terminal as that may fix the problem, but it did not.


What happened was I had a Software Update process already running and downloading new iTunes and whole bunch of other stuff over a very slow Public Library connection. 






Friday, November 11, 2011

Uninstalling Android app from command line

If you are switching between PROD and DEV versions of your app you will get this error:


[2011-11-26 14:27:22 - Local Guide Chicago] Re-installation failed due to different application signatures.
[2011-11-26 14:27:22 - Local Guide Chicago] You must perform a full uninstall of the application. WARNING: This will remove the application data!
[2011-11-26 14:27:22 - Local Guide Chicago] Please execute 'adb uninstall com.chicagoandroid.chicagolocal' in a shell.
[2011-11-26 14:27:22 - Local Guide Chicago] Launch canceled!



Make sure you have ONLY ONE device connected to USB, or the command will fail.
It would be also a good idea to put adb on your classpath, but it is not essential.

Open Terminal and perform these two commands (adjust to your situation):


$ cd /Applications/Android/android-sdk-macosx/platform-tools
$ ./adb uninstall com.chicagoandroid.chicagolocal
Success

one line version would be:

$ /Applications/Android/android-sdk-macosx/platform-tools/adb uninstall com.chicagoandroid.chicagolocal
Success



Since I do that often I made this automatic:

1. Open TextEdit
2. past the command:
/Applications/Android/android-sdk-macosx/platform-tools/adb uninstall com.chicagoandroid.chicagolocal
3. save with .bash extension
4. change permission to execute:
$ chmod 755 /Users/uki/Desktop/Uninstall\ Chicago\ Local\ Guide.bash
5. Make sure that Terminal knows to open .bash files

Sunday, October 23, 2011

Android: creating 9-patch image


This tutorial shows how to create a stretchable 9patch image on Mac OS. 



Step 1) 
Prepare some cool image to practice on.
Find on Internet (bad designer), or draw a button background image you really like:


Step 2)
Download Android SDK
http://developer.android.com/sdk/index.html



Step 3)
Install the Android SDK in your preferred location,  for me it is in:

example: $ /Applications/Android/adt-bundle-mac-x86_64-20130522/sdk/




Step 4) 
Add Android SDK tools to your system PATH

4.1 Open Terminal by: 
Open Launchpad and type in search "ter" and select Terminal




4.1 Edit your .profile
In Terminal type $ edit ~/.profile 
This should open default editor in my case TextWrangler with .profile 

4.2 Add following lines on the bottom adjusted for your installation directory

export ANDROID_HOME=/Applications/Android/adt-bundle-mac-x86_64-20130522/sdk/
export PATH=${PATH}:${ANDROID_HOME}/tools:${ANDROID_HOME}/platform-tools




4.3 Save file 
4.4. To refresh system, open NEW Terminal window by pressing on Terminal and click "command n"
see if you can see correct tool
$ which draw9patch

/Applications/Android/adt-bundle-mac-x86_64-20130522/sdk//tools/draw9patch

SUCCESS!

Step 5)
Open 
draw9patch tool by typing
$ draw9patch




Step 3) drag the image to the draw9patch tool and try to draw pixels on the edges of the image
  • left and top edges are for stretching areas
  • right and bottom are for (text) content area



actual 9patch file when saved, notice the file has name.9.png name and keep the 9 part:


final result




example with VERTICALLY non-stretchable image:



    <Button
        android:id="@+id/button1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignLeft="@+id/textView1"
        android:layout_below="@+id/textView1"
        android:layout_marginLeft="20dp"
        android:layout_marginTop="44dp"
        android:background="@drawable/back_button_normal"
        android:gravity="left|top"
        android:text="Good, this is what we wanted! :)"
        android:textColor="#CCCCCC" />






When you are working with images that have non-stretchable elements in them, you might define 2 stretching points. Also, it is a good practice to define a STYLE GUIDE if you need to create a set of 9patch images as in the case below:


  • left 16 (upper stretch)
  • left 26 (lower stretch)
  • right 9 (upper edge of text)
  • right 33 (lower edge of text)
  • bottom 8 (left margin)
  • bottom 16 (right margin, before arrow)
  • top 15 (any pixel inside text area)


Example with 2 stretchable areas


Enlarge and study the image.

You can see in the image above that I count 16 pixels on the left so the stretch is always in the same point of the bevel. I try to avoid stretching such images with bevel and create them "to size" vertically.

I leave 8 pixels as the margin on the left, and 4 pixels from the drop-down arrow which cannot stretch.
I leave 9 pixels from the top and bottom because in other images of this set I have rounded corners.

You can visualize the final image after stretching on the right-hand side (image in the middle).

Make sure to +1 our Google Developers Group page:


Shameless plug:
Visit my company website CyberWalkAbout.com for example of great work we have done and inquire about very competitive (low) rates of design and development we offer at info@CyberWalkAbout.com


Android Market Enigma

I have deployed "SF Bay Area Local Guide" over 24 hours ago and I got few downloads, yet the app is not showing on either Web Android Market, nor mobile version for any device I own.

Any suggestions?



Saturday, October 22, 2011

Android ADT 14 Declares War on My Code!

The error comes up only when compiling the project, it started happening when I updated tools in Eclipse to ADT 14, but others had it in prior versions.

Unable to execute dex: Multiple dex files define

Conversion to Dalvik format failed with error 1
UNEXPECTED TOP-LEVEL EXCEPTION:
java.lang.IllegalArgumentException: already added: Lcom/cyberwalkabout/common/DistanceUtils;




We have been using multiple project libraries for months, but now including them causes a problem as some of them are referenced more than once. 



SOLUTION is listed here: http://ukitech.blogspot.com/2012/06/javalangillegalargumentexception.html




Other problems encountered:

Updates (Android ADT 14) in Eclipse Indigo that introduced these problems:










switch() to if-then



Among many, many things that went wrong today this one was was annoying because of many places I had to change, but at least quick to fix.

Android ADT 14 decided that it does not like to use R.id.reference as a parameter to switch statement and it had to me changed to if-then.



So all of the switch statements had to be converted to if-then. Thankfully there is a shortcut for that (command-1).





Friday, September 23, 2011

Bash Shell: Working with large text files

When working with multi-Gb text files I use these commands:

1. Get the first line which often contains column names and dump it into a small text file



uki $ head -n 1 source_file_name.txt > header_line.txt



2. Get first record after the headline and dump it into a small text file

uki $ head -n 2 source_file_name.txt | tail -1 > first_data_line.txt 

3. Finally, when developing using large files, I take SAMPLE 1000 records (out of millions) to speed up the dev time, I use 1000 because that is default SELECT * number of records in MySQL, but you can use any other if you want, but I would not go too small as you many not catch memory leak errors. The random number 2500 in this example I would change occasionally to pull different sample. You do want to sample your data in different places.


uki $ head -n 2500 source_file_name.txt | tail -1000 > sample_1000_records.txt 

Resulting files:

Thursday, September 22, 2011

Eclipse: increase size of Console buffer size

Then you are executing applications that have a lot System.out.print.. output, you might want to increate size of your Console to hold more text.

Eclipse > Preferences > type in Console in search > Run/Debug > Console

Increase buffer size from default 80,000 characters to MAX 999999.


Friday, September 16, 2011

Java: importing data from a big text file to MySQL

These 2 classes show you how to read a HUGE TEXT file and insert data from each line to the MySQL database efficiently. The parsing of the lines is out of scope of this exercise as it will be different for each application.


package com.your_package.data;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Date;

public class YourClassName
{

public static void main(String[] args)
{
String db = "your_db_name";
String user = "root";
String password = "your_password";
Connection connection = makeDbConnection(db, user, password);

BigTextFile file = null;
try
{
file = new BigTextFile("/Users/uki/Documents/file_name.txt");
} catch (Exception e)
{
e.printStackTrace();
}

for (String line : file)
{
// process line here the way you want it
System.out.println(line.substring(line.lastIndexOf("|") + 1));

String partNumber = "0001";
String attributeName = "some name";
String attributeValue = "some value";

String sql = buildInsertSqlStatement(attributeName, attributeValue, partNumber);
insertIntoDb(connection, sql);

break; // remove after all works
}

}

private static String buildInsertSqlStatement(String attributeName, String attributeValue, String partNumber)
{
StringBuffer sql = new StringBuffer();
sql.append("INSERT INTO your_database_name.table_name VALUES (");
sql.append("'" + attributeName + "'");
sql.append(", '" + attributeValue + "'");
sql.append(", '" + partNumber + "' );");
return sql.toString();
}

private static void insertIntoDb(Connection connection, String sql)
{
final String TAG = YourClassName.class.getCanonicalName();
try
{
Statement st = connection.createStatement();

System.out.println(TAG + "Executing: " + sql);
int val = st.executeUpdate(sql.toString());
System.out.println(TAG + " Returned: " + val);

} catch (SQLException e)
{
System.out.println("SQL insert failed " + e);
}

System.out.println(TAG + "Finished " + new Date());
}

private static Connection makeDbConnection(String db, String user, String password)
{
Connection con = null;
try
{
String url = "jdbc:mysql://localhost:3306/";
String driver = "com.mysql.jdbc.Driver";
Class.forName(driver);
con = DriverManager.getConnection(url + db, "root", "");
} catch (Exception e)
{
e.printStackTrace();
}
return con;
}
}





package com.your_package.data;

import java.io.BufferedReader;
import java.io.FileReader;
import java.util.Iterator;

public class BigTextFile implements Iterable
{
private class FileIterator implements Iterator
{
private String line;

public boolean hasNext()
{
try
{
line = bufferedReader.readLine();
} catch (Exception ex)
{
line = null;
ex.printStackTrace();
}

return line != null;
}

public String next()
{
return line;
}

public void remove()
{
}
}

private BufferedReader bufferedReader;

public BigTextFile(String filePath) throws Exception
{
bufferedReader = new BufferedReader(new FileReader(filePath));
}

public void Close()
{
try
{
bufferedReader.close();
} catch (Exception ex)
{
}
}

public Iterator iterator()
{
return new FileIterator();
}
}

MySQL: importing table data from text file


You can import table data from text file.

If columns are matched in order to delimited file, then it is very easy, example data:

column1data|column2data|column3data| |column5data
column1data|column2data|column3data|column4data|column5data


Note that the file name should be the exact name of the table you are writing to:


uki: ~ $ mysqlimport --local -u root --fields-terminated-by="|" sears_product_info /Users/uki/Documents/XYZ/table_name.txt 

database_name. table_name: Records: 526733  Deleted: 0  Skipped: 526733  Warnings: 557695


Note that if the record already exists then it will not be imported again (a good feature for me).
If you want to clean the table content then you execute 
DELETE FROM database_name. table_name
The import of 500,000 records may take up to 10 minutes on MacBook Pro.

Android: obtaining Google Map apiKey

TO GET MD5 for DEVELOPERS (DEBUG):


STEP 1. Find you debug.keystore file 




STEP 2a. Open Terminal and execute keytool command

$ keytool -list -storepass android -keystore /Users/uki/.android/debug.keystore


androiddebugkey, Jan 21, 2010, PrivateKeyEntry, 
Certificate fingerprint (MD5): A0:AC:1A:E2:E7:06:C2:93:CF:9E:xxxxxxx.....

skip to STEP 3


STEP 2b.


FOR DEPLOYMENT (not local development):
Build (Export) your application for deployment at least once to you have your deployment signature. Follow the wizard and make certificate for 35 years.


Use the same keytool utility:

uki: ~ $  keytool -list -keystore /some_directory/keystore_name.keystore
Enter keystore password:

Keystore type: JKS
Keystore provider: SUN

Your keystore contains 13 entries

[...]
taxi share - national, Sep 21, 2011, PrivateKeyEntry,
Certificate fingerprint (MD5): 15:A1:16:70:.....................................................
[...]
uki: ~ $







4. Copy and paste the apiKey to your Maps



Thursday, September 15, 2011

Select longest records from the database

To find the longest record in your table you can use this query:


SELECT max(length(column_name)) from table_name 
This can help you in optimizing the size of your tables.


SELECT column_name FROM table_nameWHERE length(column_name) =
( SELECT max(length(
( SELECT max(length(column_name)) from table_name );


Result is a list of records with the longest length



Execute SQL script (MySQL dump) from command line

1) Find you mysql installation directory:

uki: ~ $ cd /usr/local/mysql/bin

2) Start mysql shell
uki: ~ $ ./mysql -u root
Welcome to the MySQL monitor.  Commands end with ; or \g.
Your MySQL connection id is 238
Server version: 5.5.9 MySQL Community Server (GPL)
...

3) Select database you want to execute script agaist
mysql> use my_database_name
Reading table information for completion of table and column names
You can turn off this feature to get a quicker startup with -A

Database changed

4) Execute SQL script
in the next line notice "slash dot space"

mysql> \. /Users/uki/Documents/script_name.sql 

Add MySQL to PATH

1.  Open Terminal go to your home directory
uki: ~ $ cd ~

2. see if you already created .bash_profile


uki: ~ $ ls .bash*
.bash_history .bash_profile

3. if .bash_profile is not there execute
uki: ~ $ touch .bash_profile

4. open file for editing
uki: ~ $ open .bash_profile 

or using your favorite editor
uki: ~ $ bbedit .bash_profile 
5. add content to the file


# User specific Terminal config
if [ -n "$PS1" ]; then PS1='\u: \w \$ '; fi
shopt -s checkwinsize
date -u
export PATH=$PATH:/usr/local/bin
####### JAVA DEV #######
export JAVA_HOME=/Library/Java/Home/
export PATH=$PATH:$JAVA_HOME/bin
export PATH=$PATH:/usr/local/mysql/bin
6.  RE-OPEN the Terminal
7. try to run mysql command

uki: ~ $ mysql -u root
Welcome to the MySQL monitor.  Commands end with ; or \g.
Your MySQL connection id is 236
Server version: 5.5.9 MySQL Community Server (GPL)
Copyright (c) 2000, 2010, Oracle and/or its affiliates. All rights reserved.
Oracle is a registered trademark of Oracle Corporation and/or its
affiliates. Other names may be trademarks of their respective
owners.
Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.
mysql> 



Wednesday, September 14, 2011

Placing Android pop-up toasts on the screen


You can place your toasts anywhere on the screen you want. In this case I put it in the center of the screen with 20 pixels off the left margin and 40 below the center line.

Toast toast = Toast.makeText(TrucksScreen.this, getString(R.string.fetching_map_details), Toast.LENGTH_SHORT);
toast.setGravity(Gravity.CENTER, -20, -40);

toast.show();

Friday, September 2, 2011

Solr Search Engine

Starting the engine with Jetty (included J2EE container):

~ uki$ cd /Users/uki/solr/apache-solr-3.3.0/exampleexample uki$ java -jar start.jar


Indexing XML:

~ uki$ cd /Users/uki/solr/apache-solr-3.3.0/example/exampledocs

ushofml299009:exampledocs uki$ java -jar post.jar /Users/uki/Documents/workspace/Spin\ XSLT/SPIN_43.xml


Statistics:

http://localhost:8983/solr/admin/stats.jsp

numDocs : 17

Wednesday, August 24, 2011

Deploying Android apps on phones and tablets

There are features like "telephony" that are supported only on phones, if you include them in your application manifest the tablet users will not be able to find the app in the Market. Google's solution to that is to deploy multiple APK under the same package.

~~~~~~~~~~~~~~~~~~~~~~~~

PHONES (deploy first):

     

It is OK to include android:largeScreens="true" for phones as you will see at the end.

~~~~~~~~~~~~~~~~~~~~~~~~

TABLETS (deploy second):

If you want the app to show on the Android market for tablets, deploy another APK with API-Level 9 ( android:largeScreens="true", android:minSdkVersion="9")





DO NOT include, or try android:required="false" android:name="android.hardware.telephony":





After deployment you will see 2 versions on your developer's dashboard:




Please support us and download the FREE app:



Saturday, August 13, 2011

XML file design and formatting

It is well know that XML files are larger and therefore slower that JSON, however with careful design they don't have to be so. Remember the rule:

1) it the tag repeats only once then it should be converted to an attribute, for example:
- name, latitude, longitude, etc.

2) shorten the tag names, but don't go to far so it is still human readable
3) compress (zip) XML files when transferring them over a network



Formatting:
1) put each attribute on separate line, white space does not cost when compressed
2) extend line length to at least 120 characters for readability, your window size most likely allows for more