Saturday, March 26, 2016

Dismiss or Cancel dialog by swipe gesture

Dismiss dialog by all four swipe gestures as swipe right to left, left to right, top to bottom, bottom to top First, create a inner class in custom dialogFragment class for the the SimpleGestureListener as,
class MyGestureDetector extends GestureDetector.SimpleOnGestureListener {

        private static final int SWIPE_MIN_DISTANCE = 120;
        private static final int SWIPE_THRESHOLD_VELOCITY = 200;

        public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX,
                               float velocityY) {

            if (e1.getX() - e2.getX() > SWIPE_MIN_DISTANCE
                    && Math.abs(velocityX) > SWIPE_THRESHOLD_VELOCITY) {
                // swipe right to left
                dismiss();
            } else if (e2.getX() - e1.getX() > SWIPE_MIN_DISTANCE
                    && Math.abs(velocityX) > SWIPE_THRESHOLD_VELOCITY) {
                // swipe left to right
                dismiss();
            } else if (e1.getY() - e2.getY() > SWIPE_MIN_DISTANCE
                    && Math.abs(velocityY) > SWIPE_THRESHOLD_VELOCITY) {
                // top to bottom
                dismiss();
            } else if (e2.getY() - e1.getY() > SWIPE_MIN_DISTANCE
                    && Math.abs(velocityY) > SWIPE_THRESHOLD_VELOCITY) {
                // bottom to top
                dismiss();
            }

            return super.onFling(e1, e2, velocityX, velocityY);
       }
}
Then in onCreateDialog() of dialogFragment,
GestureDetector gestureDetector = new GestureDetector(getActivity(), new MyGestureDetector());
LayoutInflater inflater = getActivity().getLayoutInflater();
View view = inflater.inflate(R.layout.dialog_nmd, null);
view.setOnTouchListener(new View.OnTouchListener() {
   @Override
   public boolean onTouch(View v, MotionEvent event) {
      boolean eventConsumed = gestureDetector.onTouchEvent(event);
      if (eventConsumed) {
          return true;
      } else {
          return false;
       }
    }
});
AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
builder.setView(view);
dialog = builder.create();
Window window = dialog.getWindow();
window.requestFeature(Window.FEATURE_NO_TITLE);
WindowManager.LayoutParams wlp = window.getAttributes();
window.setAttributes(wlp);
dialog.getWindow().setCallback(windowCallback);
Here the window call back interface is as,
private Window.Callback windowCallback = new Window.Callback() {
        @Override
        public boolean dispatchKeyEvent(KeyEvent event) {
            if (event.getKeyCode() == KeyEvent.KEYCODE_BACK) {
                getDialog().dismiss();
            }
            return false;
        }

        @Override
        public boolean dispatchKeyShortcutEvent(KeyEvent event) {
            return false;
        }

        @Override
        public boolean dispatchTouchEvent(MotionEvent event) {
            if (gestureDetector != null) {
                return gestureDetector.onTouchEvent(event);
            }
            return false;
        }

        @Override
        public boolean dispatchTrackballEvent(MotionEvent event) {
            return false;
        }

        @Override
        public boolean dispatchGenericMotionEvent(MotionEvent event) {
            return true;
        }

        @Override
        public boolean dispatchPopulateAccessibilityEvent(AccessibilityEvent event) {
            return false;
        }

        @Nullable
        @Override
        public View onCreatePanelView(int featureId) {
            return null;
        }

        @Override
        public boolean onCreatePanelMenu(int featureId, Menu menu) {
            return false;
        }

        @Override
        public boolean onPreparePanel(int featureId, View view, Menu menu) {
            return false;
        }

        @Override
        public boolean onMenuOpened(int featureId, Menu menu) {
            return false;
        }

        @Override
        public boolean onMenuItemSelected(int featureId, MenuItem item) {
            return false;
        }

        @Override
        public void onWindowAttributesChanged(WindowManager.LayoutParams attrs) {

        }

        @Override
        public void onContentChanged() {

        }

        @Override
        public void onWindowFocusChanged(boolean hasFocus) {

        }

        @Override
        public void onAttachedToWindow() {

        }

        @Override
        public void onDetachedFromWindow() {

        }

        @Override
        public void onPanelClosed(int featureId, Menu menu) {

        }

        @Override
        public boolean onSearchRequested() {
            return false;
        }

        @Nullable
        @Override
        public ActionMode onWindowStartingActionMode(ActionMode.Callback callback) {
            return null;
        }

        @Override
        public void onActionModeStarted(ActionMode mode) {

        }

        @Override
        public void onActionModeFinished(ActionMode mode) {

        }
    };

Note:
Here the tricky part is window callback, if you miss it, then this won't work. You have to override the dispatchTouchEvent() in this interface.

Monday, March 21, 2016

Add Button and onClick on Custom Notification in Android

Add a broadcast receiver with action and pass the Intent with action to PendingIntent The code snippet is as follows,
Intent intent = new Intent("action.cancel.notification");
PendingIntent pi = PendingIntent.getBroadCast(this, id, intent, PendingIntent.FLAG_UPDATE_CUREENT);
Also add the receiver in manifest
<receiver android:name="CancelNotificationReceiver">
<intent-filter>
<action android:name="action.cancel.notification"/>
</intent-filter>
</receiver>
After that, to the pictureView add the pendingIntnet as,
pictureView.setOnClickPendingIntent(R.id.button, pi);
The last part is define the receiver class as,
public class CancelNotificationReceiver extends Broadcastreceiver {
   public void onReceive(Context context, Intent intent) {
     if ("action.cancel.notification".equalsIgnoreCase(intent.getAction())) {
        // .. do what ever you want
     }
   }
}

