com.android.madpausa.cardnotificationviewer.CardElementHolder.java Source code

Java tutorial

Introduction

Here is the source code for com.android.madpausa.cardnotificationviewer.CardElementHolder.java

Source

/**
 *  Copyright 2015 Antonio Petrella
 *
 *  This file is part of Card Notification Viewer
 *
 *   Card Notification Viewer is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation, either version 3 of the License, or
 *  (at your option) any later version.
 *
 *  Card Notification Viewer is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with Card Notification Viewer.  If not, see <http://www.gnu.org/licenses/>.
 */

package com.android.madpausa.cardnotificationviewer;

import android.animation.Animator;
import android.animation.ValueAnimator;
import android.app.PendingIntent;
import android.content.Intent;
import android.os.Bundle;
import android.service.notification.StatusBarNotification;
import android.support.v4.content.LocalBroadcastManager;
import android.support.v4.view.GestureDetectorCompat;
import android.support.v7.widget.CardView;
import android.support.v7.widget.RecyclerView;
import android.util.Log;
import android.view.GestureDetector;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewConfiguration;
import android.view.ViewGroup;
import android.widget.Scroller;

/**
 * Created by ANTPETRE on 26/10/2015.
 * This represents the viewHolder in the RecyclerView
 */
public class CardElementHolder extends RecyclerView.ViewHolder {
    private static final String TAG = CardElementHolder.class.getSimpleName();

    StatusBarNotification sbn;
    ViewGroup parent;
    View root;
    View notificationView;

    ConcreteNotificationListenerService nService;

    GestureDetectorCompat gDetector;
    Scroller scroller;
    ValueAnimator animator;

    boolean cancel = false;

    public CardElementHolder(View r, ViewGroup p, ConcreteNotificationListenerService s) {
        super(r);
        root = r;
        parent = p;
        nService = s;

        //initializing objects for human interaction
        NotificationTouchListener nTouch = new NotificationTouchListener();
        gDetector = new GestureDetectorCompat(root.getContext(), nTouch);
        root.setOnTouchListener(nTouch);

        initAnimator();
    }

    private void initAnimator() {
        scroller = new Scroller(root.getContext());
        animator = ValueAnimator.ofFloat(1, 0);
        animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                animateScroll();
                if (cancel)
                    root.setAlpha((Float) animation.getAnimatedValue());
            }
        });
        animator.addListener(new Animator.AnimatorListener() {
            @Override
            public void onAnimationStart(Animator animation) {
                root.setLayerType(View.LAYER_TYPE_HARDWARE, null);
            }

            @Override
            public void onAnimationEnd(Animator animation) {
                endAnimation();
            }

            @Override
            public void onAnimationCancel(Animator animation) {
                endAnimation();
            }

            @Override
            public void onAnimationRepeat(Animator animation) {

            }

            private void endAnimation() {
                root.setTranslationX(scroller.getFinalX());
                root.setLayerType(View.LAYER_TYPE_SOFTWARE, null);
                if (cancel)
                    nService.cancelNotification(sbn);
            }
        });
    }

    public CardView getCardView() {
        return (CardView) root.findViewById(R.id.cardListitem);
    }

    public void setSbn(StatusBarNotification n) {
        sbn = n;
    }

    public void setNotificationView(View view) {
        notificationView = view;
        getCardView().addView(view);
        //should reset, in case of recycle
        resetHolder();
    }

    public View getNotificationView() {
        return notificationView;
    }

    public StatusBarNotification getSbn() {
        return sbn;
    }

    private void animateScroll() {
        if (scroller.computeScrollOffset()) {
            root.setTranslationX(scroller.getCurrX());
        } else {
            root.setTranslationX(scroller.getFinalX());
        }
    }

    private void resetHolder() {
        cancel = false;
        root.setTranslationX(0);
        root.setLayerType(View.LAYER_TYPE_HARDWARE, null);
        root.setAlpha(1);
        root.setLayerType(View.LAYER_TYPE_SOFTWARE, null);
    }

    @Override
    public boolean equals(Object o) {
        if (o instanceof CardElementHolder) {
            CardElementHolder v = (CardElementHolder) o;
            if (v.getSbn() != null)
                return NotificationFilter.getNotificationKey(v.getSbn())
                        .equals(NotificationFilter.getNotificationKey(sbn));
        }
        return false;
    }

    private class NotificationTouchListener extends GestureDetector.SimpleOnGestureListener
            implements View.OnTouchListener {
        //TODO check standard values
        static final int MIN_FLING_DISTANCE = 200;
        static final int MIN_FLING_VELOCITY = 200;
        static final int MIN_SLIDE_DISTANCE = 5;

        float startX;
        boolean isMoving = false;

        @Override
        public boolean onTouch(View v, MotionEvent event) {
            boolean result = gDetector.onTouchEvent(event);
            //handling basic
            if (!result) {
                switch (event.getActionMasked()) {
                case MotionEvent.ACTION_MOVE:
                    root.setLayerType(View.LAYER_TYPE_HARDWARE, null);
                    int distance = (int) (event.getX() - startX);
                    //should reserve touch only after a threshold
                    if (Math.abs(distance) > MIN_SLIDE_DISTANCE) {
                        parent.requestDisallowInterceptTouchEvent(true);
                        isMoving = true;
                        v.setPressed(false);
                    }
                    root.setTranslationX(v.getTranslationX() + distance);
                    result = true;
                    break;
                case MotionEvent.ACTION_UP:
                case MotionEvent.ACTION_CANCEL:
                    v.setPressed(false);
                    int x = Math.round(v.getTranslationX());
                    parent.requestDisallowInterceptTouchEvent(false);
                    scroller.startScroll(x, 0, -1 * x, 0, (int) animator.getDuration());
                    animator.start();
                    result = true;
                    isMoving = false;
                    break;
                }
            }
            return result;
        }

        @Override
        public void onLongPress(MotionEvent e) {
            super.onLongPress(e);
            if (!isMoving) {
                Log.d(TAG, "Long pressing!");

                //sending message to open dialog
                Intent intent = new Intent(NotificationDialogFragment.NOTIFICATION_DIALOG_INTENT);
                intent.putExtra(ConcreteNotificationListenerService.NOTIFICATION_EXTRA, sbn);
                LocalBroadcastManager.getInstance(root.getContext().getApplicationContext()).sendBroadcast(intent);
            }
        }

        @Override
        public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
            //shouldn't perform fling if the notification can't be removed
            boolean fling = sbn.isClearable();
            if (fling) {
                float distance = Math.abs(root.getTranslationX());
                fling = distance > MIN_FLING_DISTANCE && Math.abs(velocityX) > MIN_FLING_VELOCITY;
                if (fling) {
                    cancel = true;
                    //performing fling
                    scroller.fling((int) root.getTranslationX(), 0, Math.round(velocityX), 0,
                            -1 * Integer.MAX_VALUE, Integer.MAX_VALUE, 0, 0);
                    animator.start();
                }
            }
            return fling;
        }

        @Override
        public boolean onDown(MotionEvent e) {
            root.setPressed(true);
            startX = e.getX();
            return true;
        }

        @Override
        public boolean onSingleTapConfirmed(MotionEvent e) {
            if (!isMoving) {
                root.setPressed(false);
                //TODO open dialog with group notifications
                PendingIntent pendingIntent = sbn.getNotification().contentIntent;
                if (pendingIntent != null) {
                    try {
                        pendingIntent.send();
                    } catch (PendingIntent.CanceledException ex) {
                        Log.e(TAG, "error sending the intent");
                    }
                }
                return true;
            }
            return false;
        }
    }
}