1

I know that there are countless examples of this on the internet, but for some reason I can't get this to work in the particular code I'm working on, despite all the resources/answers I've read through. And I don't have anyone with Java skills around me to help out. So hopefully that changes here.

I'm updating an existing Android media picker plugin (for a cordova app) to make it show video thumbnails in addition to pictures in the device gallery. I'm stuck with a "non-static variable in static context" error, but I'm having a really hard time identifying what needs to be changed. Below is the meat of the code I have. I have removed some parts to hopefully focus on the relevant bits. Essentially, the error occurs inside decodeSampledBitmapFromUri when I'm trying to get the thumbnail of a video. The method I'm using is MediaStore.Video.Thumbnails.getThumbnail and its first argument is the context, which is where the error starts. You can see that in loadThumbnail I tried getting the context using cordova.getActivity() and then passing it to decodeSampledBitmapFromUri, but even inside loadThumbnail I'm still getting the non-static error. I'm not sure how to proceed from here (I'm very new to Java). This is the code (with some other parts stripped out because I think they're not relevant):

public class MediaPicker extends CordovaPlugin {
    private static final String HEIGHT = "height";
    private static final String COLUMNS = "columns";
    private static final String SELECTION_LIMIT = "selectionLimit";

    private MediaPickerView view;

    public static int getResourceId(Context context, String group, String key) {
        return context.getResources().getIdentifier(key, group, context.getPackageName());
    }

    public static int DP2PX(Context context, float dipValue) {
        DisplayMetrics metrics = context.getResources().getDisplayMetrics();
        return (int)TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dipValue, metrics);
    }

    @Override
    public void initialize(CordovaInterface cordova, CordovaWebView webView) {
        super.initialize(cordova, webView);
    }

    @Override
    public boolean execute(String action, JSONArray data, CallbackContext callbackContext) throws JSONException {
        if (action.equals("display")) {
            JSONObject store = data.getJSONObject(0);
            double height = Double.parseDouble(store.getString(HEIGHT));
            int columns = Integer.parseInt(store.getString(COLUMNS));
            int selectionLimit = Integer.parseInt(store.getString(SELECTION_LIMIT));
            display(height, columns, selectionLimit, callbackContext);
            return true;
        } else {
            return false;
        }
    }

    private void display(final double height, final int columns, final int selectionLimit, final CallbackContext callbackContext) {
        cordova.getActivity().runOnUiThread(new Runnable() {
            @Override
            public void run() {
                if (view == null) {
                    view = new MediaPickerView(cordova.getActivity());
                }
                view.setOptions(height, columns, selectionLimit, callbackContext);
                view.load();
                cordova.getActivity().addContentView(view,
                        new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
                                ViewGroup.LayoutParams.MATCH_PARENT));
            }
        });
    }



    public static class MediaPickerView extends FrameLayout implements StateController {
        private GridView grid;
        private View balanceView;
        private int selectionLimit;
        private CallbackContext callbackContext;
        private TreeMap<Integer, PictureInfo> selection = new TreeMap();

        public MediaPickerView(Context context) {
            super(context);
            init();
        }

        public MediaPickerView(Context context, AttributeSet attrs) {
            super(context, attrs);
            init();
        }

        private void init() {
            LayoutInflater.from(getContext()).inflate(getResourceId(getContext(), "layout", "view_media"), this);
            grid = (GridView)findViewById(getResourceId(getContext(), "id", "gridView"));
            balanceView = findViewById(getResourceId(getContext(), "id", "vBalancer"));
        }

        public void setOptions(double height, int columns, int selectionLimit, CallbackContext callbackContext) {
            this.selectionLimit = selectionLimit;
            this.callbackContext = callbackContext;
            grid.setNumColumns(columns);
            LinearLayout.LayoutParams glp = (LinearLayout.LayoutParams) grid.getLayoutParams();
            glp.weight = (float) height;
            LinearLayout.LayoutParams blp = (LinearLayout.LayoutParams) balanceView.getLayoutParams();
            blp.weight = (float) (1 - height);
            requestLayout();
        }

        public void load() {
            final GridAdapter adapter = new GridAdapter(getContext(), this);
            grid.setAdapter(adapter);
            new Thread() {
                @Override
                public void run() {
                    final String[] columns = new String[]{
                            MediaStore.Video.VideoColumns._ID,
                            MediaStore.Video.VideoColumns.DATA,
                            MediaStore.Video.VideoColumns.BUCKET_DISPLAY_NAME,
                            MediaStore.Video.VideoColumns.DISPLAY_NAME,
                            MediaStore.Video.VideoColumns.DATE_TAKEN,
                            MediaStore.Video.VideoColumns.MIME_TYPE,
                            MediaStore.Files.FileColumns.MEDIA_TYPE};
                    final String orderBy = MediaStore.Video.VideoColumns.DATE_TAKEN
                            + " DESC";
                    final String selection = MediaStore.Files.FileColumns.MEDIA_TYPE + "="
                            + MediaStore.Files.FileColumns.MEDIA_TYPE_VIDEO;
                    Uri queryUri = MediaStore.Files.getContentUri("external");
                    final Cursor cursor = getContext().getContentResolver().query(
                            queryUri,
                            columns,
                            selection, // Which rows to return (all rows)
                            null, // Selection arguments (none)
                            orderBy);


                    if (cursor.moveToFirst()) {
                        adapter.setCursor(cursor);
                    }
                }
            }.start();
        }
    }

    public static class MediaCache extends LruCache<String, Bitmap> {
        public MediaCache(int maxSize) {
            super(maxSize);
        }

        @Override
        protected int sizeOf(String key, Bitmap value) {
            if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
                return value.getAllocationByteCount();
            }
            return value.getByteCount();
        }
    }

    public static class GridAdapter extends BaseAdapter implements AsyncPictureLoader{

        private Cursor cursor;
        private Executor executor = Executors.newFixedThreadPool(4);
        private MediaCache pictureCache;
        private int dataColumn;
        private StateController stateController;
        private ArrayList<PictureView> createdViews = new ArrayList<PictureView>();

        public GridAdapter(Context context, StateController stateController) {
            this.stateController = stateController;
            int memClass = ( (ActivityManager)context.getSystemService( Context.ACTIVITY_SERVICE ) ).getMemoryClass();
            int cacheSize = 1024 * 1024 * memClass / 10;
            pictureCache = new MediaCache(cacheSize);
        }

        @Override
        public int getCount() {
            return cursor != null ? cursor.getCount() : 0;
        }

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

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

        @Override
        public View getView(int position, View convertView, ViewGroup parent) {
            PictureView view;
            if (convertView != null) {
                view = (PictureView) convertView;
            } else {
                view = new PictureView(pictureCache, parent.getContext(), this, stateController);
                createdViews.add(view);
            }
            view.load(position);
            return view;
        }

        public void setCursor(Cursor cursor) {
            this.cursor = cursor;
            dataColumn = cursor
                    .getColumnIndex(MediaStore.Video.VideoColumns.DATA);
            new Handler(Looper.getMainLooper()).post(new Runnable() {
                @Override
                public void run() {
                    notifyDataSetChanged();
                }
            });
        }

        @Override
        public void loadThumbnail(final PictureInfo pictureInfo, final AsyncPictureLoaderCallback callback) {
            if (cursor == null) {
                return;
            }
            executor.execute(new Runnable() {
                @Override
                public void run() {
                    if (cursor == null || pictureInfo.cancelled) {
                        return;
                    }
                    synchronized (cursor) {
                        cursor.moveToPosition(pictureInfo.position);
                        pictureInfo.uri = cursor.getString(dataColumn);
                    }
                    if (pictureInfo.uri == null) {
                        return;
                    }
                    synchronized (pictureCache) {
                        Bitmap cachedBitmap = pictureCache.get(pictureInfo.uri);
                        if (cachedBitmap != null) {
                            pictureInfo.thumbnail = cachedBitmap;
                            callback.onLoad(pictureInfo);
                            return;
                        }
                    }
                    int thumbSideSize = callback.getThumbnailSideSize();
                    if (thumbSideSize <= 0) {
                        thumbSideSize = 128;
                    }
                    // the next 4 variables are needed for videos, see https://stackoverflow.com/a/29555484/7987987
                    int type = cursor.getColumnIndex(MediaStore.Files.FileColumns.MEDIA_TYPE);
                    int tInt = cursor.getInt(type);
                    int colId = cursor.getColumnIndex(MediaStore.Video.VideoColumns._ID);
                    int id = cursor.getInt(colId);
                    pictureInfo.thumbnail = decodeSampledBitmapFromUri(pictureInfo.uri, thumbSideSize, thumbSideSize, tInt, id, cordova.getActivity());
                    if (pictureInfo.thumbnail != null) {
                        callback.onLoad(pictureInfo);
                        synchronized (pictureCache) {
                            pictureCache.put(pictureInfo.uri, pictureInfo.thumbnail);
                        }
                    } else {
                    }
                }
            });
        }

        private Bitmap decodeSampledBitmapFromUri(String path, int reqWidth, int reqHeight, int typeInt, int id, Context context) {
            Bitmap bm = null;
            // First decode with inJustDecodeBounds=true to check dimensions
            final BitmapFactory.Options options = new BitmapFactory.Options();
            if (typeInt == 3) {
                // this is a video, handle according to https://stackoverflow.com/a/29555484/7987987
                // using BitmapFactory options as seen in the link above
                options.inSampleSize = 4; // hardcoded for now until this works, then I'll make it dynamic
                options.inPurgeable = true;
                bm = MediaStore.Video.Thumbnails.getThumbnail(
                        context.getContentResolver(), id,
                        MediaStore.Video.Thumbnails.MINI_KIND, options);
            } else {
                // this is an image
                options.inJustDecodeBounds = true;
                BitmapFactory.decodeFile(path, options);

                // Calculate inSampleSize
                options.inSampleSize = calculateSampleSize(options, reqWidth, reqHeight);

                // Decode bitmap with inSampleSize set
                options.inJustDecodeBounds = false;
                bm = BitmapFactory.decodeFile(path, options);
            }

            return bm;
        }

        public int calculateSampleSize(BitmapFactory.Options options, int reqWidth, int reqHeight) {
            // Raw height and width of image
            final int height = options.outHeight;
            final int width = options.outWidth;
            int inSampleSize = 1;

            if (height > reqHeight || width > reqWidth) {
                if (width > height) {
                    inSampleSize = Math.round((float)height / (float)reqHeight);
                } else {
                    inSampleSize = Math.round((float)width / (float)reqWidth);
                }
            }

            return inSampleSize;
        }
    }

    public interface AsyncPictureLoader {
        void loadThumbnail(PictureInfo position, AsyncPictureLoaderCallback callback);
    }

    public interface AsyncPictureLoaderCallback {
        void onLoad(PictureInfo picture);
        int getThumbnailSideSize();
    }

    public interface StateController {
        // stuff here that controls selection
    }

    public static class PictureInfo {
        public int position;
        public String uri;
        public Bitmap thumbnail;
        public volatile boolean cancelled = false;
    }

    public static class PictureView extends FrameLayout implements AsyncPictureLoaderCallback, CompoundButton.OnCheckedChangeListener {
        private final AsyncPictureLoader loader;
        private final CheckBox checkBox;
        private final StateController stateController;
        private final MediaCache pictureCache;
        private ImageView vImage;
        private PictureInfo pictureInfo;

        public PictureView(MediaCache pictureCache, Context context, AsyncPictureLoader loader, StateController stateController) {
            super(context);
            this.pictureCache = pictureCache;
            this.loader = loader;
            this.stateController = stateController;
            LayoutInflater.from(getContext()).inflate(getResourceId(getContext(), "layout", "view_media_item"), this);
            vImage = (ImageView)findViewById(getResourceId(getContext(), "id", "vImage"));
            checkBox = (CheckBox)findViewById(getResourceId(getContext(), "id", "checkBox"));
        }

        public void load(int position) {
            if (pictureInfo != null) {
                pictureInfo.cancelled = true;
            }
            pictureInfo = new PictureInfo();
            pictureInfo.position = position;
            pictureInfo.thumbnail = null;
            pictureInfo.uri = null;
            vImage.setImageResource(0);
            vImage.setVisibility(INVISIBLE);
            loader.loadThumbnail(pictureInfo, this);

            updateSelection();
        }

        @Override
        public void onLoad(PictureInfo picture) {
            if (this.pictureInfo != picture) {
                return;
            }
            new Handler(Looper.getMainLooper()).post(new Runnable() {
                @Override
                public void run() {
                    vImage.setImageBitmap(pictureInfo.thumbnail);
                    vImage.setVisibility(VISIBLE);
                }
            });
        }
    }
}
Uche Ozoemena
  • 816
  • 2
  • 10
  • 25

0 Answers0