viernes, 30 de mayo de 2014

Android: Add image to android gallery

public static void addImageToAndroidGallery(Context context, String path) {

ContentValues values = new ContentValues();

values.put(Images.Media.DATE_TAKEN, System.currentTimeMillis());

values.put(Images.Media.MIME_TYPE, "image/jpeg");

values.put(MediaStore.MediaColumns.DATA, path);

context.getContentResolver().insert(Images.Media.EXTERNAL_CONTENT_URI, values);

}

miércoles, 21 de mayo de 2014

Android: Execute code in the main thread


Handler mainHandler = new Handler(Looper.getMainLooper());

         mainHandler.post(new Runnable() {
    
    @Override
    public void run() {
     // TODO Auto-generated method stub
     
    }
   });

viernes, 16 de mayo de 2014

Android: ScreenDimensionsHelper class


apublic class ScreenDimensionsHelper {

 public static interface OnViewDimensionListener {

  public void onDimensionsObtained(int width, int height);
 }

 public static void getViewDimensions(final View view, final ScreenDimensionsHelper.OnViewDimensionListener callback) {

  view.getViewTreeObserver().addOnGlobalLayoutListener(new OnGlobalLayoutListener() {

   @Override
   public void onGlobalLayout() {

    if (callback != null) {

     callback.onDimensionsObtained(view.getWidth(), view.getHeight());
    }
    else {

     throw new IllegalArgumentException("No callback object was provided.");
    }
   }

   @SuppressWarnings("deprecation")
   @SuppressLint("NewApi")
   private void removeOnGlobalLayoutListener(final View view) {

    int currentAPIVersion = android.os.Build.VERSION.SDK_INT;

    if (currentAPIVersion >= android.os.Build.VERSION_CODES.JELLY_BEAN) {
     view.getViewTreeObserver().removeOnGlobalLayoutListener(this);
    }
    else {
     view.getViewTreeObserver().removeGlobalOnLayoutListener(this);
    }
   }
  });
 }

 public static DisplayMetrics getDisplayMetricsIncludingDecorations(Activity activity) {

  DisplayMetrics displayMetrics = new DisplayMetrics();

  if (isApiLevelBefore14()) {
   
   displayMetrics = getDisplayMetricsIncludingDecorationsBaseApi(activity);
  }
  else if (isApiLevelBetween14And17()) {
   
   displayMetrics = getDisplayMetricsIncludingDecorationsApi14(activity);
  }
  else {
   
   displayMetrics = getDisplayMetricsIncludingDecorationsApi17(activity);
  }
  
  return displayMetrics;
 }

 private static boolean isApiLevelBefore14() {
  
  return Build.VERSION.SDK_INT < android.os.Build.VERSION_CODES.ICE_CREAM_SANDWICH;
 }

 private static DisplayMetrics getDisplayMetricsIncludingDecorationsBaseApi(Activity activity) {

  return getDisplayMetricsWithoutDecorations(activity);
 }

 private static boolean isApiLevelBetween14And17() {

  return Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.ICE_CREAM_SANDWICH
    && Build.VERSION.SDK_INT < android.os.Build.VERSION_CODES.JELLY_BEAN_MR1;
 }

 private static DisplayMetrics getDisplayMetricsIncludingDecorationsApi14(Activity activity) {

  Display display = activity.getWindowManager().getDefaultDisplay();

  DisplayMetrics displayMetrics = new DisplayMetrics();

  try {
   displayMetrics.widthPixels = (Integer) Display.class.getMethod("getRawWidth").invoke(display);
   displayMetrics.heightPixels = (Integer) Display.class.getMethod("getRawHeight").invoke(display);
  }
  catch (Exception ignored) {
   // TODO
  }

  return displayMetrics;
 }

 private static DisplayMetrics getDisplayMetricsIncludingDecorationsApi17(Activity activity) {
  
  Display display = activity.getWindowManager().getDefaultDisplay();
  
  DisplayMetrics displayMetrics = new DisplayMetrics();
  
  try {
   Point realSize = new Point();
   Display.class.getMethod("getRealSize", Point.class).invoke(display, realSize);
   displayMetrics.widthPixels = realSize.x;
   displayMetrics.heightPixels = realSize.y;
  }
  catch (Exception ignored) {
   // TODO
  }
  
  return displayMetrics;
 }

 public static DisplayMetrics getDisplayMetricsWithoutDecorations(Activity activity) {

  Display display = activity.getWindowManager().getDefaultDisplay();
  DisplayMetrics displayMetrics = new DisplayMetrics();
  display.getMetrics(displayMetrics);

  return displayMetrics;
 }

 /**
  * The status bar height will be 0 if it is not being display, non 0 otherwise.
  *
  * @param activity
  * @return
  */
 public static int getStatusBarHeight(Activity activity) {

  Rect rectgle = new Rect();
  Window window = activity.getWindow();
  window.getDecorView().getWindowVisibleDisplayFrame(rectgle);

  return rectgle.top;
 }

 public static int getActionBarHeight(Context context) {

  int actionBarHeight;

  final TypedArray styledAttributes = context.getTheme().obtainStyledAttributes(new int[] { android.R.attr.actionBarSize });
  actionBarHeight = (int) styledAttributes.getDimension(0, 0);

  styledAttributes.recycle();

  return actionBarHeight;
 }

 @SuppressLint("NewApi")
 public static boolean isSameAspectRatioThanScreen(Activity activity, Size currentSize) {

  Point point = new Point();
  
  WindowManager windowManager = (WindowManager) activity.getSystemService(Context.WINDOW_SERVICE);
  windowManager.getDefaultDisplay().getSize(point);

  DisplayMetrics screenSize = getDisplayMetricsIncludingDecorations(activity);

  float screenAspectRatio = (float) screenSize.widthPixels / screenSize.heightPixels;
  float sizeAspectRatio = (float) currentSize.width / currentSize.height;

  return Math.abs(screenAspectRatio - sizeAspectRatio) <= 0.01;
 }

 /**
  * @Deprecated Use "getDisplayMetricsWithoutDecorations" or "getDisplayMetricsIncludingDecorations" instead.
  *
  */
 @Deprecated
 public static DisplayMetrics getScreenSize() {

  return Resources.getSystem().getDisplayMetrics();
 }
}

