jueves, 18 de diciembre de 2014

Android: Custom loading dialog

Creator method
public static Dialog buildLoadingDialog(Activity act, String title) {
  final Dialog dialog = new Dialog(act, android.R.style.Theme_Translucent);
  dialog.requestWindowFeature(Window.FEATURE_NO_TITLE);
  dialog.setCancelable(false);
  dialog.setContentView(R.layout.dialog_loading);
  
  WindowManager.LayoutParams lp = dialog.getWindow().getAttributes();
  lp.dimAmount = 0.6f;  
  dialog.getWindow().setAttributes(lp);  
  dialog.getWindow().addFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND);
  
  TextView titleText = (TextView) dialog.findViewById(R.id.dialog_loading_title);
  titleText.setText(title);
  
  ImageView loadingImage = (ImageView) dialog.findViewById(R.id.dialog_loading_image);
  AnimationDrawable loadAnimation = (AnimationDrawable)loadingImage.getDrawable();
  loadAnimation.start();
  
  return dialog;
 }
R.layout.dialog_loading
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_gravity="center"
    android:background="#7C000000"
    android:padding="30dp"
    android:orientation="vertical" >
    
    <ImageView
        android:id="@+id/dialog_loading_image"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:src="@drawable/loading"
        android:layout_gravity="center_horizontal"
        android:layout_marginBottom="5dp"
        android:layout_marginTop="10dp"
 />
    <TextView
        android:id="@+id/dialog_loading_title"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center_horizontal"
        android:textColor="@color/text_color_1"
        android:textSize="22sp" />
    
</LinearLayout>
res/drawable/loading
<animation-list xmlns:android="http://schemas.android.com/apk/res/android"
    android:oneshot="false">
    <item android:drawable="@drawable/loading_1" android:duration="100" />
    <item android:drawable="@drawable/loading_2" android:duration="100" />
    <item android:drawable="@drawable/loading_3" android:duration="100" />
    <item android:drawable="@drawable/loading_4" android:duration="100" />
    <item android:drawable="@drawable/loading_5" android:duration="100" />
    <item android:drawable="@drawable/loading_6" android:duration="100" />
    <item android:drawable="@drawable/loading_7" android:duration="100" />
    <item android:drawable="@drawable/loading_8" android:duration="100" />
    <item android:drawable="@drawable/loading_9" android:duration="100" />
    <item android:drawable="@drawable/loading_10" android:duration="100" />
    <item android:drawable="@drawable/loading_11" android:duration="100" />
</animation-list>
How to use it
    private void showLoadingDialog() {
        loadingDialog = DialogHelper.buildLoadingDialog(this, "Cargando...");
        loadingDialog.show();
    }

    private void hideLoadingDialog() {
        loadingDialog.dismiss();
        loadingDialog = null;
    }

martes, 16 de diciembre de 2014

Android: Filter logcat output from eclipse

In the "EditView" on top of the logcat output type:

tag:^(?!<tag 1>|<tag 2>| ... |<tag n>).*$

Example:

tag:^(?!Adreno200-ES20|memalloc).*$

miércoles, 26 de noviembre de 2014

Android: Text sizes (TextAppearance)


<style name="TextAppearance.Large">
    <item name="android:textSize">22sp</item>
</style>

<style name="TextAppearance.Medium">
    <item name="android:textSize">18sp</item>
</style>

<style name="TextAppearance.Small">
    <item name="android:textSize">14sp</item>
    <item name="android:textColor">?textColorSecondary</item>
</style>

Android: Android 5

If you set the build target of your project to 21 (Android 5) your application might stop compiling that is because Android 5 uses Java 1.7 whereas the rest uses 1.6.

martes, 25 de noviembre de 2014

Android: Menu

On a fragment (menu from scratch)
public class MyFragment extends Fragment {
    
@Override
 public void onCreate(Bundle savedInstanceState) {

     super.onCreate(savedInstanceState);
        
     setHasOptionsMenu(true);
}
...

@Override
    public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
        
        inflater.inflate(R.menu.menu_main, menu);

        super.onCreateOptionsMenu(menu, inflater);
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        switch (item.getItemId()) {
            // Handle the clicked menu option here
        }
    }
On an Activity (menu from scratch)
@Override
    public boolean onCreateOptionsMenu(Menu menu) {
        
        this.getMenuInflater().inflate(R.menu.menu_main, menu);

        return true;
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        switch (item.getItemId()) {
            // Handle the clicked menu option here

        }
    }

lunes, 17 de noviembre de 2014

Skype: Account is logged permanently

When you notice you never log out from your account, try the following:

In any chat window type:  /showplaces

This command will display all the devices where you are logged in.

If you type: /remotelogout

It will log you out from all those devices but the current one.

jueves, 13 de noviembre de 2014

miércoles, 12 de noviembre de 2014

Android: Gotcha ListView

1) If you have a focusable view in your Layout of your list item the onItemClickListener won't get called. You can fix this issue by adding a "android:descendantFocusability="blocksDescendants"" to your list item layout container.

jueves, 6 de noviembre de 2014

Android: Activity orientation

Create a base class from which your activities will inherit
public class AbstractActivity extends Activity {
 
 ...
 
 @Override
 protected void onCreate(Bundle arg0) {

     super.onCreate(arg0);
     
     setOrientationAccordingToDevice();
 }

 ...

 protected void setOrientationAccordingToDevice() {

        if (ScreenHelper.isDeviceATablet(this)) {
            // Since it is a tablet it will have an api > 8. So this is safe.
            this.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE);
        }
        else {
            // How do I implement the SCREEN_ORIENTATION_SENSOR_PORTRAIT functionality previous to api 9?
            this.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
        }
    }
}
ScreenHelper
public class ScreenHelper {

