com.silentcircle.silentphone2.list.ShortcutCardsAdapter.java Source code

Java tutorial

Introduction

Here is the source code for com.silentcircle.silentphone2.list.ShortcutCardsAdapter.java

Source

/*
 * Copyright (C) 2011 Google Inc.
 * Licensed to The Android Open Source Project.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.silentcircle.silentphone2.list;

import android.annotation.TargetApi;
import android.content.Context;
import android.content.res.Resources;
import android.database.ContentObserver;
import android.database.Cursor;
import android.database.DataSetObserver;
import android.graphics.Rect;
import android.net.Uri;
import android.os.Build;
import android.os.Handler;
import android.provider.ContactsContract;
import android.support.annotation.NonNull;
import android.support.v4.content.ContextCompat;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewConfiguration;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.FrameLayout;
import android.widget.LinearLayout;
import android.widget.TextView;

import com.silentcircle.common.util.ViewUtil;
import com.silentcircle.contacts.calllognew.CallLogAdapter;
import com.silentcircle.contacts.calllognew.CallLogListItemView;
import com.silentcircle.contacts.calllognew.CallLogQueryHandler;
import com.silentcircle.silentphone2.R;
import com.silentcircle.silentphone2.list.SwipeHelper.OnItemGestureListener;
import com.silentcircle.silentphone2.list.SwipeHelper.SwipeHelperCallback;
import com.silentcircle.silentphone2.services.InsertCallLogHelper;

/**
 * An adapter that displays call shortcuts from {@link com.android.dialer.calllog.CallLogAdapter}
 * in the form of cards.
 */
public class ShortcutCardsAdapter extends BaseAdapter {

    private class CustomDataSetObserver extends DataSetObserver {
        @Override
        public void onChanged() {
            notifyDataSetChanged();
        }
    }

    protected ContentObserver mChangeObserver = new ContentObserver(new Handler()) {

        @Override
        public boolean deliverSelfNotifications() {
            return true;
        }

        @Override
        public void onChange(boolean selfChange) {
            onChange(selfChange, null);
        }

        @Override
        public void onChange(boolean selfChange, Uri uri) {
            mCallLogAdapter.invalidateCache();
            mCallLogAdapter.notifyDataSetChanged();
        }
    };

    @SuppressWarnings("unused")
    private static final String TAG = ShortcutCardsAdapter.class.getSimpleName();
    private static final float CLIP_CARD_BARELY_HIDDEN_RATIO = 0.001f;
    private static final float CLIP_CARD_MOSTLY_HIDDEN_RATIO = 0.9f;
    // Fade out 5x faster than the hidden ratio.
    private static final float CLIP_CARD_OPACITY_RATIO = 5f;

    private final CallLogAdapter mCallLogAdapter;

    private final ListsFragment mFragment;

    private final int mCallLogMarginHorizontal;
    private final int mCallLogMarginTop;
    private final int mCallLogMarginBottom;
    private final int mCallLogPaddingStart;
    private final int mCallLogPaddingTop;
    private final int mCallLogPaddingBottom;
    private final int mShortCardBackgroundColor;

    private final Context mContext;

    private final CallLogQueryHandler mCallLogQueryHandler;

    private final OnItemGestureListener mCallLogOnItemSwipeListener = new OnItemGestureListener() {
        @Override
        public void onSwipe(View view) {
            mCallLogQueryHandler.markNewCallsAsOld();
            //            mCallLogQueryHandler.markNewVoicemailsAsOld();
            InsertCallLogHelper.removeMissedCallNotifications(mContext);
            //            CallLogNotificationsHelper.updateVoicemailNotifications(mContext);
            mFragment.dismissShortcut(view);
        }

        @Override
        public void onTouch() {
        }

        @Override
        public boolean isSwipeEnabled() {
            return true;
        }
    };

    @SuppressWarnings("FieldCanBeLocal")
    private final CallLogQueryHandler.Listener mCallLogQueryHandlerListener = new CallLogQueryHandler.Listener() {
        @Override
        public void onVoicemailStatusFetched(Cursor statusCursor) {
        }

        @Override
        public boolean onCallsFetched(Cursor combinedCursor) {
            mCallLogAdapter.invalidateCache();
            mCallLogAdapter.changeCursor(combinedCursor);
            mCallLogAdapter.notifyDataSetChanged();
            // Return true; took ownership of cursor
            return true;
        }
    };

