Friday, May 9, 2014

How to force the View Pager to wrap its content.

The problem is that the "wrap_content" doesn't seem to be working with the View pager.
Here is the solution:
import android.content.Context;
import android.support.v4.view.ViewPager;
import android.util.AttributeSet;
import android.view.View;

public class CustomViewPager extends ViewPager {

    public CustomViewPager(Context context) {
        super(context);
    }

public CustomViewPager(Context context, AttributeSet attrs){

        super(context, attrs);
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {

        super.onMeasure(widthMeasureSpec, heightMeasureSpec);

        int height = 0;
        for(int i = 0; i < getChildCount(); i++) {

            View child = getChildAt(i);

            child.measure(widthMeasureSpec, MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED));

            int h = child.getMeasuredHeight();

            if(h > height) height = h;

        }

        heightMeasureSpec = MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY);

        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
    }
}

reference:
http://stackoverflow.com/questions/8394681/android-i-am-unable-to-have-viewpager-wrap-content

That's it, don't hesitate to comment, to share your knowledge and to correct me.

Dimming the background behind the dialogs

I'm just reblogging what you can find here.
WindowManager.LayoutParams lp = dialog.getWindow().getAttributes(); 
lp.dimAmount=0.78f; 
dialog.getWindow().setAttributes(lp); 
dialog.getWindow().addFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND);
That's it, don't hesitate to comment, to share your knowledge and to correct me.

Friday, September 27, 2013

Re-sign a .IPA file

The problem:
  1. To re-sign a .ipa file with a different distribution provisioning profile.
  2. To change the bundle identifier.
Required files:
  1. The .ipa file.
  2. The distribution provisioning profile. (.mobileprovision)
  3. Entitlements.plist
Put all the files under the same directory.

Create "Entitlements.plist":
Copy the following code in a text file and make sure that the extension is (.plist).
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
    <key>application-identifier</key>
    <string>kjhdfsui.com.your.bundle.id</string>
    <key>get-task-allow</key>
    <false/>
</dict>
</plist>
To get the bundle-id value used in "Entitlements.plist" (the previous piece of code), open (.mobileprovision) using an editor and copy the value under "Entitlements".

Re-sign:
  1. Unzip the .ipa file. in the terminal type
    unzip MyApp.ipa
  2. Inside the (Payload) folder, you'll find the .app file. Open it by right click > show package content.
  3. find the (info.plist) file and open it using Xcode. replace the old bundle identifier with the new one.
  4. Open the terminal and move to the directory that contains the (.ipa) file.
  5. Remove the old code signature
    rm -r "Payload/MyApp.app/_CodeSignature" "Payload/MyApp.app/CodeResources" 2> /dev/null | true
    
  6. Replace the embedded mobile provisioning profile
    cp "MyEnterprise.mobileprovision" "Payload/MyApp.app/embedded.mobileprovision"
    
  7. Re-sign
    /usr/bin/codesign -f -s "iPhone Distribution: Certificate Name" --resource-rules "Payload/MyApp.app/ResourceRules.plist" --entitlements Entitlements.plist "Payload/MyApp.app"
    
  8. Re-package
    zip -qr "MyApp.resigned.ipa" Payload
    
  9. To submit the app:
    1. Create an app in itunes connect.
    2. Use "Application loader" to upload the app.
For some reason you may face this error "Codesign object file format invalid or unsuitable" in step #7. to solve it, type the following in the terminal:
export CODESIGN_ALLOCATE="/Applications/Xcode.app/Contents/Developer/usr/bin/codesign_allocate"

If you have many versions of Xcode, then you'll need to specify a version that contains "codesign_allocate".

Sunday, August 4, 2013

AFHTTPClient *httpClient = [[AFHTTPClient alloc] initWithBaseURL:[NSURL URLWithString:@"https://graph.facebook.com/"]];
    
    NSString *link = [NSString stringWithFormat:@"https://graph.facebook.com/%@/likes/%@", USER_ID, PAGE_ID];
    
    NSDictionary *params = @{@"access_token" : [[[FBSession activeSession] accessTokenData] accessToken]};
    
    NSMutableURLRequest *request = [httpClient requestWithMethod:@"GET"
                                                            path:link
                                                      parameters:params];
    
    AFJSONRequestOperation *operation = [AFJSONRequestOperation JSONRequestOperationWithRequest:request success:^(NSURLRequest *request, NSHTTPURLResponse *response, id JSON ) {
        
        if([[JSON objectForKey:@"data"] count] == 0){
            
            // The page is not liked.
        }
        else{
            
            // The user likes the page.
        }
    }
    failure:^(NSURLRequest *request, NSHTTPURLResponse *response, NSError *error, id JSON){
        
                                             
    }];

    [operation start];
That's it, don't hesitate to comment, to share your knowledge and to correct me.

Friday, August 2, 2013

iOS, Set CoreData up.

