jueves, 27 de febrero de 2014

Android: Proper way of implementing a view pager that uses a position to fragment map

public class MyFragmentPagerAdapter extends FragmentStatePagerAdapter {

private ArrayList<SectionType> sections;
private SparseArray<Fragment> fragmentMap;

public MyFragmentPagerAdapter(FragmentManager fm) {

super(fm);

fragmentMap = new SparseArray<Fragment>();
...
}

@Override
public Object instantiateItem(ViewGroup container, int position) {

Fragment instantiateItem = 

(Fragment) super.instantiateItem(container, position);

fragmentMap.put(position, instantiateItem);

...

return instantiateItem;
}

private Fragment getNewFragmentByPosition(int i) {

// Create your fragment
}

@Override
public Fragment getItem(int i) {

Fragment fragment = fragmentMap.get(i);

if (fragment == null) {
fragment = getNewFragmentByPosition(i);
}

return fragment;
}

@Override
public void destroyItem(ViewGroup container, int position, 

Object object) {

super.destroyItem(container, position, object);

fragmentMap.remove(position);
}

...
}

Note that we save the fragment in the map in "instantiateItem" rather than in "getItem". This is intentional since if the activity is killed by Android and later recreated then "getItem" won't be called when the fragments are recreated, but "instantiateItem" will.

miércoles, 26 de febrero de 2014

Android: Validate email

public final static boolean isValidEmail(CharSequence candidateEmail) {

  boolean isValid = false;

  if (!TextUtils.isEmpty(candidateEmail)) {
  isValid = 

  android.util.Patterns.EMAIL_ADDRESS
  .matcher(candidateEmail).matches();
  }

  return isValid;
}

martes, 25 de febrero de 2014

Android: Simple broadcast (Notifications)

I now started using broadcast to "hear" when my user in session is null which shouldn't happen but I have worked with application where this did happen and this came handy.

You could use this very same approach when handling errors like no available connection.

Global Approach

The receiver
public class NullUserBroadcastReceiver extends BroadcastReceiver {

 @Override
 public void onReceive(Context context, Intent intent) {
  
  // Do something
 }
}

Session class

If I try to retrieve some value out of the session and find out that my user is null I just call this method.

private void nullUserHandler() {

 // Log if you want...

 Intent intent = new Intent(NullUserBroadcastReceiver.class.getName());

  // The action is the same as the one defined in the manifest
 intent.setAction("my.project.nulluser"); 

  Context context = Application.getContext();
 context.sendBroadcast(intent);
} 

Manifest


  



Note: Local broadcast should be used instead. Check

Local Approach

Activity
@Override
public void onResume() {

  super.onResume();

  LocalBroadcastManager.getInstance(this).registerReceiver(mMessageReceiver,
      new IntentFilter("my-event"));
}

private BroadcastReceiver mMessageReceiver = new BroadcastReceiver() {

  @Override
  public void onReceive(Context context, Intent intent) {

       String message = intent.getStringExtra("message");
    // ...
  }
};

@Override
protected void onPause() {

  LocalBroadcastManager.getInstance(this).unregisterReceiver(mMessageReceiver);
super.onPause();
} 

Some class
private void sendMessage() {

  Intent intent = new Intent("my-event");
  intent.putExtra("message", "data");
  LocalBroadcastManager.getInstance(this).sendBroadcast(intent);
} 

Android: Current method name and caller method name

StackTraceElement[] stack = Thread.currentThread().getStackTrace();
String getStackTraceName = stack[0].getMethodName();
String currentMethodName = stack[1].getMethodName();
String callerMethodName = stack[2].getMethodName();

jueves, 20 de febrero de 2014

Android: Setting up an alarm

Alarms can be set through the system sevice AlarmManager (context.getSystemService(Context.ALARM_SERVICE)). The problem with alarms is that once the device is shut down the alarms are erased this is why we need Android to give us time when the device has finished booting. To "ask" Android to call us after the device has finished booting we need to implement a BroadcastReceiver who receives the android.intent.action.BOOT_COMPLETED message.

In the following example I have a database with the alarms.

OnBootReceiver (receives android.intent.action.BOOT_COMPLETED messages and sets alarms)

public class OnBootReceiver extends BroadcastReceiver {

@Override
public void onReceive(Context context, Intent intent) {

DatabaseHelper helper = DatabaseHelper.getInstance();

Cursor cursor = helper.getAllAlarms();

cursor.moveToFirst();

while (!cursor.isAfterLast())  {

setAlarm(context, helper.getAlarmFromCursor(cursor));
 
   cursor.moveToNext();
}

helper.close();

}

public static void setAlarm(Context context, Alarm alarm) {

AlarmManager alarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
alarmManager.set(AlarmManager.RTC_WAKEUP, alarm.getTimeInMillis(), getPendingIntent(context, alarm.getId()));
}

public static void cancelAlarm(Context context, Alarm alarm) {

AlarmManager alarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
alarmManager.cancel(getPendingIntent(context, alarm));
}

private static PendingIntent getPendingIntent(Context context, Alarm alarm) {

Intent intent = new Intent(context, OnAlarmReceiver.class);
intent.putExtra(OnAlarmReceiver.ALARM_ID, alarm.getId());

return PendingIntent.getBroadcast(context, (int) alarmId, intent, 0);
}
}