Android: Aspect ratio class

Aspect ratio class

public class AspectRatio {

private int width;
private int height;

public AspectRatio(int width, int height) {

Assert.assertTrue("Both width and height must be positive non-zero values.",
areWidthAndHeightGreaterThanZero(width, height));

this.width = width;
this.height = height;

reduceAspectRatio();
}

public int getWidth() {

return width;
}

public void setWidth(int width) {

this.width = width;

reduceAspectRatio();
}

private void reduceAspectRatio() {

int greatestCommonDivisor = MathHelper.greatestCommonDivisor(this.width, this.height);
this.width = this.width / greatestCommonDivisor;
this.height = this.height / greatestCommonDivisor;
}

public int getHeight() {

return height;
}

public void setHeight(int height) {

this.height = height;

reduceAspectRatio();
}

private boolean areWidthAndHeightGreaterThanZero(int width, int height) {

return (width > 0) && (height > 0);
}

@Override
public String toString() {

return String.format("%d:%d", width, height);
}

@Override
public boolean equals(Object other) {

   if (other == null) return false;
   if (other == this) return true;
   if (!(other instanceof AspectRatio)) return false;
   AspectRatio otherAspectRatio = (AspectRatio)other;
   // Further testing...
 
   return (otherAspectRatio.getWidth() == width) && (otherAspectRatio.getHeight() == height);
}

public static AspectRatio getDisplayAspectRatioWithoutDecorations(Activity activity) {

DisplayMetrics displayMetrics = ScreenDimensionsHelper.getDisplayMetricsWithoutDecorations(activity);

return new AspectRatio(displayMetrics.widthPixels, displayMetrics.heightPixels);
}

public static AspectRatio getDisplayAspectRatioIncludingDecorations(Activity activity) {

DisplayMetrics displayMetrics = ScreenDimensionsHelper.getDisplayMetricsIncludingDecorations(activity);

return new AspectRatio(displayMetrics.widthPixels, displayMetrics.heightPixels);
}
}