1- Link against CoreData.framework
2- add the following to the appDelegate.h
@property (nonatomic, strong) NSManagedObjectContext *managedObjectContext;
@property (nonatomic, strong) NSManagedObjectModel *managedObjectModel;
@property (nonatomic, strong) NSPersistentStoreCoordinator *persistentStoreCoordinator;
3- add the following to the appDelegate.m
#import <coredata/coredata.h>
// Returns the managed object context for the application.
// If the context doesn't already exist, it is created and bound to the persistent store coordinator for the application.
- (NSManagedObjectContext *)managedObjectContext
{
    if (_managedObjectContext != nil) {
        return _managedObjectContext;
    }
    
    NSPersistentStoreCoordinator *coordinator = [self persistentStoreCoordinator];
    if (coordinator != nil) {
        _managedObjectContext = [[NSManagedObjectContext alloc] init];
        [_managedObjectContext setPersistentStoreCoordinator:coordinator];
    }
    return _managedObjectContext;
}
// Returns the managed object model for the application.
// If the model doesn't already exist, it is created from the application's model.
- (NSManagedObjectModel *)managedObjectModel
{
    if (_managedObjectModel != nil) {
        return _managedObjectModel;
    }
    NSURL *modelURL = [[NSBundle mainBundle] URLForResource:@"database" withExtension:@"momd"];
    _managedObjectModel = [[NSManagedObjectModel alloc] initWithContentsOfURL:modelURL];
    return _managedObjectModel;
}
// Returns the persistent store coordinator for the application.
// If the coordinator doesn't already exist, it is created and the application's store added to it.
- (NSPersistentStoreCoordinator *)persistentStoreCoordinator
{
    if (_persistentStoreCoordinator != nil) {
        return _persistentStoreCoordinator;
    }
    
    NSURL *storeURL = [[self applicationDocumentsDirectory] URLByAppendingPathComponent:@"database.sqlite"];
    
    NSError *error = nil;
    _persistentStoreCoordinator = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:[self managedObjectModel]];
    if (![_persistentStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:storeURL options:nil error:&error]) {
        /*
         Replace this implementation with code to handle the error appropriately.
         
         abort() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development. 
         
         Typical reasons for an error here include:
         * The persistent store is not accessible;
         * The schema for the persistent store is incompatible with current managed object model.
         Check the error message to determine what the actual problem was.
         
         
         If the persistent store is not accessible, there is typically something wrong with the file path. Often, a file URL is pointing into the application's resources directory instead of a writeable directory.
         
         If you encounter schema incompatibility errors during development, you can reduce their frequency by:
         * Simply deleting the existing store:
         [[NSFileManager defaultManager] removeItemAtURL:storeURL error:nil]
         
         * Performing automatic lightweight migration by passing the following dictionary as the options parameter: 
         [NSDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithBool:YES], NSMigratePersistentStoresAutomaticallyOption, [NSNumber numberWithBool:YES], NSInferMappingModelAutomaticallyOption, nil];
         
         Lightweight migration will only work for a limited set of schema changes; consult "Core Data Model Versioning and Data Migration Programming Guide" for details.
         
         */
        NSLog(@"Unresolved error %@, %@", error, [error userInfo]);
        abort();
    }    
    
    return _persistentStoreCoordinator;
}
// Returns the URL to the application's Documents directory.
- (NSURL *)applicationDocumentsDirectory
{
    return [[[NSFileManager defaultManager] URLsForDirectory:NSDocumentDirectory inDomains:NSUserDomainMask] lastObject];
}
That's it, don't hesitate to comment, to share your knowledge and to correct me.

iOS email validator

-(BOOL) isValidEmail:(NSString *)checkString
{
    BOOL stricterFilter = YES; // Discussion http://blog.logichigh.com/2010/09/02/validating-an-e-mail-address/
    NSString *stricterFilterString = @"[A-Z0-9a-z._%+-]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,4}";
    NSString *laxString = @".+@.+\\.[A-Za-z]{2}[A-Za-z]*";
    NSString *emailRegex = stricterFilter ? stricterFilterString : laxString;
    NSPredicate *emailTest = [NSPredicate predicateWithFormat:@"SELF MATCHES %@", emailRegex];
    return [emailTest evaluateWithObject:checkString];
}
That's it, don't hesitate to comment, to share your knowledge and to correct me.

Friday, April 5, 2013

NSURL *baseURL = [NSURL URLWithString:@"https://graph.facebook.com/"];
AFHTTPClient *httpClient = [[AFHTTPClient alloc] initWithBaseURL:baseURL];

NSString *link = [NSString stringWithFormat:@"/%@/likes", @"OBJECT_ID"];
NSDictionary *params = @{@"access_token" : [[[FBSession activeSession] accessTokenData] accessToken]};

[httpClient postPath:link parameters:params success:^(AFHTTPRequestOperation *op, id result) {

    NSLog(@"result %@", result);

  } failure:^(AFHTTPRequestOperation *operation, NSError *error) {

    NSLog(@"error %@", error);

}];
That's it, don't hesitate to comment, to share your knowledge and to correct me.

Thursday, January 24, 2013

Twitter for iOS6 without the modal view.

Hi,
I guess I got this one from some Stackoverflow question.
Link against Social.framework and accounts.framework.
 #import <Accounts/Accounts.h>  
 #import <Social/Social.h>  
 =========================================
 ACAccountStore *account = [[ACAccountStore alloc] init];  
 ACAccountType *accountType = [account accountTypeWithAccountTypeIdentifier:    
           ACAccountTypeIdentifierTwitter];  
       
 [account requestAccessToAccountsWithType:accountType options:nil   
         completion:^(BOOL granted, NSError *error)  
 {  
    if (granted == YES)  
    {  
      NSArray *arrayOfAccounts = [account   
          accountsWithAccountType:accountType];  
   
      if ([arrayOfAccounts count] > 0)  
      {  
       ACAccount *twitterAccount = [arrayOfAccounts lastObject];  
   
       NSDictionary *message = @{@"status": @”My First Twitter post from iOS6”};  
         
       NSURL *requestURL = [NSURL   
        URLWithString:@"http://api.twitter.com/1/statuses/update.json"];  
   
       SLRequest *postRequest = [SLRequest   
         requestForServiceType:SLServiceTypeTwitter  
             requestMethod:SLRequestMethodPOST  
             URL:requestURL parameters:message];  
   
       postRequest.account = twitterAccount;  
   
       [postRequest performRequestWithHandler:^(NSData *responseData,   
             NSHTTPURLResponse *urlResponse, NSError *error)  
       {  
          NSLog(@"Twitter HTTP response: %i", [urlResponse   
             statusCode]);  
       }];  
      }  
    }  
 }];  