    @TargetApi(Build.VERSION_CODES.M)
    @SuppressWarnings("deprecation")
    public ShortcutCardsAdapter(Context context, ListsFragment fragment, CallLogAdapter callLogAdapter) {
        final Resources resources = context.getResources();
        mContext = context;
        mFragment = fragment;
        mCallLogMarginHorizontal = resources.getDimensionPixelSize(R.dimen.recent_call_log_item_margin_horizontal);
        mCallLogMarginTop = resources.getDimensionPixelSize(R.dimen.recent_call_log_item_margin_top);
        mCallLogMarginBottom = resources.getDimensionPixelSize(R.dimen.recent_call_log_item_margin_bottom);
        mCallLogPaddingStart = resources.getDimensionPixelSize(R.dimen.recent_call_log_item_padding_start);
        mCallLogPaddingTop = resources.getDimensionPixelSize(R.dimen.recent_call_log_item_padding_top);
        mCallLogPaddingBottom = resources.getDimensionPixelSize(R.dimen.recent_call_log_item_padding_bottom);
        mShortCardBackgroundColor = ViewUtil.getColorFromAttributeId(context,
                R.attr.call_log_primary_background_color);

        mCallLogAdapter = callLogAdapter;
        mCallLogAdapter.registerDataSetObserver(new CustomDataSetObserver());
        mCallLogQueryHandler = new CallLogQueryHandler(mContext.getContentResolver(), mCallLogQueryHandlerListener);

        mContext.getContentResolver().registerContentObserver(ContactsContract.Contacts.CONTENT_URI, true,
                mChangeObserver);
    }

    /**
     * Determines the number of items in the adapter.
     * mCallLogAdapter contains the item for the most recent caller.
     * mContactTileAdapter contains the starred contacts.
     * The +1 is to account for the presence of the favorites menu.
     *
     * @return Number of items in the adapter.
     */
    @Override
    public int getCount() {
        return mCallLogAdapter.getCount();
    }

    @Override
    public Object getItem(int position) {
        return mCallLogAdapter.getItem(position);
    }

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

    @Override
    public boolean hasStableIds() {
        return true;
    }

    /**
     * Determine the number of view types present.
     */
    @Override
    public int getViewTypeCount() {
        return mCallLogAdapter.getViewTypeCount();
    }

    @Override
    public int getItemViewType(int position) {
        return mCallLogAdapter.getItemViewType(position);
    }

    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        final SwipeableShortcutCard wrapper;
        if (convertView == null) {
            wrapper = new SwipeableShortcutCard(mContext);
            wrapper.setOnItemSwipeListener(mCallLogOnItemSwipeListener);
        } else {
            wrapper = (SwipeableShortcutCard) convertView;
        }