 public static boolean isDeviceATablet(Activity activity) {

  DisplayMetrics metrics = new DisplayMetrics();
  activity.getWindowManager().getDefaultDisplay().getMetrics(metrics);

  int widthPixels = metrics.widthPixels;
  int heightPixels = metrics.heightPixels;

  float scaleFactor = metrics.density;

  float widthDp = widthPixels / scaleFactor;
  float heightDp = heightPixels / scaleFactor;

  float smallestWidth = Math.min(widthDp, heightDp);

  return (smallestWidth >= 600);
 }
 
 public static void hideSoftKeyboard(Activity activity, View view) {

  InputMethodManager imm = (InputMethodManager) activity.getSystemService(Activity.INPUT_METHOD_SERVICE);
  imm.hideSoftInputFromWindow(view.getWindowToken(), 0);
 }
}

martes, 28 de octubre de 2014

Android: Expandable list, expand all groups.

Set the group on click listener:
list.setOnGroupClickListener(new ExpandableListView.OnGroupClickListener()
        {
            public boolean onGroupClick(ExpandableListView arg0, View itemView, int itemPosition,
                    long itemId)
            {
                return true;
            }
        });
When created you need to expand all groups like so:
private void expandGroups() {
        for (int i = 0; i < listAdapter.getGroupCount(); i++) {
            listView.expandGroup(i);
        }
    }

miércoles, 22 de octubre de 2014

Android: Databases: Handling databases

Create SQLite database in app
public class DatabaseHelper extends SQLiteOpenHelper {


 private static final String DATABASE_NAME = "DATABASE_NAME";

 private static final int SCHEMA_VERSION = 1;


 ...


 private DatabaseHelper() {


  super(ReportTvApplication.getContext(), DATABASE_NAME, null, SCHEMA_VERSION);

 }

}

Pre-existing SQLite database file from the "assets" folder.

1. Preparing the SQLite database file.

Open your database and add a new table called "android_metadata":

CREATE TABLE "android_metadata" ("locale" TEXT DEFAULT 'en_US');

INSERT INTO "android_metadata" VALUES ('en_US');

Then, it is necessary to rename the primary id field of your tables to "_id" (or add a primary key field called "_id") so Android will know where to bind the id field of your tables.

2. Copying, opening and accessing your database in your Android application.

Now just put your database file in the "assets" folder of your project and create a Database Helper class by extending the SQLiteOpenHelper class from the "android.database.sqlite" package.

Make your DataBaseHelper class look like this:
public class DatabaseHelper extends SQLiteOpenHelper {


    private static final String DATABASE_NAME = "database.sql";
    private SQLiteDatabase myDataBase;
    private final Context myContext;


    public DatabaseHelper(Context context) {

        super(context, DATABASE_NAME, null, 1);

        this.myContext = context;
    }


    public void createDataBase() throws IOException {

        boolean dbExist = databaseExists();

        if (dbExist) {
            // do nothing - database already exist

        } else {

            this.getReadableDatabase();

            try {
                copyDataBase();

            } catch (IOException e) {
                throw new Error("Error copying database");
            }
        }
    }


    private boolean databaseExists() {

        SQLiteDatabase checkDB = null;

        try {
            final String myPath = getDatabasePath();

            checkDB = SQLiteDatabase.openDatabase(myPath, null, SQLiteDatabase.OPEN_READWRITE);
        } catch (SQLiteException e) {
            // database does't exist yet.
        }

        if (checkDB != null) {
            checkDB.close();
        }

        return checkDB != null ? true : false;
    }

    private String getDatabasePath() {

        final String myPath = myContext.getDatabasePath(DatabaseHelper.DATABASE_NAME)
                .getAbsolutePath();

        return myPath;
    }

    private void copyDataBase() throws IOException {

        // Open your local db as the input stream
        InputStream myInput = myContext.getAssets().open(DATABASE_NAME);

        // Open the empty db as the output stream
        OutputStream myOutput = new FileOutputStream(getDatabasePath());

        // transfer bytes from the inputfile to the outputfile
        byte[] buffer = new byte[1024];

        int length;

        while ((length = myInput.read(buffer)) > 0) {
            myOutput.write(buffer, 0, length);
        }

        // Close the streams
        myOutput.flush();
        myOutput.close();
        myInput.close();
    }

    public void openDataBase() throws SQLException {


        // Open the database

        myDataBase = SQLiteDatabase.openDatabase(getDatabasePath(), null,

                SQLiteDatabase.OPEN_READWRITE);


    }


    @Override

    public synchronized void close() {


        if (myDataBase != null)

            myDataBase.close();


        super.close();


    }


    @Override

    public void onCreate(SQLiteDatabase db) {


    }


    @Override

    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {


    }


        // Add your public helper methods to access and get content from the database.

       // You could return cursors by doing "return myDataBase.query(....)" so it'd be easy

       // to you to create adapters for your views.


}

Example query with distinct
public List<String> getSimulations() {


  String[] projection = new String[] { SIMULATION_ID };


// The first boolean corresponds to distinct

  Cursor cursor = getReadableDatabase().query(true, TABLE, projection, null, null, null, null, null, null);


  return getSimulationsFromCursor(cursor);


 }


private List<String> getSimulationsFromCursor(Cursor cursor) {


  List<String> simulations = new ArrayList<String>();


  // This is the safe way to check if the cursor has content

  if (cursor == null || !cursor.moveToFirst()) {

   return null;

  }


  // For whatever reason cursor.isLast() wasn't working for me, I could go past the last

  for (int i = 0; i < cursor.getCount(); i++, cursor.moveToNext()) {


  // The index belongs to the index of the field in the projection string

   simulations .add(cursor.getString(0));

  }


  return scenarios;

 }