That's it, don't hesitate to comment, to share your knowledge and to correct me. 
Hi,
I found this here.
Link the project against Twitter.framework and accounts.framework.
 #import <Twitter/Twitter.h>  
 #import <Accounts/Accounts.h>  
 =============================================  
 - (void)postToTwitter   
  {   
   // Create an account store object.   
   ACAccountStore *accountStore = [[ACAccountStore alloc] init];   
     
   // Create an account type that ensures Twitter accounts are retrieved.   
   ACAccountType *accountType = [accountStore accountTypeWithAccountTypeIdentifier:  
   ACAccountTypeIdentifierTwitter];   
     
   // Request access from the user to use their Twitter accounts.   
   [accountStore requestAccessToAccountsWithType:accountType   
   withCompletionHandler:^(BOOL granted, NSError *error) {   
    if(granted) {   
     // Get the list of Twitter accounts.   
     NSArray *accountsArray = [accountStore accountsWithAccountType:accountType];   
     
     if ([accountsArray count] > 0) {   
      // Grab the initial Twitter account to tweet from.   
      ACAccount *twitterAccount = [accountsArray objectAtIndex:0];   
      TWRequest *postRequest = nil;   
     
      postRequest = [[TWRequest alloc] initWithURL:[NSURL URLWithString:  
             @"http://api.twitter.com/1/statuses/update.json"]   
             parameters:[NSDictionary dictionaryWithObject:  
             @"The message to post" forKey:@"status"]   
             requestMethod:TWRequestMethodPOST];   
     
      // Set the account used to post the tweet.   
      [postRequest setAccount:twitterAccount];   
     
      dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^(void) {   
       [postRequest performRequestWithHandler:^(NSData *responseData, NSHTTPURLResponse *urlResponse, NSError *error) {   
        dispatch_async(dispatch_get_main_queue(), ^(void) {   
         if ([urlResponse statusCode] == 200) {   
          NSLog(@"Successful");   
         }else {   
     
         NSLog(@"Failed");   
         }   
        });   
       }];   
      });   
     
     }   
     else   
     {   
      [[UIApplication sharedApplication] openURL:[NSURL URLWithString:@"prefs:root=TWITTER"]];   
     }   
    }   
   }];   
  }
That's it, don't hesitate to comment, to share your knowledge and to correct me.

Sunday, December 9, 2012


Hola,
It's highly recommended to use (dp/dip) when defining UI layout, to express layout dimensions or position in a density-independent way.
But, how can we specify the dimensions in (dp) programmatically?
The answer is:
 float android.util.TypedValue.applyDimension(int unit, float value, 
 DisplayMetrics metrics)  
Where (as mentioned in the documentation):
unit is the unit to convert from.
value is the value to apply the unit to.
metrics is the current display metrics to use in the conversion -- supplies display density and scaling information.

Example:
TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, <whatever dp value>,
 getResources().getDisplayMetrics());  
That's it, don't hesitate to comment, to share your knowledge and to correct me.

Monday, November 26, 2012

iOS6, Rotation.

Hi, it's been a while since I posted some thing.

Ok, This post is to fix the auto-rotation problem that has been around since the release of iOS6.

To be clear, the problem is that iOS6 doesn't support - (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation; any more. And hence, our apps/code won't behave as expected any more.

In this post, I'll be using three methods/callbacks were introduced in iOS6 which are (BOOL)shouldAutorotate- (NSUInteger)supportedInterfaceOrientations and (NSUInteger)application:(UIApplication *)application supportedInterfaceOrientationsForWindow:(UIWindow *)window.

One important thing to know, (BOOL)shouldAutorotate and (NSUInteger)supportedInterfaceOrientations are added to the topmost parent of the view controller that you want to control its rotation. if, of-course, the view controller is a root view controller, then these methods will be added to it directly.

I hate to repeat myself, but for the sake of clarification, if your root-view-controller is a navigation-controller and you want to control the rotation of a view-controller inside the navigation-controller then  (BOOL)shouldAutorotate and (NSUInteger)supportedInterfaceOrientations will be added to the navigation-controller NOT the view-controller itself.

If you have a tab-bar-controller that contains a navigation-controller inside which lies the view-controller that you want to control its rotation, then (BOOL)shouldAutorotate and (NSUInteger)supportedInterfaceOrientations will be added to the topmost parent which is - in this case - the tab-bar-controller.

How to add those methods to a tab-bar-controller or a navigation-controller?
  1. By subclassing them and overriding the methods inside them.
  2. By adding them to a category of your tab-bar-controller's or navigation-controller's type. I like this method because, it's simpler specially if the code is already written and i'm doing some slight modifications.
Now, lets take the case of a tab-bar-controller that contains a navigation-controller inside which lies the view-controller that you want to control its rotation as an example. you'll do as follows:
  1. add (NSUInteger)application:(UIApplication *)application supportedInterfaceOrientationsForWindow:(UIWindow *)window to your appdelegate as follows:
     - (NSUInteger)application:(UIApplication *)application 
    supportedInterfaceOrientationsForWindow:(UIWindow *)window {  
       return (UIInterfaceOrientationMaskPortrait | UIInterfaceOrientationMaskLandscapeLeft |  
           UIInterfaceOrientationMaskLandscapeRight);  
     }  
    

  2. create a category inside your appdelegate as follows:
     @implementation tab-view-controller (fixingRotation)  
       
     - (BOOL)shouldAutorotate {  
        
       if ([((UINavigationController *)[[((AppDelegate *)[[UIApplication sharedApplication]
     delegate]).tabBarController viewControllers] objectAtIndex:self.selectedIndex])
    .visibleViewController isKindOfClass:[myViewController class]]) {  
           
         return YES;  
       }  
       else{  
           
         return NO;  
       }  
     }  
     -(NSUInteger) supportedInterfaceOrientations {  
       return (UIInterfaceOrientationMaskPortrait | UIInterfaceOrientationMaskLandscapeLeft |  
           UIInterfaceOrientationMaskLandscapeRight);  
     }  
       
     @end  
And your done.

Don't remove -(BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation from your code to keep supporting iOS5.

It's recommended to see the documentation for better understanding of the callbacks.

That's it, don't hesitate to comment, to share your knowledge and to correct me. 

Monday, April 23, 2012

Android: Saving cache files on external storage.

Hi,

If you want to save some files on external storage and you want those files to be removed on application un-installation, then do as the documentation says ;)

If you're using API Level 8 or greater, use getExternalCacheDir() to open a File 
that represents the external storage directory where you should save cache files. If 
the user uninstalls your application, these files will be automatically deleted. However,
 during the life of your application, you should manage these cache files and remove 
those that aren't needed in order to preserve file space.  
   
If you're using API Level 7 or lower, use getExternalStorageDirectory() to open a File
that represents the root of the external storage, then write your cache data in the following
 directory:  
   
 /Android/data/<package_name>/cache/ 
 The <package_name> is your Java-style package name, such as "com.example.android.app".  

Unfortunately, our apps can not receive un-install intent. So, this is the best way to do that.

That's it, don't hesitate to comment, to share your knowledge and to correct me.

Tuesday, April 17, 2012

Android: Create an image Viewer using ViewPager.

Hi,

This post is for those guys digging the Internet trying to find out how to create an image viewer. And I managed to do that using ViewPager.