Aspect ratio tests

public class AspectRatioTest extends AndroidTestCase {

@SuppressWarnings("unused")
public void testZeroWithAspectRatio() {

try {

AspectRatio aspectRatio = new AspectRatio(0, 1);

assertTrue(false);
}
catch (AssertionFailedError e) {

assertTrue(true);
}
}

@SuppressWarnings("unused")
public void testZeroHeightAspectRatio() {

try {

AspectRatio aspectRatio = new AspectRatio(1, 0);

assertTrue(false);
}
catch (AssertionFailedError e) {

assertTrue(true);
}
}

public void test4x3AspectRatio() {

try {

AspectRatio aspectRatio = new AspectRatio(640, 480);

assertEquals(4, aspectRatio.getWidth());
assertEquals(3, aspectRatio.getHeight());
}
catch (AssertionFailedError e) {

assertTrue(false);
}
}

public void test16x9AspectRatio() {

try {

AspectRatio aspectRatio = new AspectRatio(1280, 720);

assertEquals(16, aspectRatio.getWidth());
assertEquals(9, aspectRatio.getHeight());
}
catch (AssertionFailedError e) {

assertTrue(false);
}
}

public void testEquivalentAspectRatios() {

try {

AspectRatio ar640x480 = new AspectRatio(640, 480);
AspectRatio ar1600x1200 = new AspectRatio(1600, 1200);

assertEquals(ar640x480, ar1600x1200);
}
catch (AssertionFailedError e) {

assertTrue(false);
}
}

public void testDifferentAspectRatios() {

try {

AspectRatio ar1024x800 = new AspectRatio(1024, 800);
AspectRatio ar1280x720 = new AspectRatio(1280, 720);

assertNotSame(ar1024x800, ar1280x720);
}
catch (AssertionFailedError e) {

assertTrue(false);
}
}

miércoles, 14 de mayo de 2014

Android: Better emulator

1) Download the software
2)
  1. Download the following ZIPs:
    • ARM Translation Installer v1.1 Hosted by FILETRIP(Mirrors) - If you have issues flashing ARM Trnaslation, Try re-downloading from a mirror
    • Download the correct GApps for your Android version (GApss you can find them here)
  2. Next Open your Genymotion VM and go to the Homescreen
  3. Now Drag&Drop the Genymotion-ARM-Translation.zip onto the Genymotion VM window.
  4. It should say "File transfer in progress", once it asks you to flash it click 'OK'
  5. Now Reboot your VM using ADB or an app like ROM Toolbox. If nescessary you can simply close the VM window, but I don't recommend it.
  6. Once you're on the Homescreen again Drag&Drop the gapps-jb-20130813-signed.zip(or whatever version you got) onto your VM, and click 'OK' when asked
  7. Once it finishes, again Reboot your VM and open the Google Play Store.
  8. Sign in using your Google account
  9. Once in the Store go to the 'My Apps' menu and let everything update(fixes a lot of issues), also try updating Google Play Services directly.
  10. Now try searching for 'Netflix' and 'Google Drive'
  11. If both apps show up in the results and you're able to Download/Install them, then congrats you now have ARM support and Google Play fully setup!

Note: To reboot the device safetly use adb. Adb is in the ...\sdk\platform-tools of your android sdk. To reboot a device open de cmd and type "adb reboot" if there is only one device or "device -s <device_name> reboot" if there is more than one. You can get the device_name with "adb devices".