Android: Testing

1) Test

   1.1) Create a new source folder called "Test"
   1.2) Create a subclass that either extends either "TestCase" or "AndroidTestCase"*
   1.3) All method should be named like "public void testX()" where "X" is whatever you want.
   1.4) In the manifest you need to add:
 
<instrumentation
        android:name="android.test.InstrumentationTestRunner"
        android:targetPackage="<application_package" >
    </instrumentation>

If you are going to user "AndroidTestCase" you will also have to add:

<uses-library android:name="android.test.runner" />

*AndroidTestCase is useful when you need to test something that uses the context.

   1.5) Create a class that extends TestSuite as follows:

import android.test.suitebuilder.TestSuiteBuilder;
import junit.framework.Test;
import junit.framework.TestSuite;

public class AllTests extends TestSuite {
    public static Test suite() {
        return new TestSuiteBuilder(AllTests.class).includeAllPackagesUnderHere().build();
    }
}

viernes, 10 de octubre de 2014

Android: Tabs

1) When you create tabs the pager will create (at least), te current and the next. It is in times like this you may need to user "getUserVisibleHint()" method from the fragment which tells you whether the fragment is visible or not.

viernes, 19 de septiembre de 2014

Android: Drop down menu

I had a custom action bar, the center button was the button that opened a menu under it like a drop down menu (if not exactly like one).

AbstractActivity
public abstract class AbstractActivity extends ActionBarActivity {

@Override
 protected void onCreate(Bundle savedInstanceState) {

  this.getSupportActionBar().setDisplayHomeAsUpEnabled(false);
  this.getSupportActionBar().setDisplayShowHomeEnabled(false);
  this.getSupportActionBar().setDisplayUseLogoEnabled(false);
  this.getSupportActionBar().setDisplayShowCustomEnabled(true);
  this.getSupportActionBar().setCustomView(R.layout.action_bar_view);
  this.getSupportActionBar().setBackgroundDrawable(
    getResources().getDrawable(R.drawable.background_test));

  View customView = getSupportActionBar().getCustomView();
  View mainMenu = customView.findViewById(R.id.main_menu);
  mainMenu.setOnClickListener(new OnClickListener() {

   @Override
   public void onClick(View v) {

    getMainMenu().animateToggle();
   }
  });

  setTitle(this.getTitle());
 }
}

action_bar_view
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:background="@android:color/white"
    android:gravity="center"
    android:orientation="horizontal" >

    <com.provengroup.view.MainMenuButtonView 
        android:id="@+id/main_menu"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content" />

</LinearLayout>

MainMenuButtonView
public class MainMenuButtonView extends LinearLayout {

    private ImageView logoImage;
    private Animation animationRight;
    private Animation animationLeft;
    boolean opened = false;

    public MainMenuButtonView(Context context) {
        this(context, null);
    }

    public MainMenuButtonView(Context context, AttributeSet attrs) {
        super(context, attrs);

        LayoutInflater inflater = (LayoutInflater) context
                .getSystemService(Context.LAYOUT_INFLATER_SERVICE);
        inflater.inflate(R.layout.main_menu_button_view, this, true);

        logoImage = (ImageView) findViewById(R.id.logo_image);

        animationRight = AnimationUtils.loadAnimation(context, R.anim.rotate_right);
        animationRight.setFillAfter(true);

        animationLeft = AnimationUtils.loadAnimation(context, R.anim.rotate_left);
        animationLeft.setFillAfter(true);
    }

    public Bitmap getResizedImage() {

        Bitmap image = BitmapFactory.decodeResource(getResources(),
                R.drawable.checkpos_logo_image);

        int height = getHeight();

        if (height != 0 && height != image.getWidth()) {

            int width = height * image.getWidth() / image.getHeight();

            Bitmap scaledImage = Bitmap.createScaledBitmap(image, width,
                    height, false);

            image.recycle();

            return scaledImage;

        } else {

            return image;
        }
    }

    @Override
    public void setOnClickListener(final OnClickListener l) {

        super.setOnClickListener(new OnClickListener() {

            @Override
            public void onClick(final View v) {

                if (opened) {
                    logoImage.startAnimation(animationLeft);
                    
                    opened = false;

                } else {
                    logoImage.startAnimation(animationRight);
                    
                    opened = true;
                }

                l.onClick(v);
            }
        });
    }

    public void restore() {
        
        if (opened) {
            
            logoImage.startAnimation(animationLeft);
            
            opened = false;
        }
    }

}

main_menu_button_view
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/main_menu"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:background="@android:color/white"
    android:gravity="center"
    android:orientation="horizontal" >

    <ImageView
        android:id="@+id/logo_image"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:src="@drawable/checkpos_logo_image" />

    <ImageView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginLeft="10dp"
        android:src="@drawable/checkpos_logo_text" />

</LinearLayout>

rotate_right
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
    <rotate 
        android:duration="300"
        android:pivotX="50%"
        android:pivotY="50%"
        android:fromDegrees="0"
        android:toDegrees="180"/>
</set>

rotate_left
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
    <rotate 
        android:duration="300"
        android:pivotX="50%"
        android:pivotY="50%"
        android:fromDegrees="180"
        android:toDegrees="0"/>
</set>

miércoles, 17 de septiembre de 2014

Android: Device driver issues