First, you'll need to download the support package using the SDK manager.

Then, set up the project to use the library, as mentioned here. OR, if you use Eclipse, you can simply right click your project > Android Tools > Add Compatibility Library.

I'll use the project that we created in the previous tutorial to get a horizontal list of images and I'll make some modifications to get to our target.

So, the main layout will be as follows.

main.xml
 <?xml version="1.0" encoding="utf-8"?>  
 <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"  
   android:layout_width="fill_parent"  
   android:layout_height="fill_parent" >  
     
      <HorizontalScrollView android:layout_width="fill_parent"  
        android:layout_height="wrap_content">  
          
        <LinearLayout android:id="@+id/_linearLayout"  
          android:layout_width="fill_parent"  
          android:layout_height="wrap_content"  
          android:orientation="horizontal">  
            
        </LinearLayout>  
          
      </HorizontalScrollView>  
     
      <android.support.v4.view.ViewPager android:id="@+id/_viewPager"  
          android:layout_width="fill_parent"  
          android:layout_height="fill_parent"  
          android:background="#000000"  
          android:visibility="gone" />  
        
 </RelativeLayout>  

cell.xml
  <?xml version="1.0" encoding="UTF-8"?>   
  <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"   
      android:id="@+id/linearLayout1"   
      android:layout_width="wrap_content"   
      android:layout_height="wrap_content"   
      android:orientation="vertical" >   
     
   <ImageView android:id="@+id/_image"   
       android:layout_width="wrap_content"   
       android:layout_height="wrap_content"/>   
      
   <TextView android:id="@+id/_imageName"   
       android:layout_width="wrap_content"   
       android:layout_height="wrap_content"   
       android:layout_gravity="center_horizontal"/>   
      
  </LinearLayout>   

We need to create a class that extends PagerAdapter - which is a base class that provide the adapter to populate pages inside of a ViewPager - as mentioned here.

 import android.app.Activity;  
 import android.os.Parcelable;  
 import android.support.v4.view.PagerAdapter;  
 import android.support.v4.view.ViewPager;  
 import android.view.View;  
 import android.widget.ImageView;  
   
 public class GalleryPagerAdapter extends PagerAdapter {  
   
      private Activity activity;  
      private int[] drawableIDs;  
        
      public GalleryPagerAdapter(Activity activity,int[] drawableIDs){  
             
           this.activity = activity;  
           this.drawableIDs = drawableIDs;  
      }  
        
   @Override  
   public int getCount() {  
       return drawableIDs.length;  
   }  
   
      /**  
       * Create the page for the given position. The adapter is responsible  
       * for adding the view to the container given here, although it only  
       * must ensure this is done by the time it returns from  
       * {@link #finishUpdate()}.  
       *  
       * @param container The containing View in which the page will be shown.  
       * @param position The page position to be instantiated.  
       * @return Returns an Object representing the new page. This does not  
       * need to be a View, but can be some other container of the page.  
       */  
   @Override  
   public Object instantiateItem(View collection, int position) {  
         
       ImageView imageView = new ImageView(activity);  
         
       imageView.setBackgroundResource(drawableIDs[position]);  
         
       ((ViewPager) collection).addView(imageView,0);  
         
       return imageView;  
   }  
   
      /**  
       * Remove a page for the given position. The adapter is responsible  
       * for removing the view from its container, although it only must ensure  
       * this is done by the time it returns from {@link #finishUpdate()}.  
       *  
       * @param container The containing View from which the page will be removed.  
       * @param position The page position to be removed.  
       * @param object The same object that was returned by  
       * {@link #instantiateItem(View, int)}.  
       */  
   @Override  
   public void destroyItem(View collection, int position, Object view) {  
       ((ViewPager) collection).removeView((ImageView) view);  
   }  
   
   @Override  
   public boolean isViewFromObject(View view, Object object) {  
       return view==((ImageView)object);  
   }  
   
      /**  
       * Called when the a change in the shown pages has been completed. At this  
       * point you must ensure that all of the pages have actually been added or  
       * removed from the container as appropriate.  
       * @param container The containing View which is displaying this adapter's  
       * page views.  
       */  
   @Override  
   public void finishUpdate(View arg0) {}  
     
   @Override  
   public void restoreState(Parcelable arg0, ClassLoader arg1) {}  
   
   @Override  
   public Parcelable saveState() {  
       return null;  
   }  
   
   @Override  
   public void startUpdate(View arg0) {}  
 }  

And finally, the activity.

 import android.app.Activity;  
 import android.os.Bundle;  
 import android.support.v4.view.ViewPager;  
 import android.view.View;  
 import android.view.View.OnClickListener;  
 import android.widget.ImageView;  
 import android.widget.LinearLayout;  
 import android.widget.TextView;  
   
 public class TestingActivity extends Activity {  
        
      // mainLayout is the child of the HorizontalScrollView ...  
      private LinearLayout mainLayout;  
        
      // this is an array that holds the IDs of the drawables ...  
      private int[] images = {R.drawable.dd1, R.drawable.dd2, 
      R.drawable.dd3, R.drawable.dd4, R.drawable.dd5, R.drawable.dd6, R.drawable.dd7};  
        
      private View cell;  
      private TextView text;  
        
      private ViewPager viewPager;  
        
      @Override  
      public void onBackPressed() {  
             
           if(viewPager != null && viewPager.isShown()){  
                  
                viewPager.setVisibility(View.GONE);  
           }  
           else{  
   
                super.onBackPressed();  
           }  
      }  
        
   /** Called when the activity is first created. */  
   @Override  
   public void onCreate(Bundle icicle) {  
     super.onCreate(icicle);  
       
     setContentView(R.layout.main);  
   
     viewPager = (ViewPager) findViewById(R.id._viewPager);  
       
     mainLayout = (LinearLayout) findViewById(R.id._linearLayout);  
       
     for (int i = 0; i < images.length; i++) {  
                  
          cell = getLayoutInflater().inflate(R.layout.cell, null);  
            
          final ImageView imageView = (ImageView) cell.findViewById(R.id._image);  
          imageView.setOnClickListener(new OnClickListener() {  
                       
                     @Override  
                     public void onClick(View v) {  
                            
                          viewPager.setVisibility(View.VISIBLE);  
                          viewPager.setAdapter
                          (new GalleryPagerAdapter(TestingActivity.this, images));  
                          viewPager.setCurrentItem(v.getId());  
                     }  
                });  
            
          imageView.setId(i);  
            
          text = (TextView) cell.findViewById(R.id._imageName);  
            
          imageView.setImageResource(images[i]);  
          text.setText("Image#"+(i+1));  
            
          mainLayout.addView(cell);  
       }  
   }  
 }  