martes, 13 de mayo de 2014

Android: Google maps marker

Google maps V2

// HUE is a float value ranged between 0 and 360
BitmapDescriptor defaultMarker = BitmapDescriptorFactory.defaultMarker(BitmapDescriptorFactory.HUE_ORANGE);

// Alternatively you could create if with any bitmap desired
     
mMap.addMarker(
 new MarkerOptions()
  .position(data.getLatLong())
  .draggable(false)
  .visible(true)
  .icon(defaultMarker)
  .title("Some title"));)
  .snippet(data.getMarkerDescription())
</pre>


mMap is a GoogleMap instance. You can get it through the getMap() method of the MapFragment. You retrieve the MapFragment as follows:

1) If you have a the map fragment inside an activity:
((SupportMapFragment) getSupportFragmentManager().findFragmentById(R.id.map)).getMap();

2) If you have a map fragment inside a fragment:
((SupportMapFragment) getChildFragmentManager().findFragmentById(R.id.map)).getMap();

Photoshop: Align element horizontally and vertically

1) Select everything (ctrl + A)
2) The options could be display on the top bar. If it doesn't do: "Layer > Align Layer to Selection > Vertical Centers" and "Layer > Align Layer to Selection > Horizontal Centers"

lunes, 12 de mayo de 2014

Android: Google maps

1) Make a copy and import to your workspace the google play services project:

E.g.:

C:\Program Files\Android SDK\adt-bundle-windows-x86_64-20130729\adt-bundle-windows-x86_64-20130729\sdk\extras\google\google_play_services\libproject\google-play-services_lib

1.1) Add the library project to your project.

2) Create the app in the google console

3) From the left menu select "API & AUTH" > "Credentials" > "Add credentials" > "API key" > "Android key"

(To copy the SHA from the cmd right click in the window's "navigation bar" select "edit > mark" and select the SHA. Right click again in the window's "navigation bar" select "edit > copy").

You should add the SHA-1 related to the debug.keystore (as well as the realease keystore, if available). You can find it by going to:

Window > Preferences > Android > Build (In Eclipse)

4) Go to "API & AUTH" > "APIs" in the left menu. Search for "Google Maps Android API" click on it and click on "Enable API".

5) In the AndroidManifest

<meta-data android:name="com.google.android.gms.version"
           android:value="@integer/google_play_services_version" />

<meta-data
            android:name="com.google.android.maps.v2.API_KEY"
            android:value="<Api key from google console obtained in previous step" />

<uses-feature android:glEsVersion="0x00020000" android:required="true"/>

IMPORTANT: In android studio 2 xmls where created one for debug (app/src/debug/res/values/google_maps_api) and for release (app/src/release /res/values/google_maps_api). In my case I created two keys, one for debug and one for release this is the only way I could make it work.

6) Test if everything is alright

Add the following code one activity;

<fragment xmlns:android="http://schemas.android.com/apk/res/android"
          android:id="@+id/map"
          android:layout_width="match_parent"
          android:layout_height="match_parent"
          android:name="com.google.android.gms.maps.MapFragment"/>

Android: Resizable ImageView (according to image preserving proportion, viewTreeObserver)