Manifest
For boot receiver add
<receiver
            android:name="ar.com.fennoma.reporttv.alarm.OnBootReceiver"
            android:enabled="true" >
            <intent-filter>
                <action android:name="android.intent.action.BOOT_COMPLETED" />
            </intent-filter>
        </receiver>

OnAlarmReceiver (Receives the pendingIntent from OnBootReceiver)

public class OnAlarmReceiver extends BroadcastReceiver {

public static final String ALARM_ID = "ALARM_ID";

@Override
public void onReceive(Context context, Intent intent) {

if (isProcessAliveAndInForeground(context)) {
this.createAlertView(context, intent);
}
else {

this.createNotification(context, intent);
}
}

private void createAlertView(Context context, Intent intent) {

DatabaseHelper helper = DatabaseHelper.getInstance();

Notification notification = helper.getNotification(intent.getLongExtra(ALARM_ID, -1));

helper.deleteNotification((int) notification.getCalendaredProgramId());

helper.close();

Intent i = new Intent(context, MainActivity.class);
i.putExtra(MainActivity.SHOW_ALARM_NOTIFICATION, true);
i.putExtra(MainActivity.NOTIFICATION, notification);
        i.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
        context.startActivity(i);
}

private void createNotification(Context context, Intent intent) {

DatabaseHelper helper = DatabaseHelper.getInstance();

Notification notification = helper.getNotification(intent.getLongExtra(ALARM_ID, -1));

helper.deleteNotification((int) notification.getCalendaredProgramId());

SimpleDate programStartingDate = new SimpleDate(notification.getProgramStartingDateInMillis());

String time = SimpleDate.getPrettyHour(programStartingDate.getHours(), programStartingDate.getMinutes());

String message = String.format("%s empezará a las %s en el canal %s", notification.getProgramName(), time,
notification.getSignName());

NotificationCompat.Builder mBuilder = new NotificationCompat.Builder(context).setSmallIcon(R.drawable.notification_icon)
.setContentTitle("ReporTV").setContentText(message);

Intent resultIntent = new Intent(context, MainActivity.class);
resultIntent.putExtra(MainActivity.SHOW_ALARM_NOTIFICATION, true);
resultIntent.putExtra(MainActivity.NOTIFICATION, notification);

// The stack builder object will contain an artificial back stack for the started Activity.
// This ensures that navigating backward from the Activity leads out of your application to the Home screen.
TaskStackBuilder stackBuilder = TaskStackBuilder.create(context);
// Adds the back stack for the Intent (but not the Intent itself)
// stackBuilder.addParentStack(MainActivity.class);
// Adds the Intent that starts the Activity to the top of the stack
stackBuilder.addNextIntent(resultIntent);

PendingIntent resultPendingIntent = stackBuilder.getPendingIntent(0, PendingIntent.FLAG_UPDATE_CURRENT);
mBuilder.setContentIntent(resultPendingIntent);
NotificationManager mNotificationManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
// mId allows you to update the notification later on.
mNotificationManager.notify((int) notification.getId(), mBuilder.build());

helper.close();
}

private boolean isProcessAliveAndInForeground(Context context) {

return BaseActivity.isVisible();
}
}


Manifest
In this case simple add
<receiver android:name="ar.com.fennoma.reporttv.alarm.OnAlarmReceiver" />


Android: How to get the country the user is currently at

The following example grabs the position of the user  from either the GPS or other source

LocationManager locationManager = (LocationManager) activity.getSystemService(Context.LOCATION_SERVICE);