That's it, don't hesitate to comment, to share your knowledge and to correct me. 

Sunday, April 15, 2012

Android: Create a simple horizontal image list.

Hi,

In this post we'll create our horizontal image List using HorizontalScrollView.

First,  create the layout.

main.xml
 <?xml version="1.0" encoding="utf-8"?>  
 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"  
   android:orientation="vertical"  
   android:layout_width="fill_parent"  
   android:layout_height="fill_parent" >  
     
      <HorizontalScrollView android:layout_width="fill_parent"  
        android:layout_height="wrap_content">  
          
        <LinearLayout android:id="@+id/_linearLayout"  
          android:layout_width="fill_parent"  
          android:layout_height="wrap_content"  
          android:orientation="horizontal">  
            
        </LinearLayout>  
          
      </HorizontalScrollView>  
     
 </LinearLayout>  

The cell can be as simple as an image or can be more complex such as an image and a text below the image.

cell.xml
 <?xml version="1.0" encoding="UTF-8"?>  
 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"  
   android:id="@+id/linearLayout1"  
   android:layout_width="wrap_content"  
   android:layout_height="wrap_content"  
   android:orientation="vertical" >  
   
   <ImageView android:id="@+id/_image"  
     android:layout_width="wrap_content"  
     android:layout_height="wrap_content"/>  
     
   <TextView android:id="@+id/_imageName"  
     android:layout_width="wrap_content"  
     android:layout_height="wrap_content"  
     android:layout_gravity="center_horizontal"/>  
     
 </LinearLayout>  

Now, add some pics to the drawable folder.

The last thing that we'll do is to create our activity.

 package com.testing.testing;  
   
 import android.app.Activity;  
 import android.os.Bundle;  
 import android.view.View;  
 import android.view.View.OnClickListener;  
 import android.widget.ImageView;  
 import android.widget.LinearLayout;  
 import android.widget.TextView;  
 import android.widget.Toast;  
   
 public class TestingActivity extends Activity {  
        
      // mainLayout is the child of the HorizontalScrollView ...  
      private LinearLayout mainLayout;  
        
      // this is an array that holds the IDs of the drawables ...  
      private int[] images = {R.drawable.dd1, R.drawable.dd2, R.drawable.dd3,
                   R.drawable.dd4, R.drawable.dd5, R.drawable.dd6, R.drawable.dd7};  
        
      private View cell;  
      private TextView text;  
        
   /** Called when the activity is first created. */  
   @Override  
   public void onCreate(Bundle icicle) {  
     super.onCreate(icicle);  
       
     setContentView(R.layout.main);  
   
     mainLayout = (LinearLayout) findViewById(R.id._linearLayout);  
       
     for (int i = 0; i < images.length; i++) {  
                  
          cell = getLayoutInflater().inflate(R.layout.cell, null);  
            
          final ImageView imageView = (ImageView) cell.findViewById(R.id._image);  
          imageView.setOnClickListener(new OnClickListener() {  
                       
                     @Override  
                     public void onClick(View v) {  
                          // do whatever you want ...  
                          Toast.makeText(TestingActivity.this, 
                          (CharSequence) imageView.getTag(), Toast.LENGTH_SHORT).show();  
                     }  
                });  
            
          imageView.setTag("Image#"+(i+1));  
            
          text = (TextView) cell.findViewById(R.id._imageName);  
            
          imageView.setImageResource(images[i]);  
          text.setText("Image#"+(i+1));  
            
          mainLayout.addView(cell);  
      }  
   }  
 }  

That's it, don't hesitate to comment, to share your knowledge and to correct me. 

Friday, April 6, 2012

Android: Layout params programmatically.

Hi,

This is a concise tip.

To set the width, height and gravity for -say - a LinearLayout, do the following:

 LinearLayout linearLayout = (LinearLayout)findViewById(R.id.movieDetails_linearLayout);  
             
 linearLayout.setLayoutParams(new ParentView.LayoutParams(LayoutParams.WRAP_CONTENT  
                                    , LayoutParams.WRAP_CONTENT,Gravity.CENTER_HORIZONTAL));  

Note that You have to use the layout's parent when setting the parameters. ex.) if the layout's parent is a scroll view, then you'll do the following:

linearLayout.setLayoutParams(new ScrollView.LayoutParams(LayoutParams.WRAP_CONTENT  
                            , LayoutParams.WRAP_CONTENT,Gravity.CENTER_HORIZONTAL));  

That's it, don't hesitate to comment, to share your knowledge and to correct me. 

Saturday, March 31, 2012

Hi,

I've never imagined that playing a HTML5 Youtube video inside a WebView would be such a tedious task. I even didn't think of it as a separate task.

The problem is that the video doesn't play. YES, it's as simple as that.

Any way, I found a work around solution that causes the video to run inside the media player instead of the WebView and of-course instead of launching the browser.