Tuesday, February 23, 2016

How to find default sms messaging app


From api level 19 onwards, its just a one line call to get the packageName of default sms application.
Telephony.Sms.getDefaultSmsPackage(context)
Before API level 19, there is no concept of Default Sms, so what we can do is get the list of messaging apps.
private Object[] getMessagingAppPackageNames(Context context) {
   List messagingAppPackgeNameList = new ArrayList<>();
   final PackageManager pm = context.getPackageManager();
   //get metaData for installed apps
   List packages = pm.getInstalledApplications(PackageManager.GET_META_DATA);

   for (ApplicationInfo appInfo : packages) {
      String packageName = appInfo.packageName;
      if (packageName != null && (packageName.contains("sms")
             || packageName.contains("mms") || packageName.contains("message")
             || packageName.contains("SMS") || packageName.contains("MMS")
             || packageName.contains("Message") || packageName.contains("media")
             || packageName.contains("Media"))) {
                messagingAppPackgeNameList.add(packageName);
      }
   }
   return messagingAppPackgeNameList.toArray();
}
This list may have all messaging apps plus some other apps also. But at least we can make sure the list will have all messaging app package names.

Saturday, February 20, 2016

Dsiplay Multiple Notifications in status bar

For showing multiple notifications use unique identifier while creating notification which means use unique id in notify() method.

eg: notificationManager.notify(tag, id, notification);

Here, id should be unique.

Also, in PendingIntent "REQUEST_CODE" should be unique. use the same code as notificationId.

PendingIntent pIntent = PendingIntent.getService(context, id, intent, PendingIntent.FLAG_UPDATE_CURRENT);
Here also, id should be unique, use the same id itself. When clicking on notification, if you want to launch some component, to "intent" pass the desired arguments. For cancel, use both tag and id in the activity or service to cancel. First pass the tag and id to the service and then cancel using BOTH. This is very important


Wednesday, February 17, 2016

How to get a Color from hexadecimal Color String

To get the color int from hexadecimal string use the parseColor() methood from android.graphics.Color class

eg: int color = Color.parseColor("#FFFF0000");

Monday, February 15, 2016

DialogFragment - Where to use OnDismissListener and OnCancelListener

If you want to use OnCancelListener or OnDismissListener on dialog fragment, use it in onStart() rather than on onCreateDialog(); 

Saturday, February 13, 2016

Snackbar and FloatingButton Behavior

Snackbar is just an extension of Toast. It is an actionable Toast. Snackbar will stay on the screen forever or for a short duration (Snackbar.LENGTH_INDEFINITE, SnackBar.LENGTH_LONG, Snackbar.LENGTH_SHORT) and we can set action as well. One example that is coming to my mind is, from a list user deleted an item, then we can give user the option to "Undo" using Snackbar.

Now we will come to the implementation part.

Snackbar snackbar = Snackbar.make(recyclerView, "Do you want to undo", Snackbar.LENGTH_LONG).setAction("Undo", snackBarClickListener);
snackbar.setActionTextColor(Color.WHITE);
View snackbarView = snackbar.getView();
snackbarView.setBackgroundColor(Color.DKGRAY);
snackbar.getView().addOnAttachStateChangeListener(new View.OnAttachStateChangeListener() {

     @Override
     public void onViewAttachedToWindow(View v) {
        // write your code here
     }

     @Override
     public void onViewDetachedFromWindow(View v) {
        // write your code here
     }
});
Now about the floating button. Floating button is like when the snackbar appears, floating button floats. We will see the implementation. Here the floating action button is bottom right side. The xml to be defined in the layout is,

<android.support.design.widget.FloatingActionButton
        android:id="@+id/bottom_right_button"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="bottom|right"
        android:src="@drawable/foooooo"
        app:backgroundTint="#some color"
        app:elevation="6dp"
        app:fabSize="normal"
        app:rippleColor="# some color"
        app:layout_behavior="com.smartandroidians.FloatingActionButtonBehavior"
        android:layout_marginBottom="@dimen/fab_margin_bottom"
        android:layout_marginRight="@dimen/fab_margin_right"
        app:layout_anchorGravity="bottom|right|end" />
Now the class of FloatingActionButton is as follows,
public class FloatingActionButtonBehavior extends CoordinatorLayout.Behavior {

    public FloatingActionButtonBehavior(Context context, AttributeSet attrs) {
    }

    @Override
    public boolean layoutDependsOn(CoordinatorLayout parent, FloatingActionButton child, View dependency) {
        return dependency instanceof Snackbar.SnackbarLayout;
    }

    @Override
    public boolean onDependentViewChanged(CoordinatorLayout parent, FloatingActionButton child, View dependency) {
        float translationY = Math.min(0, dependency.getTranslationY() - dependency.getHeight());
        child.setTranslationY(translationY);
        return true;
    }
}
This is in the new android design support library. In gradle file, add this line as
dependencies {
  compile 'com.android.support:design:22.2.1'
}
Then if you are using proguard file, then all these lines, otherwise FloatingActionButtonBehavior class won't accept the proguard.
-keepclasseswithmembers class * {
    native ;
}

-keepclasseswithmembers class * {
    public (android.content.Context, android.util.AttributeSet);
}

-keepclasseswithmembers class * {
    public (android.content.Context, android.util.AttributeSet, int);
}
If you want to know more about proguard settings, please check here

One last point is, for FloatingActionButton should be in CoordinatorLayout. the xml is as,
<android.support.design.widget.CoordinatorLayout
    android:id="@+id/rootLayout"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto">
That's it !!! Enjoy Coding !!!

Androidians