        // Special case wrapper view for the most recent call log item. This allows
        // us to create a card-like effect for the more recent call log item in
        // the PhoneFavoriteMergedAdapter, but keep the original look of the item in
        // the CallLogAdapter.
        final View view = mCallLogAdapter.getView(position, convertView == null ? null : wrapper.getChildAt(0),
                parent);
        wrapper.removeAllViews();
        wrapper.prepareChildView(view);
        wrapper.addView(view);
        return wrapper;
    }

    @Override
    public boolean areAllItemsEnabled() {
        return mCallLogAdapter.areAllItemsEnabled();
    }

    @Override
    public boolean isEnabled(int position) {
        return mCallLogAdapter.isEnabled(position);
    }

    public void unregisterContentObserver() {
        if (mContext != null && mChangeObserver != null) {
            mContext.getContentResolver().unregisterContentObserver(mChangeObserver);
        }
    }

    /**
     * The swipeable call log row.
     */
    class SwipeableShortcutCard extends FrameLayout implements SwipeHelperCallback {
        private final SwipeHelper mSwipeHelper;
        private OnItemGestureListener mOnItemSwipeListener;

        private float mPreviousTranslationZ = 0;
        private final Rect mClipRect = new Rect();

        public SwipeableShortcutCard(Context context) {
            super(context);
            final float densityScale = getResources().getDisplayMetrics().density;
            final float pagingTouchSlop = ViewConfiguration.get(context).getScaledPagingTouchSlop();

            mSwipeHelper = new SwipeHelper(context, SwipeHelper.X, this, densityScale, pagingTouchSlop);
        }

        private void prepareChildView(View view) {
            // Override CallLogAdapter's accessibility behavior; don't expand the shortcut card.
            view.setAccessibilityDelegate(null);
            //            view.setBackgroundResource(R.drawable.rounded_corner_bg);

            final LayoutParams params = new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT);
            params.setMargins(mCallLogMarginHorizontal, mCallLogMarginTop, mCallLogMarginHorizontal,
                    mCallLogMarginBottom);
            view.setLayoutParams(params);

            LinearLayout actionView = (LinearLayout) view.findViewById(R.id.primary_action_view);
            if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.JELLY_BEAN_MR1) {
                actionView.setPadding(mCallLogPaddingStart, mCallLogPaddingTop, actionView.getPaddingRight(),
                        mCallLogPaddingBottom);
            } else {
                actionView.setPaddingRelative(mCallLogPaddingStart, mCallLogPaddingTop, actionView.getPaddingEnd(),
                        mCallLogPaddingBottom);
            }

            // TODO: Set content description including type/location and time information.
            TextView nameView = (TextView) actionView.findViewById(R.id.name);
            actionView.setContentDescription(
                    getResources().getString(R.string.description_call_back_action, nameView.getText()));

            mPreviousTranslationZ = getResources()
                    .getDimensionPixelSize(R.dimen.recent_call_log_item_translation_z);
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP)
                view.setTranslationZ(mPreviousTranslationZ);

            final CallLogListItemView callLogItem = (CallLogListItemView) view
                    .findViewById(R.id.call_log_list_item);
            // Reset the internal call log item view if it is being recycled
            callLogItem.setTranslationX(0);
            callLogItem.setTranslationY(0);
            callLogItem.setAlpha(1);
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2)
                callLogItem.setClipBounds(null);
            setChildrenOpacity(callLogItem, 1.0f);

            callLogItem.findViewById(R.id.call_log_row).setBackgroundColor(mShortCardBackgroundColor);
            //            callLogItem.findViewById(R.id.primary_action_view).setBackgroundColor(mShortCardBackgroundColor);
            callLogItem.findViewById(R.id.call_indicator_icon).setVisibility(View.INVISIBLE);
        }

        @Override
        public View getChildAtPosition(MotionEvent ev) {
            return getChildCount() > 0 ? getChildAt(0) : null;
        }

        @Override
        public View getChildContentView(View v) {
            return v.findViewById(R.id.call_log_list_item);
        }

        @Override
        public void onScroll() {
        }

        @Override
        public boolean canChildBeDismissed(View v) {
            return true;
        }

        @Override
        public void onBeginDrag(View v) {
            // We do this so the underlying ScrollView knows that it won't get
            // the chance to intercept events anymore
            requestDisallowInterceptTouchEvent(true);
        }

        @Override
        public void onChildDismissed(View v) {
            if (v != null && mOnItemSwipeListener != null) {
                mOnItemSwipeListener.onSwipe(v);
            }
        }

        @Override
        public void onDragCancelled(View v) {
        }

        @Override
        public boolean onInterceptTouchEvent(MotionEvent ev) {
            return mSwipeHelper.onInterceptTouchEvent(ev) || super.onInterceptTouchEvent(ev);
        }

        @Override
        public boolean onTouchEvent(@NonNull MotionEvent ev) {
            return mSwipeHelper.onTouchEvent(ev) || super.onTouchEvent(ev);
        }

        public void setOnItemSwipeListener(OnItemGestureListener listener) {
            mOnItemSwipeListener = listener;
        }

        /**
         * Clips the card by a specified amount.
         *
         * @param ratioHidden A float indicating how much of each edge of the card should be
         *         clipped. If 0, the entire card is displayed. If 0.5f, each edge is hidden
         *         entirely, thus obscuring the entire card.
         */
        @TargetApi(Build.VERSION_CODES.LOLLIPOP)
        public void clipCard(float ratioHidden) {
            final View viewToClip = getChildAt(0);
            if (viewToClip == null) {
                return;
            }
            int width = viewToClip.getWidth();
            int height = viewToClip.getHeight();

            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
                if (ratioHidden <= CLIP_CARD_BARELY_HIDDEN_RATIO) {
                    viewToClip.setTranslationZ(mPreviousTranslationZ);
                } else if (viewToClip.getTranslationZ() != 0) {
                    mPreviousTranslationZ = viewToClip.getTranslationZ();
                    viewToClip.setTranslationZ(0);
                }
            }

            if (ratioHidden > CLIP_CARD_MOSTLY_HIDDEN_RATIO) {
                mClipRect.set(0, 0, 0, 0);
                setVisibility(View.INVISIBLE);
            } else {
                setVisibility(View.VISIBLE);
                int newTop = (int) (ratioHidden * height);
                mClipRect.set(0, newTop, width, height);
                // Since the pane will be overlapping with the action bar, apply a vertical offset
                // to top align the clipped card in the viewable area;
                // viewToClip.setTranslationY(-newTop);
            }
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2)
                viewToClip.setClipBounds(mClipRect);

            // If the view has any children, fade them out of view.
            final ViewGroup viewGroup = (ViewGroup) viewToClip;
            setChildrenOpacity(viewGroup, Math.max(0, 1 - (CLIP_CARD_OPACITY_RATIO * ratioHidden)));
        }

        private void setChildrenOpacity(ViewGroup viewGroup, float alpha) {
            final int count = viewGroup.getChildCount();
            for (int i = 0; i < count; i++) {
                viewGroup.getChildAt(i).setAlpha(alpha);
            }
        }
    }
}