1) Run the SDK Manager.
2) Select "Android SDK Platform-tools" under "Tools" (if needed) and "Google USB Driver" under "Extras" (if needed)
3) Right click on "Computer" and select "Manage". Click on "Device manager". Alternatively click on Windows home button and search for "Device manager" and run it.
4) Expand the "Portable devices" tab. Right click on your device and then select the following "Update driver software" > "Browse my computer for driver software" > "Let me pick from a list of device driver on my computer" > "Have disk.." >  "Browse" and go to your root android path "<android_sdk_path>/extras/google/usb_driver/android_winusb.inf" click "Next" and "Yes" to all following dialogs and you will be done.

For a more in depth explanation . Thanks  you are the man!

lunes, 25 de agosto de 2014

Android: Intent for showing turn-by-turn (how to get to location)


 private void showRouteToLocation(int position) {

        PointOfSaleModel pointOfSale = clientsAdapter.getItem(position);

        String format = "http://maps.google.com/maps?saddr=%s,%s&daddr=%s,%s";

        UserLocation userLocation = GPSHelper.getInstance().getLastKnownLocation(getActivity());

        String userLatitude = formattedDouble(userLocation.getLat());
        String userLongitude = formattedDouble(userLocation.getLon());

        String destinationLatitude = formattedDouble(pointOfSale.getLatitud());
        String destinationLongitud = formattedDouble(pointOfSale.getLongitud());

        String request = String.format(format, userLatitude, userLongitude, destinationLatitude,
                destinationLongitud);

        Intent intent = new Intent(android.content.Intent.ACTION_VIEW, Uri.parse(request));
        startActivity(intent);
    }

jueves, 21 de agosto de 2014

Android: Extending linear layout

Layout
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    xmlns:custom="http://schemas.android.com/apk/res/<app package>"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    tools:context=".MainActivity" >

        <com.vogella.android.view.compoundview.ColorOptionsView
            android:id="@+id/view1"
            android:layout_width="match_parent"
            android:layout_height="?android:attr/listPreferredItemHeight"
            android:background="?android:selectableItemBackground"
            android:onClick="onClicked"
            custom:titleText="Background color"
            custom:valueColor="@android:color/holo_green_light"
             />

        ...


</LinearLayout>

Class
public class ColorOptionsView extends LinearLayout {

  private View mValue;
  private ImageView mImage;

  public ColorOptionsView(Context context, AttributeSet attrs) {
    super(context, attrs);

    TypedArray a = context.obtainStyledAttributes(attrs,
        R.styleable.ColorOptionsView, 0, 0);
    String titleText = a.getString(R.styleable.ColorOptionsView_titleText);
    int valueColor = a.getColor(R.styleable.ColorOptionsView_valueColor,
        android.R.color.holo_blue_light);
    a.recycle();

    LayoutInflater inflater = (LayoutInflater) context
        .getSystemService(Context.LAYOUT_INFLATER_SERVICE);
    inflater.inflate(R.layout.view_color_options, this, true);

    TextView title = (TextView) getChildAt(0);
    title.setText(titleText);

    mValue = getChildAt(1);
    mValue.setBackgroundColor(valueColor);

    mImage = (ImageView) getChildAt(2);
  }

  public ColorOptionsView(Context context) {
    this(context, null);
  }

  public void setValueColor(int color) {
    mValue.setBackgroundColor(color);
  }

  public void setImageVisible(boolean visible) {
    mImage.setVisibility(visible ? View.VISIBLE : View.GONE);
  }

}

view_color_options.xml
<?xml version="1.0" encoding="utf-8"?>
<merge xmlns:android="http://schemas.android.com/apk/res/android" >

    <TextView
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_weight="1"
        android:layout_centerVertical="true"
      android:layout_marginLeft="16dp"
        android:textSize="18sp"
        />

  <View
      android:layout_width="26dp"
      android:layout_height="26dp"
      android:layout_centerVertical="true"
      android:layout_marginLeft="16dp"
      android:layout_marginRight="16dp"
      />

    <ImageView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
      android:layout_marginRight="16dp"
        android:layout_centerVertical="true"
        android:visibility="gone"
        />
   
</merge>

attrs.xml
<?xml version="1.0" encoding="utf-8"?>
<resources>
    <declare-styleable name="ColorOptionsView">
        <attr name="titleText" format="string" localization="suggested" />
        <attr name="valueColor" format="color" />
    </declare-styleable>

</resources>

viernes, 15 de agosto de 2014

Android: Action on keyboard enter button

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

    <LinearLayout
        android:layout_width="250dp"
        android:layout_height="match_parent"
        android:layout_gravity="center"
        android:gravity="center"
        android:orientation="vertical" >

        <EditText
            android:id="@+id/username"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:imeOptions="actionNext"
            android:hint="@string/username"
            android:singleLine="true" />

        <EditText
            android:id="@+id/pasword"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:hint="@string/password"
            android:imeOptions="actionGo"
            android:inputType="textPassword"
            android:singleLine="true" />

        <Button
            android:id="@+id/login_button"
            android:layout_width="match_parent"
            android:layout_height="40dp"
            android:layout_marginTop="30dp"
            android:text="@string/login" />
    </LinearLayout>

</LinearLayout>

Activity
@Override
public void onCreate(Bundle savedInstanceState) {
  
 super.onCreate(savedInstanceState);
  
 setContentView(R.layout.activity_login);
  
 EditText password = (EditText) findViewById(R.id.editPass);
 password.setOnKeyListener(new OnKeyListener() {

  public boolean onKey(View view, int keyCode, KeyEvent event) {

   if (event != null && event.getAction() == KeyEvent.ACTION_DOWN && keyCode == KeyEvent.KEYCODE_ENTER) {
      
    doLogin();
    return true;
   } else {
    return false;
   }
  }
 });

 Button loginBtn = (Button) findViewById(R.id.btnLogin);
 loginBtn.setOnClickListener(this);
}