First, you'll need to add the next piece of code to your activity.

 private class MyChromeClient extends WebChromeClient implements OnCompletionListener, OnErrorListener{  
   
        FrameLayout.LayoutParams COVER_SCREEN_GRAVITY_CENTER = new FrameLayout.LayoutParams(  
            ViewGroup.LayoutParams.WRAP_CONTENT,  
            ViewGroup.LayoutParams.WRAP_CONTENT, Gravity.CENTER);  
   
        @Override  
        public void onShowCustomView(View view, CustomViewCallback callback) {  
          if (view instanceof FrameLayout) {  

            // mainWebView is the view that the video should've played inside.
            wv = (WebView)findViewById(R.id.mainWebView);  
                 
            mCustomViewContainer = (FrameLayout) view;  
            mCustomViewCallback = callback;  

            // mainLayout is the root layout that (ex. the layout that contains the webview)
            mContentView = (RelativeLayout)findViewById(R.id.mainLayout);  
            if (mCustomViewContainer.getFocusedChild() instanceof VideoView) {  
              mVideoView = (VideoView) mCustomViewContainer.getFocusedChild();  
              // frame.removeView(video);  
              mContentView.setVisibility(View.GONE);  
              mCustomViewContainer.setVisibility(View.VISIBLE);  
              setContentView(mCustomViewContainer);  
              mVideoView.setOnCompletionListener(this);  
              mVideoView.setOnErrorListener(this);  
              mVideoView.start();  
   
            }  
          }  
        }  
   
        public void onHideCustomView() {  
          if (mVideoView == null){  
            return;  
          }else{  
          // Hide the custom view.  
          mVideoView.setVisibility(View.GONE);  
          // Remove the custom view from its container.  
          mCustomViewContainer.removeView(mVideoView);  
          mVideoView = null;  
          mCustomViewContainer.setVisibility(View.GONE);  
          mCustomViewCallback.onCustomViewHidden();  
          // Show the content view.  
          mContentView.setVisibility(View.VISIBLE);  
          }  
        }  
   
        public void onCompletion(MediaPlayer mp) {  
          mp.stop();  
          mCustomViewContainer.setVisibility(View.GONE);  
          onHideCustomView();  
          setContentView(mContentView);  
        }  
   
        public boolean onError(MediaPlayer arg0, int arg1, int arg2) {  
          setContentView(mContentView);  
          return true;  
        }  
      }  

Then, you need (of-course) to use the back button to close the video and to go back. And this is how I managed to do this.

 @Override  
      public void onBackPressed() {  
            if(mCustomViewContainer != null){ 
                 
                 mVideoView.stopPlayback();  
                 mCustomViewContainer.setVisibility(View.GONE);  
            
                 if (mVideoView == null){  
              
                      return;  
                 }else{  
                      
                      // Hide the custom view.  
                      mVideoView.setVisibility(View.GONE);  
                      // Remove the custom view from its container.  
                      mCustomViewContainer.removeView(mVideoView);  
                      mVideoView = null;  
                      mCustomViewContainer.setVisibility(View.GONE);  
                      mCustomViewCallback.onCustomViewHidden();  
                      // Show the content view.  
                      mContentView.setVisibility(View.VISIBLE);  
                      setContentView(mContentView);  
                      mCustomViewContainer = null; 
                 }  
            }else if(mainWebView.canGoBack()){
   
                 mainWebView.goBack();
          
            }else{
 
                 super.onBackPressed(); 
            }
      }

You'll need is to add your member variables 

 private VideoView mVideoView;  
 private RelativeLayout mContentView;  
 private FrameLayout mCustomViewContainer;  
 private WebChromeClient.CustomViewCallback mCustomViewCallback;  

One last thing, and actually it's the most important step, is to add the next line.

 webView.setWebChromeClient(new MyChromeClient());  

That's it, don't hesitate to comment, to share your knowledge and to correct me. 

Saturday, March 24, 2012

Hi,

The problem can be solved by opening the keypad and pressing the following keys:

*2767*3855#

CAUTION:
Once you press the ( # ) key, the device will start the process. No menus will appear and No options will be available, The process will just start.

The original thread is here.

That's it, don't hesitate to comment, to share your knowledge and to correct me. 

Wednesday, March 21, 2012

Hi,

I was wondering how Google can find my location when I'm using WiFi, and I kept digging the Internet to find an answer.

To collect information about WiFi access points, Google uses information provided by  
  • cellphone handsets.
  • computer applications.
  • radio receivers attached to vehicles.
Google used wardriving to collect data about access points. It used the same vehicles which collected imagery for the street view service to collect information about WiFi access points.

Furthermore, according to this page, Google also uses Android powered devices to collect these data as follows: 

Android Location Services periodically checks on your location using GPS, Cell-ID, and WiFi to locate your device. When it does this, your Android phone will send back publicly broadcast WiFi access points' Service set identifier (SSID) and Media Access Control (MAC) data. 

The Data is used in aggregate to improve Google's location based services.

The data collected by the vehicle-based method is not tied to any particular user. Google doesn't share location based services directly with users but through the free publicly available Geolocation API.

Applications that return information to users receive geocoded locations that are individual to the user's request and they don't receive specific information about an access point.

When using vehicle-based method, Data about WiFi access points are collected passively which means that the equipment that receives the data  receives signals broadcast to it but not actively initiating a communication with the access point.

Data collected is only that is broadcast publicly on the WiFi radio network. It's visible to anyone else with a WiFi receiver.

Google location based services using WiFi access points data work as follows:

  • The user's device sends a request to Google location server with a list of MAC addresses which are currently visible to the device.
  • The location server compares the MAC addresses seen by the user's device with it's list of known MAC addresses, and identifies associated geocoded locations (i.e. longitude/latitude).
  • The location server then uses the geocoded locations associated with visible MAC addresses to triangulate the approximate location of the user.
    So, The more WiFi networks around, the higher is the accuracy of the positioning.
  • This approximate location is then geocoded and sent back to the user's device.

For more details, you can visit this and this.

That's it, don't hesitate to comment, to share your knowledge and to correct me. 

Friday, March 9, 2012

This Post Is Deprecated ...

Hi,

Once upon a time I was developing an application for platforms starting from 2.1 ( API level 7 ) and it happened that I needed the app to receive notifications from the server.

Actually, I achieved this using C2DM directly ( with no external libraries ) by following this tutorial, AND then I  achieved this  once again using Urban Airship's push library for Android.

The main advantage of using C2DM directly is that the documentation is very clear as always and by following the previously mentioned tutorial you can get every thing working in no time.

But, on the other hand, it requires platform 2.2 ( API level 8 ) at least and it takes a little bit more coding effort for both the client and server sides.

After all, I enjoyed it and I think that without using C2DM directly, I wouldn't be able to understand how Urban Airship works.

Why Urban Airship:
  • Allows the devices running platforms lower than 2.2 ( API level 8 ) to receive push notifications.
  • Handles many requests between the device and the server on behalf of the developer (e.g. the registration tying an APID (Android Push ID -  is the Urban Airship ID of a device to which a message can be pushed from the Urban Airship API ) to your application happens automatically ).

  • Broadcasts the messages to all the registered devices ( of course you can do it just by looping on all the registered devices, but eventually Urban Airship still does it for you ).

  • And of course because, it was a recommendation from a friend :)