public class MyImageView extends ImageView {

private int imageWidth;
private int imageHeight;

public MyImageView(Context context) {

super(context);

addGlobalLayoutListener();
}

private void addGlobalLayoutListener() {

this.getViewTreeObserver().addOnGlobalLayoutListener(new OnGlobalLayoutListener() {

@Override
public void onGlobalLayout() {

resize(imageWidth, imageHeight);

removeOnGlobalLayoutListener(MyImageView.this);
}

@SuppressWarnings("deprecation")
@SuppressLint("NewApi")
private void removeOnGlobalLayoutListener(final View image) {

int currentAPIVersion = android.os.Build.VERSION.SDK_INT;

if (currentAPIVersion >= android.os.Build.VERSION_CODES.JELLY_BEAN) {
image.getViewTreeObserver().removeOnGlobalLayoutListener(this);
}
else {
image.getViewTreeObserver().removeGlobalOnLayoutListener(this);
}
}
});
}

public MyImageView(Context context, AttributeSet attrs) {

super(context, attrs);

addGlobalLayoutListener();
}

public MyImageView(Context context, AttributeSet attrs, int defStyle) {

super(context, attrs, defStyle);

addGlobalLayoutListener();
}

@Override
public void setImageBitmap(Bitmap bm) {

super.setImageBitmap(bm);

resize(bm.getWidth(), bm.getHeight());
}

@Override
public void setImageDrawable(Drawable drawable) {

super.setImageDrawable(drawable);
}

@Override
public void setImageResource(int resId) {

super.setImageResource(resId);
}

private void resize(int imageWidth, int imageHeight) {

int imageViewWidth = this.getWidth();

if ((imageViewWidth != 0) && (imageWidth != 0)) {

int newImageViewHeight = imageViewWidth * imageHeight / imageWidth;

this.setLayoutParams(new LayoutParams(imageViewWidth, newImageViewHeight));
}
else {

this.imageWidth = imageWidth;
this.imageHeight = imageHeight;
}
}

}

Android: Overriding equals

@Override
public boolean equals(Object other){
    if (other == null) return false;
    if (other == this) return true;
    if (!(other instanceof MyClass)) return false;
    MyClass otherMyClass = (MyClass)other;
    // Further testing...
}

jueves, 8 de mayo de 2014

Android: Transparent activity

You may want to create an activity that sits on top of another but you want the former to be invisible (for instance, you want to encapsulate something that involves a call to startActivityForResult, which was my case).

In my particular case I wanted to be able to pick an image from the Gallery or the Camera and after that I wanted to crop the obtained image and return the result to an activity.

Manifest

<activity
            android:name="package.activities.GetImageActivity"
            android:theme="@style/Theme.Transparent"
            android:configChanges="orientation"
            android:label="@string/title_activity_get_image"
            android:screenOrientation="portrait" >
        </activity>

styles

<style name="Theme.Transparent" parent="android:Theme">
        <item name="android:windowIsTranslucent">true</item>
        <item name="android:windowBackground">@android:color/transparent</item>
        <item name="android:windowContentOverlay">@null</item>
        <item name="android:windowNoTitle">true</item>
        <item name="android:windowIsFloating">true</item>
        <item name="android:backgroundDimEnabled">false</item>
    </style>

Class

public class GetImageActivity extends BaseActivity {

private static final int REQUEST_CODE_CROP_IMAGE = 300;
private Uri outputFileUri;

@Override
protected void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.activity_get_image);

ImagePicker imagePicker = new ImagePicker(this);
imagePicker.setOwnerActivity(this);
imagePicker.show();
}

@Override
public void onActivityResult(int requestCode, int resultCode, Intent imageReturnedIntent) {

super.onActivityResult(requestCode, resultCode, imageReturnedIntent);

switch (requestCode) {
case ImagePicker.SELECT_PHOTO_FROM_GALLERY:
cropGalleryImage(resultCode, imageReturnedIntent);
break;
case ImagePicker.SELECT_PHOTO_FROM_CAMERA:
cropCameraImage(resultCode);
break;

case REQUEST_CODE_CROP_IMAGE:
String path = imageReturnedIntent.getStringExtra(CropImage.IMAGE_PATH);

Intent intent = getIntent();
intent.putExtra(CropImage.IMAGE_PATH, path);
this.setResult(RESULT_OK, intent);

finish();

break;
}
}
}

Layout

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical" >
 

</LinearLayout>

miércoles, 7 de mayo de 2014

Android: Safe way to get view's width or height

