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.