locationListener = new LocationListener() {

public void onLocationChanged(Location location) {

try {
Geocoder gcd = new Geocoder(activity, Locale.getDefault());
List<Address> addresses;

addresses =
gcd.getFromLocation(location.getLatitude(), location.getLongitude(), 1);

if (addresses.size() > 0) {

// String countryCode = addresses.get(0).getCountryCode();

// Do something with it

}
catch (IOException e) {

e.printStackTrace();
}
}

public void onStatusChanged(String provider, int status, Bundle extras) {

}

public void onProviderEnabled(String provider) {

}

public void onProviderDisabled(String provider) {

}
};

locationManager.requestLocationUpdates(
LocationManager.NETWORK_PROVIDER, 0, 0, locationListener);

locationManager.requestLocationUpdates(
LocationManager.GPS_PROVIDER, 0, 0, locationListener);

Android: Simple dialog

I'll create a dialog that will pop when the user is about to leave the application to confirm if that is what he/she wants.

AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setTitle(getString(R.string.atention));
builder.setNegativeButton(android.R.string.cancel, new OnClickListener() {
 
@Override
public void onClick(DialogInterface dialog, int which) {
// On "Cancel" clicked functionality
}
});
builder.setPositiveButton(android.R.string.ok, new OnClickListener() {
 
@Override
public void onClick(DialogInterface dialog, int which) {
 
// On "Accept" clicked functionality
}
});
builder.setMessage(getString(R.string.are_you_sure_you_want_to_quit));
 
AlertDialog alert = builder.create();
/* The dialog will be dismissed when the user touches outside of it */

alert.setCanceledOnTouchOutside(true);
alert.show();

If you wanted to prevent the dialog to be dismissed previous to some condition we would have to:
AlertDialog.Builder builder = new AlertDialog.Builder(context);
...
builder.setPositiveButton(android.R.string.ok, null);
...

final AlertDialog alert = builder.create();
alert.setOnShowListener(new OnShowListener() {

@Override
public void onShow(DialogInterface dialog) {
Button possitiveButton = alert.getButton(AlertDialog.BUTTON_POSITIVE);
possitiveButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
// If the conditions is fullfilled you should call: alert.dimiss();
// else do whatever you need to do
}
});
...

martes, 18 de febrero de 2014

Android: Adding "..." when the text doesn't fit

Programatically

TextView textView = (TextView) convertView.findViewById(R.id.highlight_cell_text);
textView.setText(someText);
textView.setEllipsize(TruncateAt.END);
textView.setHorizontallyScrolling(true);

Layout

<TextView
                android:id="@+id/highlight_cell_text"
                android:scrollHorizontally="true"
                android:ellipsize="end"
                ... />

miércoles, 12 de febrero de 2014

Android: Facebook

1) You need a Facebook account and that Facebook account has to be a Facebook developer account
2) Login with your account and enter de "Manage my applications" menu currently under "Configuration" or you can choose "Create an application" in the same menu
3) In the configuration add Android platform
4) The package name is the same that the package name in the project manifest, in the class field you have to set the class that has the "android.intent.action.MAIN"
5) In the key hashes you have to add one for the debug key and one for the release key
6) To obtain the debug key you can use the following code:

private void generateKeyHash() {

try {
PackageInfo info = 

getPackageManager().getPackageInfo(
"ar.com.fennoma.reporttv",
PackageManager.GET_SIGNATURES);

for (Signature signature : info.signatures) {

MessageDigest md = 

MessageDigest.getInstance("SHA");
md.update(signature.toByteArray());

Log.d("KeyHash:", 

Base64.encodeToString(md.digest(), 
Base64.DEFAULT));
}
}
catch (Exception e) {
e.printStackTrace();
}
}

7) To obtain the release key in windows open the cmd and type:

keytool -exportcert -alias <key_alias>1 -keystore "C:\Users\fennoma-dev\Desktop\Repositories\reportv\Android\ReportTV.txt" | openssl sha1 -binary | openssl base64 | clip

8) To be able to use it with Simplefacebook (for instance) or to use it in release mode you have to disable sand mode. Currently, in the main page of the facebook application you have the application's name and to its right a circle dot filled in white or all green. If filled with white then the application is in sand mode and you have to change it to public by clicking [?] or going to "Status & Review"

lunes, 10 de febrero de 2014

Android: Resource from string

View view = (View) findViewById(getResources().getIdentifier(<dynamically_generated_view_id>, "id", activity.getPackageName()));


Note:

Instead of "id" you could use, for instance, "drawable" and obtain a resource ID for a drawable given a string.

viernes, 7 de febrero de 2014

Android: Share preferences

Clear preferences
SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(context);
sharedPreferences.edit().clear().commit();

Set preference
SharedPreferences sharedPreferences = PreferenceManager
                .getDefaultSharedPreferences(context);
        sharedPreferences.edit().putInt(PROFILE, profileId).commit();

Get preference
SharedPreferences sharedPreferences = PreferenceManager
                .getDefaultSharedPreferences(context);
        sharedPreferences.getInt(PROFILE, profileId);

Android: Admit both landscape orientations


<activity
            android:name="imageslidergallery.ImageCarrouselActivity"
            android:screenOrientation="sensorLandscape"
            ... >
</activity>

If we were to use "landscape" rather than "sensorLandscape" then we would have only one landscape orientation available.

Android: Fragment back stack

activity.getSupportFragmentManager().getBackStackEntryCount()

miércoles, 5 de febrero de 2014