//...
@Override
public void onClick(View v) {

 if (v.getId() == R.id.btnLogin) {
  doLogin();
 }
}

miércoles, 13 de agosto de 2014

Android: Network task

NetworkTask

package ar.com.fennoma.asi.tasks;

import android.content.Context;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
import android.os.AsyncTask;
import ar.com.fennoma.asi.R;

/**
 * The downside of this class is that you have to override non-traditional
 * methods rather than overriding: onPreExecute, onPostExecute, and
 * doInBackground.
 */
public abstract class NetworkTask<Params, Progress, Result> extends
        AsyncTask<Params, Progress, TaskResult> {

    public interface ICallBack<Result> {

        public void onError(Result result);

        public void onSuccessful(Result result);
    }

    private ICallBack<TaskResult> callback;
    protected Context context;

    public NetworkTask(Context context, ICallBack<TaskResult> callback) {

        this.context = context;
        this.callback = callback;
    }

    // We won't let extending classes to override this method to prevent them
    // from not calling the callback
    @Override
    protected final void onPostExecute(TaskResult response) {

        if (callback != null) {

            if (response.error) {

                callback.onError(response);

            } else {

                callback.onSuccessful(response);
            }
        }

        onPostProcessing(response);
    }

    protected void onPostProcessing(TaskResult response) {
    }

    // We won't let extending classes to override this method to prevent them
    // from skipping the check
    @Override
    protected final TaskResult doInBackground(final Params... params) {

        try {

            if (context == null) {

                throw new IllegalArgumentException("Context cannot be null");
            }

            if (deviceHasAnActiveConnection()) {

                return this.doOnActiveConnection(params);

            } else {

                doOnNoActiveConnection(params);

                return createNoConnectionResult();
            }

        } catch (final Exception e) {

            doOnException(e, params);

            return createExceptionResult(e);
        }
    }

    private TaskResult createNoConnectionResult() {

        TaskResult taskResult = new TaskResult();
        taskResult.error = true;
        taskResult.exception = false;
        taskResult.message = context.getString(R.string.no_connection);

        return taskResult;
    }

    private TaskResult createExceptionResult(Exception e) {

        TaskResult taskResult = new TaskResult();
        taskResult.error = true;
        taskResult.exception = true;
        taskResult.message = context.getString(R.string.exception);

        return taskResult;
    }

    private boolean deviceHasAnActiveConnection() {

        ConnectivityManager systemService = (ConnectivityManager) context
                .getSystemService(Context.CONNECTIVITY_SERVICE);

        NetworkInfo activeNetworkInfo = systemService.getActiveNetworkInfo();

        return (activeNetworkInfo != null) && activeNetworkInfo.isConnected()
                && activeNetworkInfo.isAvailable();
    }

    protected abstract TaskResult doOnActiveConnection(Params... params) throws Exception;

    // Hook methods
    /**
     * The callback will be called on "onPostExecute" so there is no need to do
     * it in here.
     * 
     * @param params
     */
    protected void doOnNoActiveConnection(Params... params) {

    }

    /**
     * The callback will be called on "onPostExecute" so there is no need to do
     * it in here.
     * 
     * @param params
     */
    protected void doOnException(Exception e, Params... params) {

    }
}

You will need to add the following permission to the manifest:

<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>

Android: Base activity

Base activity

public class BaseActivity extends FragmentActivity{

 private Dialog loadingDialog;

 protected List<AsyncTask> tasks;
 protected List<Toast> toasts;

 @Override
 protected void onCreate(Bundle savedInstanceState) {

  super.onCreate(savedInstanceState);

  tasks = new LinkedList<AsyncTask>();
  toasts = new LinkedList<Toast>();
}

 @Override
 protected void onPause() {

  super.onPause();

  isVisible = false;

  this.hideLoadingDialog();

  this.cancelAllTasks();

  this.cancelAllToasts();
 }

 private void cancelAllToasts() {

  for (Toast currentToast : toasts) {
   currentToast.cancel();
  }

  toasts.clear();
 }

 public void addTask(AsyncTask task) {

  tasks.add(task);
 }

 private void cancelAllTasks() {

  for (AsyncTask currentTask : tasks) {

   if (currentTask != null && currentTask.getStatus() != Status.FINISHED) {
    currentTask.cancel(true);
   }
  }

  tasks.clear();
 }

 @Override
 public void showLoadingDialog() {

  if (loadingDialog == null) {

  // Probably should have a reference counter here
   loadingDialog = DialogHelper.buildLoadingDialog(this, this.getString(R.string.loading_dialog_loading_message));

   loadingDialog.show();
  }
 }

 @Override
 public void hideLoadingDialog() {

  if (loadingDialog != null) {

  // And here we decrement that counter and dismiss the dialog if it reaches 0
   loadingDialog.dismiss();
   loadingDialog = null;
  }
 }
 
 @Override
 public void onDestroy() {

  super.onDestroy();

  MemoryManagementHelper.unbindDrawables(((ViewGroup) findViewById(android.R.id.content)).getChildAt(0));
  System.gc();
 }

}
MemoryManagementHelper

public class MemoryManagementHelper {

 public static void unbindDrawables(View view) {
  if (view != null) {
         if (view.getBackground() != null) {
          view.getBackground().setCallback(null);
         }
         
         if (view instanceof ViewGroup && !(view instanceof AdapterView)) {
             for (int i = 0; i < ((ViewGroup) view).getChildCount(); i++) {
              unbindDrawables(((ViewGroup) view).getChildAt(i));
             }
             ((ViewGroup) view).removeAllViews();
         }
  } else {
   // TODO - ?
  }
    }
}

lunes, 11 de agosto de 2014

Android: "Smart" enum class


private enum Key {

  TRANSPARENCY("transparecy"); 