The only thing that I didn't like about Urban Airship is that the documentation wasn't so clear as the one from Google that made me spend more time trying to figure out what's going on but once I did, it was a very easy task to do again and again.

Steps:
  1. Visit Urban Airship's website. Choose the pricing plan that suits your needs and sign up here.

  2. Sign up for C2DM here to authorize your Google account to become a C2DM sender ( to be able to push notifications ) and later we'll use the same Google account to generate a token for Urban Airship.

  3. Download the library from here.

  4. Create a new folder in your Android project and place the jar file inside it and then add the jar to your project libraries.


  5. Now, we need to get the authorization token by which C2DM authorizes my account ( you’ll give that token to Urban Airship so it can send messages on your behalf ).

    • Create a Java project and then create the following class:

    •  import java.io.BufferedReader;  
       import java.io.IOException;  
       import java.io.InputStreamReader;  
       import java.io.OutputStream;  
       import java.net.HttpURLConnection;  
       import java.net.URL;  
         
       public class AuthenticationUtil {  
            private AuthenticationUtil() {  
                 // Util class cannot get instanziated  
            }  
         
            public static String getToken(String email, String password)  
                      throws IOException {  
                 // Create the post data  
                 // Requires a field with the email and the password  
                 StringBuilder builder = new StringBuilder();  
                 builder.append("Email=").append(email);  
                 builder.append("&Passwd=").append(password);  
                 builder.append("&accountType=GOOGLE");  
                 builder.append("&source=MyLittleExample");  
                 builder.append("&service=ac2dm");  
         
                 // Setup the Http Post  
                 byte[] data = builder.toString().getBytes();  
                 URL url = new URL("https://www.google.com/accounts/ClientLogin");  
                 HttpURLConnection con = (HttpURLConnection) url.openConnection();  
                 con.setUseCaches(false);  
                 con.setDoOutput(true);  
                 con.setRequestMethod("POST");  
                 con.setRequestProperty("Content-Type",  
                           "application/x-www-form-urlencoded");  
                 con.setRequestProperty("Content-Length", Integer.toString(data.length));  
         
                 // Issue the HTTP POST request  
                 OutputStream output = con.getOutputStream();  
                 output.write(data);  
                 output.close();  
         
                 // Read the response  
                 BufferedReader reader = new BufferedReader(new InputStreamReader(  
                           con.getInputStream()));  
                 String line = null;  
                 String auth_key = null;  
                 while ((line = reader.readLine()) != null) {  
                      if (line.startsWith("Auth=")) {  
                           auth_key = line.substring(5);  
                      }  
                 }  
         
                 // Finally get the authentication token  
                 // To something useful with it  
                 return auth_key;  
            }  
       }  
      

    • Now, in the Java project, create the following class which will get the authentication token:

    •  import java.io.IOException;  
         
       import de.vogella.java.c2dm.server.secret.SecureStorage;  
       import de.vogella.java.c2dm.server.util.AuthenticationUtil;  
         
       public class GetAuthenticationToken {  
         
            public static void main(String[] args) throws IOException {  
                 String token = AuthenticationUtil.getToken(YOUR E-MAIL,  
                           YOUR PASSWORD);  
                 System.out.println(token);  
            }  
       }  
      

    • Run GetAuthenticationToken and get the token.

    • Hint: the previous snippets are from here.

  6. Back to your Urban Airship's account, from the SELECT AN APPLICATION tab, create a new app.


  7. Set your app up:

    • Enter the application name.

    • Set the Application mode to (Development), and later you can change it to (Production) after you finish testing the app.

    • check the check box in front of (Push Notifications Support), you can check the others (Rich Push Enabled and In-App Purchase and Subscriptions) too if you need and depending on the pricing plan that you've chosen.

    • Then you have to enter your project's package.

    • And - at last - enter the C2DM authorization token ( yes, the one that we used the previous two snippets to get ).

    • Press (Create your application) button.


  8. That's all the configuration thing, now, lets move to the app.

  9. Create a file inside the assets folder named airshipconfig.properties or you can copy it from the push example that you previously downloaded and then modify the values inside it.

  10.  developmentAppKey = Your Development App Key  
     developmentAppSecret = Your Development Secret  
     #productionAppKey = Your Production App Key  
     #productionAppSecret = Your Production Secret  
       
     #transport is "c2dm", "helium" or "hybrid".  
     #note: the helium and hybrid transports are premium features.  
     #for complete pricing information, see http://urbanairship.com/pricing/  
       
     transport = c2dm  
       
     c2dmSender = the email that you registered to C2DM with  
     inProduction = false  
    

  11. you can get your development key and secret from your app's page on Urban Airship.


  12. Create a class that extends BroadcastReceiver and lets call it IntentReceiver, we'll use this receiver to receive the messages and to handle it as follows:

  13.  public class IntentReceiver extends BroadcastReceiver {   
         
       private String msg;   
          
        @Override   
        public void onReceive(Context context, Intent intent) {   
              
           String action = intent.getAction();   
         
           // if a notification is received ...   
           if (action.equals(PushManager.ACTION_PUSH_RECEIVED)) {   
         
             // Do whatever you want ....   
         
           // if the notification is opened/clicked ...   
           } else if (action.equals(PushManager.ACTION_NOTIFICATION_OPENED)) {   
         
             // here you can get the extras from the intent.   
             // and then you can use it as you wish.   
             msg = intent.getStringExtra(PushManager.EXTRA_ALERT);   
                
             // for example, you can start an activity and send the msg as an extra.   
             Intent launch = new Intent(Intent.ACTION_MAIN);   
             launch.setClass(UAirship.shared().getApplicationContext(), MyActivity.class);   
             launch.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);   
             launch.putExtra("notification", msg);   
         
             UAirship.shared().getApplicationContext().startActivity(launch);   
         
             // save the APID in a shared preferences ...   
             //     it'll be sent to the server ...   
           } else if (action.equals(PushManager.ACTION_REGISTRATION_FINISHED)) {   
              
                         // to log the APID ...  
                         Log.i(logTag, "Registration complete. APID:"  
                              + intent.getStringExtra(PushManager.EXTRA_APID)  
                              + ". Valid: "  
                              + intent.getBooleanExtra(  
                                        PushManager.EXTRA_REGISTRATION_VALID, false));  
                       
             // if registration is successful ...   
             if(intent.getBooleanExtra(PushManager.EXTRA_REGISTRATION_VALID, false)){   
                
                // Do whatever you want ....   
             }   
             else{   
                   
                // register again ...   
             }   
           }   
        }   
      }   
    

  14. Create a class that extends Application and override its onCreate() method as follows:

  15.    
     public class MyApplication extends Application {    
          
       @Override    
       public void onCreate() {    
         
               // Take off with options defined in airshipconfig.properties...  
               UAirship.takeOff(this);    
                 
               // The push service is DISABLED BY DEFAULT...    
               PushManager.enablePush();    
                 
               // to make our receiver able to receive the broadcast intents...  
               PushManager.shared().setIntentReceiver(IntentReceiver.class);    
       }    
      }    
    

  16. One more thing to go, it's the manifest.xml:

  17.  <?xml version="1.0" encoding="utf-8"?>  
     <manifest xmlns:android="http://schemas.android.com/apk/res/android"  
        package="your.project.package"  
        android:versionCode="1"  
        android:versionName="1.0">  
       <uses-sdk android:minSdkVersion="7" />  
       
       <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />  
       <uses-permission android:name="android.permission.INTERNET" />  
       <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />  
       <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />  
          <uses-permission android:name="android.permission.READ_PHONE_STATE" />  
          <uses-permission android:name="android.permission.VIBRATE" />  
          <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />  
             
          <!-- REQUIRED for C2DM -->  
          <!-- Only this application can receive the messages and registration result -->  
          <permission android:name="your.project.package.permission.C2D_MESSAGE"  
               android:protectionLevel="signature" />  
       
          <uses-permission android:name="your.project.package.permission.C2D_MESSAGE" />  
       
          <!-- This app has permission to register and receive message -->  
          <uses-permission android:name="com.google.android.c2dm.permission.RECEIVE" />  
       
       <application android:label="@string/app_name"   
         android:icon="@drawable/launcher"  
               <!-- The Next line is very important -->  
         android:name="your.project.package.MyApplication" >  
           
         <activity android:name="your.project.package.MyActivity"  
                    android:label="@string/app_name" >  
              
           <intent-filter>  
             <action android:name="android.intent.action.MAIN" />  
             <category android:name="android.intent.category.LAUNCHER" />  
           </intent-filter>  
           
         </activity>  
           
         <!-- Urban Airshp -->  
           
              <!-- REQUIRED -->  
              <receiver android:name="com.urbanairship.CoreReceiver" />  
            
              <!-- REQUIRED for C2DM and Hybrid -->  
              <receiver android:name="com.urbanairship.push.c2dm.C2DMPushReceiver"  
                android:permission="com.google.android.c2dm.permission.SEND" >  
            
                <!-- Receive the actual message -->  
                <intent-filter >  
                  <action android:name="com.google.android.c2dm.intent.RECEIVE" />  
            
                  <category android:name="your.project.package" />  
                </intent-filter>  
                <!-- Receive the registration id -->  
                <intent-filter >  
                  <action android:name="com.google.android.c2dm.intent.REGISTRATION" />  
            
                  <category android:name="your.project.package" />  
                </intent-filter>  
              </receiver>  
            
              <service android:name="com.urbanairship.push.PushService" />  
                      
              <!-- OPTIONAL, if you want to receive push, push opened and registration completed intents   
                    Add the receiver that you've created -->  
              <receiver android:name="your.project.package.IntentReceiver" />  
       
         <!-- Urban Airshp -->  
           
       </application>  
       
     </manifest>  
    

  18. Congratulations, your app is ready to receive notifications. to test it, turn on the WiFi or your 3G and run the app, then get the APID from the Logcat.

  19. Open your app's profile on Urban Airship and choose push from the menu on the left side and then choose Test Push Notifications and of course choose Android.


  20. Once you fill in the fields and press Send it the notification will be sent to your device.

  21. You can install your app on many devices and send a broadcast message just by choosing Send Broadcast  from the menu on the left side. In this case you don't need to enter any specific APID because, Urban Airship API will send the notification to all the registered devices, and as I've mentioned earlier  tying an APID to your application happens automatically.