1)
view.getViewTreeObserver().addOnGlobalLayoutListener(
    new OnGlobalLayoutListener() {

     @Override
     public void onGlobalLayout() {

      // Do something...

      // Remove the listener after you've done. Else it will
      // get called multiple times
      removeOnGlobalLayoutListener(image);
     }

     @SuppressWarnings("deprecation")
     @SuppressLint("NewApi")
     private void removeOnGlobalLayoutListener(
       final View view) {

      int currentAPIVersion = android.os.Build.VERSION.SDK_INT;

      if (currentAPIVersion >= android.os.Build.VERSION_CODES.JELLY_BEAN) {
       view.getViewTreeObserver()
         .removeOnGlobalLayoutListener(this);
      } else {
       view.getViewTreeObserver()
         .removeGlobalOnLayoutListener(this);
      }
     }
    });

2)
view.measure(View.MeasureSpec.UNSPECIFIED, View.MeasureSpec.UNSPECIFIED);

int measuredWidth = view.getMeasuredWidth();
int measuredHeight = view.getMeasuredHeight();

martes, 6 de mayo de 2014

Android and Eclipse: Gotchas

Gotchas and errors I stepped into:

1) Views disappear randomly

android:clipChildren="false" as an attribute in a LinearLayout produced unexpected problems such as views disappearing randomly and others showing up.

2) Eclipse hangs at "Android SDK Content Loader"

a - If you see that Eclipse hangs at "Android SDK Content Loader" try to execute it with <path_to_eclipse>/eclipse -clean (or add this parameter to the shortcut in the Desktop). If this doesn't work (which was my case) delete the "cache" folder at <path_to_user_folde>/.android as well as de "ddms.cfg" file in the same location.

b -

b.1) Go to <user>\workspace\.metadata\.plugins\org.eclipse.core.resources make a copy of .projects (just in case) and delete the file.

b.2) Run eclipse. Probably your workspace will be all messed up.

b.3) Close eclipse. Go to <user>\workspace\.metadata\.plugins\org.eclipse.core.resources and rename the copy to its original name (.projects). You might have to use the cmd to achieve that.

b.3.1) Open the cmd. And navigate to <user>\workspace\.metadata\.plugins\org.eclipse.core.resources and execute the following command 'ren ".projects - Copy" ".projects"' (I used ".projects - Copy because" is the name of the copy, if you named it differently type that name instead).

3) Eclipse hangs at "DDMS post create init"

If you see that Eclipse hangs at "DDMS post create init". Close eclipse. Check and remove, if present, the .lock file from your workspace .metadata folder. In my workspace I also had a medata with a .lock extenction, so I erased it too. If when you start Eclipse the adb is failing. Go to the terminal and execute "adb kill-server", "adb start-server" if you get the following message "ADB server didn't ACK
* failed to start daemon * " kill the adb process with the Windows Task Manager.

4) Working copy locked SVN

Open de command line and navigate to the folder where the problem is present and execute the following "svn cleanup"

5) aapt.exe has stopped working and R class is not getting built

5.1) I navigated to the project folder and executed the following command:
svn log -q -v -r<revision_number> <projet_url> > <file_path_for_output>

In the list of added files I realised that the I had 2 .pngs in the menu folder. Eclipse didn't show any warning nor error.

5.2) I moved the files and it was all good.

6) When trying to resize my view dynamically my view disappeares.

Check if this helps: http://letslearnmobile.blogspot.com.ar/2014/07/android-change-view-size-dynamically.html

7) Eclipse hangs on loading workbench

Go to eclipses root director and execute: eclipse.exe -clean -refresh

8) In an Activity I had a view pager with a fragment adapter when I "minimized" and "maximized" the app the content of the view pager disappeared

My view pager adapter was inheriting from "FragmentPagerAdapter" so I made it inherit from "FragmentStatePagerAdapter" and it solved my problem.