  private String stringValue;

  Key(String stringValue) {

   this.stringValue = stringValue;
  }

  @Override
  public String toString() {

   return this.stringValue;
  }

  public static Key getEnum(String stringValue) {

   if (stringValue == null) {
    throw new IllegalArgumentException();
   }

   for (Key key : values()) {

    if (stringValue.equalsIgnoreCase(key.toString())) {
     return key;
    }
   }

   throw new IllegalArgumentException();
  }
 }

jueves, 7 de agosto de 2014

Android: startActivityForResult

In caller
Intent i = new Intent(this, SecondActivity.class);
startActivityForResult(i, 1);
In Activity that handles the intent
Intent returnIntent = new Intent();
returnIntent.putExtra("result",result);
setResult(RESULT_OK, returnIntent); // Or RESULT_CANCELED
finish()
In caller
protected void onActivityResult(int requestCode, int resultCode, Intent data) {

    if (requestCode == 1) {

        if(resultCode == RESULT_OK){
            String result=data.getStringExtra("result");

        } else if (resultCode == RESULT_CANCELED) {
            //Write your code if there's no result
        }
    }
}

miércoles, 6 de agosto de 2014

Android: Popup menu

 private PopupWindow initiatePopupWindow(View anchor) {

  try {

   LayoutInflater layoutInflater = (LayoutInflater) getApplicationContext()
     .getSystemService(Context.LAYOUT_INFLATER_SERVICE);
   View layout = layoutInflater.inflate(R.layout.test_row, null);

   layout.measure(View.MeasureSpec.UNSPECIFIED, View.MeasureSpec.UNSPECIFIED);
   mainMenu = new PopupWindow(layout, FrameLayout.LayoutParams.MATCH_PARENT,
     FrameLayout.LayoutParams.WRAP_CONTENT, true);
   Drawable background = getResources().getDrawable(
     android.R.drawable.editbox_dropdown_dark_frame);
   mainMenu.setBackgroundDrawable(background);
   mainMenu.showAsDropDown(anchor, 0, 0);

   setOnClickListeners(layout);

  } catch (Exception e) {
   e.printStackTrace();
  }
  return mainMenu;

 }

Android: Attributes application namespace in layout

xmlns:app="http://schemas.android.com/apk/res-auto"

martes, 5 de agosto de 2014

Photoshop: Batch

Create script

1) "Window" > "Actions".
2) At the bottom select the sheet like icon and press "Rec" (circle icon) and do whatever you need to do

Run script

3) "File" > "Automate" > "Batch" and select the configuration you want. If you want the same name and extention don't touch that part.

Android: Drawing

Draw dashed box

public class DashedBox extends View {

 private Paint paint;


 public DetectionRectangle(Context context) {
  
  super(context);
  
  createPaint();
 }

 private void createPaint() {
  
  paint = new Paint();
  paint.setColor(Color.WHITE);
  paint.setStyle(Style.STROKE);
  paint.setStrokeWidth(convertToPx(2));
  paint.setPathEffect(new DashPathEffect(new float[] { 20, 5 }, 0));
  
  setLayerType(View.LAYER_TYPE_SOFTWARE, null); // Mandatory else it won't draw the dash but a full line
 }
 
 private int convertToPx(int dp) {
     // Get the screen's density scale
     final float scale = getResources().getDisplayMetrics().density;
     // Convert the dps to pixels, based on density scale
     return (int) (dp * scale + 0.5f);
 }

 public DetectionRectangle(Context context, AttributeSet attrs) {
  
  super(context, attrs);
  
  createPaint();
 }

 public DetectionRectangle(Context context, AttributeSet attrs, int defStyleAttr) {
  
  super(context, attrs, defStyleAttr);
  
  createPaint();
 }

 
 @Override
 protected void onDraw(Canvas canvas) {
  
  super.onDraw(canvas);
  
  canvas.drawLine(0, 0, getWidth(), 0, paint); // Top line
  canvas.drawLine(0, 0, 0, getHeight(), paint); // Left line
  canvas.drawLine(0, getHeight(), getWidth(), getHeight(), paint); // Bot line
  canvas.drawLine(getWidth(), 0, getWidth(), getHeight(), paint); // Right line
 }
}

Android: DP to pixels


public int convertToPx(int dp) {
    // Get the screen's density scale
    final float scale = getResources().getDisplayMetrics().density;
    // Convert the dps to pixels, based on density scale
    return (int) (dp * scale + 0.5f);
}

viernes, 25 de julio de 2014

Android: Smooth scrolling tricks (ListView)


@Override
 public View getGroupView(int groupPosition, boolean isExpanded,
   View convertView, ViewGroup parent) {

  ViewHolder holder;

  if (convertView == null) {
   
   convertView = mInflater.inflate(R.layout.row, null);

   holder = createViewHolder(convertView);
   
   convertView.setTag(holder);

  } else {

   holder = (ViewHolder) convertView.getTag();
  }
  
  IModel model = getGroup(groupPosition);
  
  holder.text1.setText(model.getText1());
  holder.layout.setBackgroundColor(context.getBackgroundColor(model));
  
  return convertView;
 }


private ViewHolder createViewHolder(View convertView) {

  ViewHolder holder;
  holder = new ViewHolder();

  holder.text1 = (TextView) convertView.findViewById(R.id.row_text1);
  holder.layout = (LinearLayout) convertView.findViewById(R.id.layoutRow);

  return holder;
 }