Notes:
  1. You can stop receiving notification by calling disablePush() as follows:

  2.  PushManager.disablePush();  
    

  3. When you test your app and decide to release it, create another profile for you app with Application Mode set to Production.

  4. It's highly recommended to read the Urban Airship's documentation as well.
That's it, don't hesitate to comment, to share your knowledge and to correct me.

Thursday, March 1, 2012

Objective-C: Hello World!

Hi,
This is the famous (Hello, World!) example using the Objective-C programming language.

 #import <Foundation/Foundation.h>

 int main(int arg, const char * argv[]){

      NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];

      NSLog(@"Hello, World!");

      [pool drain];
      return 0;
 }

Explanation:
  • #import:
It's used to include what's in a file (classes, functions, ...) into the program because, the program needs them for whatever it'll do.

The next paragraph is quoted from here.

The final directive we will look at is one you will probably have seen many times already in previous chapters. The #import directive allows you to import include files into your source files. We have done this many times when we have included the Foundation Framework headers in our example programs:

 #import <Foundation/Foundation.h>  

This particular import statement has the path encapsulated by < and > characters. This means that the specified header file is to be found relative to system includes directory (or any other include directories defined for the compilation). To import a header file relative to the directory containing the source file, the file name must enclosed in quotes:

 #import "includes/myincludes.h"  

The above example imports a header file named myincludes.h located in a sub-directory of the current directory called includes. This technique may also be used to specify an absolute path (i.e. one that is not relative to the current directory):

 #import "/Users/demo/objc/includes/myincludes.h"  

  • main:
It's a special function; it's where the program starts execution.
What's between the parentheses are arguments that are passed to the program through the command-line.

  • NSAutoreleasePool / drain:
They are related to the Memory management topic which is out of the scope of this post and they are auto-generated when using Xcode as the development environment.

  • NSLog:
It displays/logs its argument.
Don't forget the @ character before the double quotes.

  • return 0:
(return) ends/terminates the execution.
(0) is the exit status which indicates that the execution has ended normally.

That's it, don't hesitate to comment, to share your knowledge and to correct me.