Blog Archives

Parsing CronJob Expression in Android

I had been trying to parse a cron expression string in my Android app and came across this CronExpression Code source code. I move it into my project and you can just use it off the shelve by calling

CronExpression conExpression = new CronExpression(“* * 10 * * ?”); // This will construct a cronjob which runs at 10am daily.

After obtaining the object, you can explore to more method that it provides for your business logic.

Advertisements

Simulate Slide to Unlock in application

Here I will showcase on how to create a slide to unlock similar to old Android lockscreen. I will customize the seekbar to transparent and replace the generic circle with custom icons. We will need 2 different icons to reflect locked and unlocked state.

First, we need to define the seekbar element in our layout file

<SeekBar
android:id="@+id/seekbar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:max="100"
android:progress="0"
android:progressDrawable="@android:color/transparent"
android:thumb="@drawable/ic_lock"/>;

Second, we need to hook up the seekbar that we specified in the layout file in our class

// Hooking up seekbar
SeekBar mSeekbar = (SeekBar) view.findViewById(R.id.seekbar);
// Set listener to our newly created seekbar
mSeekbar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
    @Override
    public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {

        // Perform your animation of the thumb icon if any, here I will progressively make the thumb icon transparent
        int alpha = (int)(progress * (255/100));
        seekBar.getThumb().setAlpha(255 - alpha);
    }

    @Override
    public void onStartTrackingTouch(SeekBar seekBar) {
        // This will be called when user starts to touch the icon
    }

            @Override
            public void onStopTrackingTouch(SeekBar seekBar) {
               
                // When user lift up the touch, we will check if it is at the end of the bar. If it is not the end, then we will set the progress status to 0 so it jumps back to the origin point
                if (seekBar.getProgress() &lt; 85){
                    seekBar.setThumb(getActivity().getResources().getDrawable(R.drawable.ic_lock));
                    seekBar.setProgress(0);
                } else {
                    // Put all the logic we want to proceed after unlock here
        }
    }
});

android.support.test.espresso.PerformException: Error performing ‘single click’

I encounter the following exception while writing the Espresso test case which involve entering text into edittext component. From what I can tell is the window loses focus and system cant find the next view for action that needs to be process. As a result, after I tell the Espresso framework to enter text, I post a closeKeyboard api to the same edittext so it can be close and Espresso framework able to find the next onView ID.

Error:
android.support.test.espresso.PerformException: Error performing ‘single click’….

Workaround: onView(withId(R.id.edittext_username)).perform(clearText(),typeText(“xxxxx”));
onView(withId(R.id.edittext_username)).perform(closeSoftKeyboard());
Thread.sleep(1000);
//…. next edittext entry

* Please post comments if anyone has a better solution to this.

Creating your own custom dialogfragment

I came accross an error “java.lang.IllegalStateException: Fragment already added:” whenever I try to show my dialogfragment. FindFragmentByTag is not reliable because I do not like to keep track of the dialog and the surprises that sometime it return NULL.

Here is the example of how one can customize the DialogFragment to your own taste. In this class, I define an extra parameter called isDialogShown to make my life easier so I do not have to rely on getFragmentManager().findFragmentByTag() that most of the time will return null object.

To check if the dialog is up, simple just check mDialogFragment.isDialogShown() and call mDiaglogFragment.dismiss() before fragment.show().

Initialize:

MyCustomDialogFragment dialog = new MyCustomDialogFragment(new OnDialogButtonClickListener(){
    
        @Override
        public void onDialogPositiveClick(int status){
          // Do your stuff here
          dialog.dismiss();
        };

        @Override
        public void onDialogNegativeClick(int status){
          // Do your stuff here
          dialog.dismiss();
        };
});

// Set texts that you want to show in your dialog
dialog.setParameters(....);

Showing dialog:


// Always check if dialog is currently shown
if (dialog.isDialogShown(){
   dialog.dismiss();
}

dialog.show(getFragmentManager(), MyCustomDialogFragment.TAG);

MyCustomDialogFragment class:

public class MyCustomDialogFragment extends DialogFragment{

    public static String TAG = MyCustomDialogFragment.class.getSimpleName();

    // Define listener that will be called when positive/negative button is pressed.
    public interface OnDialogButtonClickListener{
        public void onDialogPositiveClick(int status);
        public void onDialogNegativeClick(int status);
    }

    private OnDialogButtonClickListener mListener;

    private boolean isDialogShown = false;

    private String mTitleLabel="", mBodyLabel="", mPositiveLabel="", mNegativeLabel="";

    public MyCustomDialogFragment(OnDialogButtonClickListener listener){
        mListener = listener;
    }

    /**
     * Method to set dialog parameters
     * @param title for dialog
     * @param content for dialog
     * @param positiveLabel for positive button
     * @param negativeLabel for negative button
     */
    public void setParameters(String title, String content, String positiveLabel, String negativeLabel){
        mTitleLabel = title;
        mBodyLabel = content;
        mPositiveLabel = positiveLabel;
        mNegativeLabel = negativeLabel;
    }

    @Override
    public void show(FragmentManager manager, String tag) {
        super.show(manager, tag);
        isDialogShown = true;
    }

    @Override
    public void dismiss() {
        super.dismiss();
        isDialogShown = false;
    }

    public boolean isDialogShown() {return isDialogShown;}

    @Override
    public Dialog onCreateDialog(Bundle savedInstanceState) {
        // Use the Builder class for convenient dialog construction
        AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
        builder.setTitle(mTitleLabel)
            .setMessage(mBodyLabel)
            .setPositiveButton(mPositiveLabel, new DialogInterface.OnClickListener() {
                public void onClick(DialogInterface dialog, int id) {
                    mListener.onDialogPositiveClick(mStatusCode);
                }
            });

        // Sometime, we do not want to show negative button
        if (!mNegativeLabel.equals("")){
            builder.setNegativeButton(mNegativeLabel, new DialogInterface.OnClickListener() {
                public void onClick(DialogInterface dialog, int id) {
                    mListener.onDialogNegativeClick(mStatusCode);
                }
            });
        }

        // Create the AlertDialog object and return it
        return builder.create();
    }
}

Dynamically load a class based on string in Android

Method to get loaded class. The method will return null if class is not loaded successfully.

public Object getDeviceClass(Context context, String packageName, String className){
  try {
            // Load the dex files for loading the dynamic class
            PackageManager pm = context.getPackageManager();
            File dexOutputDir = context.getDir("dex", Context.MODE_PRIVATE);
            ApplicationInfo ai = pm.getApplicationInfo(context.getPackageName(), 0);
            String sourceApk = ai.publicSourceDir;
            DexClassLoader dexLoader = new DexClassLoader(sourceApk,
                    dexOutputDir.getAbsolutePath(),
                    null,
                    context.getClassLoader());

            // Now load the corresponding class based on the class name
            Class<?> targetClass = Class.forName(packageName + "." + className, true, dexLoader);

            return targetClass.getConstructor().newInstance();

        } catch (PackageManager.NameNotFoundException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }

return null
}

If success, you can easily cast it to your corresponding class. The loaded class object needs to extend the parent class.

       Object loadedClass = getDeviceClass(context,packagename,classname);
       ParentClass yourParentClass = (ParentClass)loadedClass;
       yourParentClass.publicMethod();

How to copy a ParseObject to a new ParseObject in different class

There is no function to copy a ParseObject into another class. In order to do so, one will need to create a ParseObject of the new class and re-assign all the keyset into the new ParseObject.

ParseObject is kinda like a Map object, so it is easy to retrieve all available keys, just as what you will do with a map.

ParseObject sourceObject = sourceParseObject;
ParseObject targetObject = new ParseObject("NewClass");
for (Iterator it = sourceObject.keySet().iterator(); it.hasNext();) {
      Object key = it.next();
      targetObject.put(key.toString(),sourceObject.get(key.toString());
    }

Listen to content uri changes

If you have an URI that you want to listen to but no broadcast is available from the content uri (No Broadcast Receiver available), then you can use ContentObserver class for this purpose.

Class:

public class myContentOberser extends ContentObserver{</code>

private String Uri contentUri = "";
private ContentResolver resolver = null;

public myContentObserver(Handler handler){
super(handler);
}

@Override
public void onChange(boolean selfChange){
// Perform what you want to do here.
}

public void startObserve(Context context, Uri uri){
if (resolver == null){
resolver = context.getContentResolver();
}

// Register the required uri to the system
resolver.registerContentObserver(uri, false, this);
}

public void stopObserver(){
if (resolver != null){
resolver.unregisterContentObserver(this);
}
}
}

Android MapFragment exception when clicked twice

My application crashed when I re-open the same MapFragment again twice.

E/AndroidRuntime(27409): Caused by: java.lang.IllegalArgumentException: Binary XML file line #5: Duplicate id 0x7f070039, tag null, or parent id 0xffffffff with another fragment for com.google.android.gms.maps.MapFragment

I solved it by declaring the view in my fragment where I called the map to static. By this way, I only create a new view if it is null, thus avoiding it to recreate the view again.

MapFragment.java

    static View rootView;
    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
        Bundle savedInstanceState) {
    	if(rootView==null){
    		rootView = inflater.inflate(R.layout.MapFragment, container, false);
    	}
        return rootView;
    }

MapFragment.xml

	<fragment 
	    android:id="@+id/mapview"
	    android:layout_width="match_parent"
	    android:layout_height="match_parent"
        class="com.google.android.gms.maps.MapFragment"/>

Android using SharedPreferences

Rather than having to call PreferenceManager.getDefaultSharedPreferences everytime in different activities, and have to keep track of all the keys in your application, it is easier to keep all preferences in one place. For that I create a wrapper class just for my preferences.

PreferencesClass.java

class PreferencesClass {
    static String preference_name = "my_preferences";
    static String preference_one = "preference_one";
    static String preference_two = "preference_two";
    static String preference_three = "preference_three";
    private static SharedPreferences getPreference(Context context){
        return PreferenceManager.getDefaultSharedPreferences(context);
    }

    public static int getPreferenceOne(Context context){ return getPreference(context).getInt(preference_one,0); }
    public static int setPreferenceOne(Context context, int value){ getPreference(context).edit().putInt(preference_one, value).commit(); }

public static int getPreferenceTwo(Context context){ return getPreference(context).getString(preference_two,null); }
    public static int setPreferenceTwo(Context context, string value){ getPreference(context).edit().putString(preference_two, value).commit(); }
    public static int getPreferenceThree(Context context){ return getPreference(context).getBoolean(preference_three,false); }
    public static int setPreferenceThree(Context context, boolean value){ getPreference(context).edit().putBoolean(preference_one, value).commit(); }

To use the method simply call the following in your activity

int preference_one = PreferencesClass.getPreferenceOne(getContext());

Check if virtual keyboard is up

As most of you can find in internet, there is no easy way to check if the soft keyboard is shown on the screen. Depending on what you are looking to achieve, for Touch Screen only application, I use the following solution. Might not be the perfect solution for you but it works for me.

As this is TOUCH only application, so I assume soft keyboard will only get triggered by when one of the edit text field got touch. So I set a public variable for the flag, false by default. When user click on either one of the edit text field, touch listener will set the flag to true, and when focused came back to main view, I will set the flag back to false in dispatchTouchEvent method.

Setting the flag

//This is my flag
public boolean isSoftKeyboardDisplayed = false;

Now, set listener for the edit text field

// This is my edit text field box
EditText editText = (EditText)findViewById(R.id.edittext);
editText.setOnTouchListener(new onTouchListener(){
@Override
public void onTouch(View v, MotionEvent event){
// The flag is set to true everytime the edit text is touched.
isSoftKeyboardDisplayed = true;
}
});

Dismiss the soft keyboard by implementing  dispatchTouchEvent to your root view. So when user touch any where other than the edit text field, the soft keyboard dissapear

// This part is to dismiss the softkeyboard when you click anywhere other than the edit text
@Override
public boolean dispatchTouchEvent(MotionEvent event) {

View v = getCurrentFocus();
boolean ret = super.dispatchTouchEvent(event);
if (v instanceof EditText) {
View w = getCurrentFocus();
int scrcoords[] = new int[2];
w.getLocationOnScreen(scrcoords);
float x = event.getRawX() + w.getLeft() - scrcoords[0];
float y = event.getRawY() + w.getTop() - scrcoords[1];
if (event.getAction() == MotionEvent.ACTION_UP && (x < w.getLeft() || x >= w.getRight() || y < w.getTop() || y > w.getBottom()) ) {
InputMethodManager imm = (InputMethodManager)getSystemService(Context.INPUT_METHOD_SERVICE);
imm.hideSoftInputFromWindow(getWindow().getCurrentFocus().getWindowToken(), 0);
// This is where I set the flag back to false when my soft keyboard is hidden
isSoftKeyboardDisplayed= false;
}
}
return ret;
}

As a result, I just check the isSoftKeyboardDisplayed flag for my keyboard display check.