Android: ImageView wrap resized image.


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

     @Override
     public void onGlobalLayout() {

      Bitmap image = getFragment().getWomanPictureBitmap(
        getWomanPictureView(), "fotito_normal");

      getWomanPictureView().setImageBitmap(image);

      removeOnGlobalLayoutListener(getWomanPictureView());
     }

     @SuppressWarnings("deprecation")
     @SuppressLint("NewApi")
     private void removeOnGlobalLayoutListener(
       final ImageView 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 Bitmap getWomanPictureBitmap(ImageView womanPictureView,
   String womanPicture) {

  int womanPictureResID = getActivity().getResources().getIdentifier(
    womanPicture, "drawable", getActivity().getPackageName());

  Bitmap image = BitmapFactory.decodeResource(getResources(),
    womanPictureResID);

  int width = womanPictureView.getWidth();

  if (width != 0 && width != image.getWidth()) {

   int height = width * image.getHeight() / image.getWidth();

   Bitmap scaledImage = Bitmap.createScaledBitmap(image, width,
     height, false);

   image.recycle();

   return scaledImage;

  } else {

   return image;
  }
 }

martes, 22 de julio de 2014

Android: Custom action bar

Using sherlock:

Manifest
<resources xmlns:android="http://schemas.android.com/apk/res/android">

    <style name="AppTheme" parent="Theme.Sherlock.Light.DarkActionBar">
</resources>

Activity
public class AbstractActivity extends SherlockActivity {
...
public BaseActivity(boolean hasMenuItemInActionBar) {
 super();
  
 this.hasMenuItemInActionBar = hasMenuItemInActionBar;
}

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);

 if (this.getSupportActionBar() != null) {
  this.getSupportActionBar().setDisplayHomeAsUpEnabled(false);
  this.getSupportActionBar().setDisplayShowHomeEnabled(false);
  this.getSupportActionBar().setDisplayUseLogoEnabled(false);
  this.getSupportActionBar().setDisplayShowCustomEnabled(true);
  this.getSupportActionBar().setCustomView(R.layout.actionbar_custom);
   
  setTitle(this.getTitle());
 }
}
...
}

Using appcompat


Manifest
<resources xmlns:android="http://schemas.android.com/apk/res/android">

    <style name="AppTheme" parent="@style/Theme.AppCompat.Light">
</resources>

Activity
public class AbstractActivity extends ActionBarActivity{
...
public BaseActivity(boolean hasMenuItemInActionBar) {
 super();
  
 this.hasMenuItemInActionBar = hasMenuItemInActionBar;
}

@Override
protected void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

 if (this.getSupportActionBar() != null) {
  this.getSupportActionBar().setDisplayHomeAsUpEnabled(false);
  this.getSupportActionBar().setDisplayShowHomeEnabled(false);
  this.getSupportActionBar().setDisplayUseLogoEnabled(false);
  this.getSupportActionBar().setDisplayShowCustomEnabled(true);
  this.getSupportActionBar().setCustomView(R.layout.actionbar_custom);
   
  setTitle(this.getTitle());
 }
}
...
}

Android: Spinner with hint text and "setError" like functionality


Adapter:

public class GenderAdapter extends BaseAdapter {

    private Context context;
    private String[] genderList;

    public GenderAdapter(Context context) {
        this.context = context;
        this.genderList = new String[3];
        this.genderList[0] = context.getString(R.string.gender); // Hint text
        this.genderList[1] = context.getString(R.string.male);
        this.genderList[2] = context.getString(R.string.female);
    }

    @Override
    public View getDropDownView(int position, View convertView, ViewGroup parent) {

        if (convertView == null) {
            convertView = LayoutInflater.from(context).inflate(R.layout.drop_down_row_light_gray, null);
        }

        TextView text = (TextView) convertView.findViewById(R.id.text);
        text.setText(getItem(position));

        Resources resources = context.getResources();
        text.setTextColor(resources.getColor(position == 0 ? R.color.gray : R.color.dark_gray)); // If it is the hint text (position == 0) set the textcolor to be a lighter color


        return convertView;
    }

    @Override
    public int getCount() {
        return genderList.length;
    }

    @Override
    public String getItem(int position) {
        return genderList[position];
    }

    @Override
    public long getItemId(int position) {
        return position;
    }

    @Override
    public View getView(int position, View convertView, ViewGroup parent) {

        if (convertView == null) {
            convertView = LayoutInflater.from(context).inflate(R.layout.closed_row_light_gray, null);
        }

        TextView text = (TextView) convertView.findViewById(R.id.text);
        text.setText(getItem(position));

        Resources resources = context.getResources();
        text.setTextColor(resources.getColor(position == 0 ? R.color.gray : R.color.dark_gray)); // If it is the hint text (position == 0) set the textcolor to be a lighter color

        return convertView;
    }
}
drop_down_row_light_gray:
<?xml version="1.0" encoding="utf-8"?>
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/text"
    style="@style/Base.TextAppearance.AppCompat.Medium"
    android:background="@color/light_gray"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:minHeight="48dp"
    android:paddingLeft="8dp"
    android:paddingRight="16dp"
    android:paddingTop="16dp"
    android:paddingBottom="16dp"
    android:ellipsize="end"
    android:textColor="@color/dark_gray"
    android:gravity="center"/>
closed_row_light_gray:
<?xml version="1.0" encoding="utf-8"?>
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/text"
    style="@style/Base.TextAppearance.AppCompat.Medium"
    android:background="@android:color/transparent"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:minHeight="48dp"
    android:paddingLeft="8dp"
    android:paddingRight="16dp"
    android:paddingTop="16dp"
    android:paddingBottom="16dp"
    android:textColor="@color/dark_gray"
    android:gravity="center"/>
Add "setError" to the spinner :
private void setSubmitButtonOnClickListener() {

        submitButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {

                if (isFormValid()) {
                    ...

                } else {
 

                    if (isAgeEmpty()) {
                        TextView textView = (TextView) ageSpinner.getSelectedView().findViewById(R.id.text); // "text" is the layout id of the TextView that displays the text
                        textView.setError(getString(R.string.age_is_required));
                    }

                    ...
                }
            }
        });
    }

Notes:
1) In my experience, when I used 9 patch as the spinne's background, the spinner's text dissapeared. It does not happen if I use a "normal" drawable or background color.
2) The "arrow thingy" to the right side of a spinner is actually a background (a 9 patch png). That being said, if you change the spinne background the arrow will dissapear.ng>

Android: White screen when coming back from activity B to activiy A

In the theme of the application or activity's theme add:

<style name="AppTheme" parent="@style/Theme.AppCompat">
       
        // ...
        <item name="android:windowIsTranslucent">true</item>
        <item name="android:windowFullscreen">true</item>
        <item name="android:background">@android:color/transparent</item>
        <item name="android:windowBackground">@android:color/transparent</item>
    </style>

jueves, 17 de julio de 2014

Android: Easy share


Uri uri = Uri.fromFile(new File(path));
       
Intent shareIntent = new Intent(Intent.ACTION_SEND);  
shareIntent.setType("image/*");  
shareIntent.putExtra(Intent.EXTRA_TEXT, "text"); 
shareIntent.putExtra(Intent.EXTRA_TITLE, "title"); 
shareIntent.putExtra(Intent.EXTRA_SUBJECT, "subject");
shareIntent.putExtra(Intent.EXTRA_STREAM, uri);
shareIntent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET); *
startActivity(Intent.createChooser(shareIntent, "Share via"));

Note: This can be used with facebook if the application is installed.


* If you leave out the flag, when returning to your app (from homescreen, from recents etc.), you would see the Activity of the share target (messaging/mailing/IM app) instead of yours.







Android: Update ADT to 23

1) Go to Help --> About Eclipse SDK --> Installation Details

2) Select from the list the following plugins:

- Android DDMS
- Android Development Tools
- Android Hierarchy Viewer
- Android Native Development Tools
- Android Traceview
- Tracer for OpenGL ES

Click uninstall and follow the steps

4) Go to Help > Install New Software > http://dl-ssl.google.com/android/eclipse/
5) Select all plugins, and follow the steps and you are done.

viernes, 4 de julio de 2014

Android: Hide keyboard

1) Hide keyboard

private void hideKeyboard() {

EditText calibationEditText = (EditText) findViewById(R.id.calibration);

InputMethodManager imm = (InputMethodManager) 
getContext().getSystemService(Context.INPUT_METHOD_SERVICE);
imm.hideSoftInputFromWindow(calibationEditText.getWindowToken(), 0);

calibationEditText.clearFocus();
}

2) I created a dialog with an EditText when I close the dialog the keyboard doesn't disappear, it only changed from input type number to normal.

AndroidManifest.xml

<activity
            android:name="ar.com.fennoma.digipad.activities.Activity"
            android:label="@string/app_name"
            android:windowSoftInputMode="stateAlwaysHidden" >
</activity>

Activity is the activity that creates the dialog.

Android: JNI

Compiling

To be able to compile JNI code you need to create a make file. You can fin examples of working make files in the samples here. I named the Vuforia Android samples since it was because of this library that I had to work with JNI :P

In windows you just have to type the following the commands promt:

cd <path_to_jni_files>
<path_to_android_ndk>\ndk-build


For example:

cd C:\Users\dev\Desktop\Repositories\MyProject\jni
C:\android-ndk-r9c\ndk-build


You can find the Android NDK here. You can find the oficial documentation about JNI here.

Loading libs

The compiled library needs to be placed inside "/libs" inside, most likely, the folder named "armeabi-v7a".

For example, to load the library named "libdetection_based_tracker.so" somewhere in your code you have to call the following method: "System.loadLibrary("detection_based_tracker");".

Create an object

Example: Instantiate a Float object

jclass floatClass = env->FindClass("java/lang/Float"); // 1

jmethodID floatConstructor = env->GetMethodID(floatClass, "<init>", "(F)V"); // 2

jobject float object = env->NewObject(floatClass, floatConstructor, 5.0); // 3

1) We first need to find the class will be working on, in this case, the float class. env is the environment pointer is of type (JNIEnv).
2) Get the constructor method.
<init> indicates its a constructor.
(F)V defines the constructor arguments (the characters inside the parentheses) and the return type (just after the parentheses). In this case the arguments is just a single float (F) and the return type is void (V). You can check all type signatures here.
3) Create a java object given the class (floatClass), the constructor (floatConstructor) and the arguments of the constructor is 5.0.

Calling methods from Android methods with JNI

jclass activityClass = env->GetObjectClass(obj);

jmethodID targetDetectedMethodID = env->GetMethodID(activityClass, "targetDetected", "([Ljava/lang/Object;)V");

if (targetDetectedMethodID == 0) {

   LOG("Function targetDetected() not found.");

   return;
}

env->CallVoidMethod(obj, targetDetectedMethodID, arguments); //1


1) Arguments can be any objects or a var_list

miércoles, 2 de julio de 2014

Android: Obtain image size from file

BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeResource(resources, resourceId, options);

// options.outHeight
// options.outWidth

Android: Multiple option share dialog

Intent shareIntent = new Intent(Intent.ACTION_SEND);
        shareIntent.setType("text/plain");
        shareIntent.putExtra(Intent.EXTRA_TEXT, "text");
        shareIntent.putExtra(Intent.EXTRA_TITLE, "title");
        shareIntent.putExtra(Intent.EXTRA_SUBJECT, "subject");
        startActivity(Intent.createChooser(shareIntent, "Share via"));