diff --git a/src/main/java/de/dennisguse/opentracks/MarkerListActivity.java b/src/main/java/de/dennisguse/opentracks/MarkerListActivity.java index b9f2d1f7c..50d1c956c 100644 --- a/src/main/java/de/dennisguse/opentracks/MarkerListActivity.java +++ b/src/main/java/de/dennisguse/opentracks/MarkerListActivity.java @@ -17,7 +17,6 @@ package de.dennisguse.opentracks; import android.app.SearchManager; -import android.content.Context; import android.content.Intent; import android.content.SharedPreferences; import android.content.SharedPreferences.OnSharedPreferenceChangeListener; @@ -30,11 +29,12 @@ import android.view.View; import androidx.annotation.NonNull; import androidx.appcompat.widget.SearchView; -import androidx.cursoradapter.widget.ResourceCursorAdapter; import androidx.loader.app.LoaderManager; import androidx.loader.content.CursorLoader; import androidx.loader.content.Loader; +import de.dennisguse.opentracks.adapters.MarkerResourceCursorAdapter; +import de.dennisguse.opentracks.adapters.ScrollVisibleViews; import de.dennisguse.opentracks.content.data.Marker; import de.dennisguse.opentracks.content.data.MarkerColumns; import de.dennisguse.opentracks.content.data.Track; @@ -44,8 +44,6 @@ import de.dennisguse.opentracks.fragments.DeleteMarkerDialogFragment; import de.dennisguse.opentracks.fragments.DeleteMarkerDialogFragment.DeleteMarkerCaller; import de.dennisguse.opentracks.util.ActivityUtils; import de.dennisguse.opentracks.util.IntentUtils; -import de.dennisguse.opentracks.util.ListItemUtils; -import de.dennisguse.opentracks.util.MarkerUtils; import de.dennisguse.opentracks.util.PreferencesUtils; /** @@ -81,7 +79,7 @@ public class MarkerListActivity extends AbstractActivity implements DeleteMarker } }; private Track track; - private ResourceCursorAdapter resourceCursorAdapter; + private MarkerResourceCursorAdapter resourceCursorAdapter; private MarkerListBinding viewBinding; @@ -123,33 +121,17 @@ public class MarkerListActivity extends AbstractActivity implements DeleteMarker viewBinding.markerList.setEmptyView(viewBinding.markerListEmpty); viewBinding.markerList.setOnItemClickListener((parent, view, position, id) -> { + resourceCursorAdapter.markerInvalid(id); Intent intent = IntentUtils.newIntent(MarkerListActivity.this, MarkerDetailActivity.class) .putExtra(MarkerDetailActivity.EXTRA_MARKER_ID, new Marker.Id(id)); startActivity(intent); }); - resourceCursorAdapter = new ResourceCursorAdapter(this, R.layout.list_item, null, 0) { - @Override - public void bindView(View view, Context context, Cursor cursor) { - int nameIndex = cursor.getColumnIndex(MarkerColumns.NAME); - int timeIndex = cursor.getColumnIndexOrThrow(MarkerColumns.TIME); - int categoryIndex = cursor.getColumnIndex(MarkerColumns.CATEGORY); - int descriptionIndex = cursor.getColumnIndex(MarkerColumns.DESCRIPTION); - int photoUrlIndex = cursor.getColumnIndex(MarkerColumns.PHOTOURL); - int iconId = MarkerUtils.ICON_ID; - String name = cursor.getString(nameIndex); - long time = cursor.getLong(timeIndex); - String category = cursor.getString(categoryIndex); - String description = cursor.getString(descriptionIndex); - String photoUrl = cursor.getString(photoUrlIndex); - - ListItemUtils.setListItem(MarkerListActivity.this, view, false, true, iconId, R.string.image_marker, name, null, null, 0, time, false, category, description, photoUrl); - } - }; + resourceCursorAdapter = new MarkerResourceCursorAdapter(this, R.layout.list_item); + ScrollVisibleViews scrollVisibleViews = new ScrollVisibleViews(resourceCursorAdapter); + viewBinding.markerList.setOnScrollListener(scrollVisibleViews); viewBinding.markerList.setAdapter(resourceCursorAdapter); ActivityUtils.configureListViewContextualMenu(viewBinding.markerList, contextualActionModeCallback); - - loadData(getIntent()); } @Override @@ -163,6 +145,7 @@ public class MarkerListActivity extends AbstractActivity implements DeleteMarker protected void onResume() { super.onResume(); this.invalidateOptionsMenu(); + loadData(getIntent()); } @Override @@ -171,6 +154,16 @@ public class MarkerListActivity extends AbstractActivity implements DeleteMarker sharedPreferences.unregisterOnSharedPreferenceChangeListener(sharedPreferenceChangeListener); } + @Override + protected void onDestroy() { + super.onDestroy(); + resourceCursorAdapter.clear(); + viewBinding = null; + resourceCursorAdapter = null; + contentProviderUtils = null; + sharedPreferences = null; + } + @Override protected View getRootView() { viewBinding = MarkerListBinding.inflate(getLayoutInflater()); @@ -237,6 +230,7 @@ public class MarkerListActivity extends AbstractActivity implements DeleteMarker if (itemId == R.id.list_context_menu_edit) { if (markerIds.length == 1) { + resourceCursorAdapter.markerInvalid(markerIds[0].getId()); Intent intent = IntentUtils.newIntent(this, MarkerEditActivity.class) .putExtra(MarkerEditActivity.EXTRA_MARKER_ID, markerIds[0]); startActivity(intent); diff --git a/src/main/java/de/dennisguse/opentracks/TrackListActivity.java b/src/main/java/de/dennisguse/opentracks/TrackListActivity.java index 655324412..7573c220b 100644 --- a/src/main/java/de/dennisguse/opentracks/TrackListActivity.java +++ b/src/main/java/de/dennisguse/opentracks/TrackListActivity.java @@ -233,7 +233,7 @@ public class TrackListActivity extends AbstractListActivity implements ConfirmDe ListItemUtils.setListItem(TrackListActivity.this, view, isRecording, recordingTrackPaused, iconId, R.string.image_track, name, totalTime, totalDistance, markerCount, - startTime, true, category, description, null); + startTime, true, category, description, false); } }; viewBinding.trackList.setAdapter(resourceCursorAdapter); diff --git a/src/main/java/de/dennisguse/opentracks/adapters/ExecutorListViewService.java b/src/main/java/de/dennisguse/opentracks/adapters/ExecutorListViewService.java new file mode 100644 index 000000000..f4ccaa4c2 --- /dev/null +++ b/src/main/java/de/dennisguse/opentracks/adapters/ExecutorListViewService.java @@ -0,0 +1,79 @@ +package de.dennisguse.opentracks.adapters; + +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.Future; + +/** + * ExecutorService wrap that avoid the execution of the same object in a ListView. + * + * @param the type of the object that will be used to identified the Runnable. + */ +class ExecutorListViewService { + + private List enqueueObjects = new ArrayList<>(); + private ExecutorService executorService; + + public ExecutorListViewService(int numThreads) { + executorService = Executors.newFixedThreadPool(numThreads); + } + + public void shutdown() { + enqueueObjects.clear(); + enqueueObjects = null; + executorService.shutdown(); + } + + /** + * Execute the runnable for the object. + * + * @param object the Object. + * @param runnable the Runnable. + */ + public void execute(T object, Runnable runnable) { + if (!preExecute(object)) { + return; + } + + new Thread(() -> { + Future future = executorService.submit(runnable); + try { + future.get(); + } catch (ExecutionException | InterruptedException e) { + e.printStackTrace(); + } + postExecute(object); + }).start(); + } + + /** + * Before execution it must checks if the object is already enqueued. + * + * @param object the object to check. + * @return true if it can be executed or false otherwise. + */ + private boolean preExecute(T object) { + synchronized (enqueueObjects) { + if (!enqueueObjects.contains(object)) { + enqueueObjects.add(object); + return true; + } else { + return false; + } + } + } + + /** + * After execution remove the object from the queue. + * + * @param object the object to be removed from the queue. + */ + private void postExecute(T object) { + synchronized (enqueueObjects) { + enqueueObjects.remove(object); + } + } +} diff --git a/src/main/java/de/dennisguse/opentracks/adapters/MarkerResourceCursorAdapter.java b/src/main/java/de/dennisguse/opentracks/adapters/MarkerResourceCursorAdapter.java new file mode 100644 index 000000000..c535e5c82 --- /dev/null +++ b/src/main/java/de/dennisguse/opentracks/adapters/MarkerResourceCursorAdapter.java @@ -0,0 +1,184 @@ +package de.dennisguse.opentracks.adapters; + +import android.app.Activity; +import android.content.Context; +import android.content.res.TypedArray; +import android.database.Cursor; +import android.graphics.Bitmap; +import android.graphics.BitmapFactory; +import android.media.ThumbnailUtils; +import android.net.Uri; +import android.util.Log; +import android.view.View; +import android.view.ViewGroup; +import android.widget.ImageView; + +import androidx.annotation.NonNull; +import androidx.collection.LruCache; +import androidx.cursoradapter.widget.ResourceCursorAdapter; + +import java.io.IOException; +import java.io.InputStream; + +import de.dennisguse.opentracks.R; +import de.dennisguse.opentracks.content.data.MarkerColumns; +import de.dennisguse.opentracks.util.ListItemUtils; +import de.dennisguse.opentracks.util.MarkerUtils; + +public class MarkerResourceCursorAdapter extends ResourceCursorAdapter implements ScrollVisibleViews.VisibleViewsListener { + + private static final String TAG = MarkerResourceCursorAdapter.class.getSimpleName(); + + private static final int LIST_PREFERRED_ITEM_HEIGHT_DEFAULT = 128; + + private final Activity activity; + + private ExecutorListViewService executorService = new ExecutorListViewService(LIST_PREFERRED_ITEM_HEIGHT_DEFAULT); + + private boolean scroll = false; + + // Cache size is in bytes. + private final LruCache memoryCache; + + public MarkerResourceCursorAdapter(Activity activity, int layout) { + super(activity, layout, null, 0); + + this.activity = activity; + + // Get max available VM memory, exceeding this amount will throw an OutOfMemory exception. + final long usedMemory = Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory(); + final int cacheSize = (int) (Runtime.getRuntime().maxMemory() - usedMemory) / 8; + + memoryCache = new LruCache(cacheSize) { + @Override + protected int sizeOf(@NonNull String key, @NonNull Bitmap bitmap) { + return bitmap.getByteCount(); + } + }; + } + + @Override + public void bindView(View view, Context context, Cursor cursor) { + int idIndex = cursor.getColumnIndex(MarkerColumns._ID); + int nameIndex = cursor.getColumnIndex(MarkerColumns.NAME); + int timeIndex = cursor.getColumnIndexOrThrow(MarkerColumns.TIME); + int categoryIndex = cursor.getColumnIndex(MarkerColumns.CATEGORY); + int descriptionIndex = cursor.getColumnIndex(MarkerColumns.DESCRIPTION); + int photoUrlIndex = cursor.getColumnIndex(MarkerColumns.PHOTOURL); + + long id = cursor.getLong(idIndex); + int iconId = MarkerUtils.ICON_ID; + String name = cursor.getString(nameIndex); + long time = cursor.getLong(timeIndex); + String category = cursor.getString(categoryIndex); + String description = cursor.getString(descriptionIndex); + String photoUrl = cursor.getString(photoUrlIndex); + + view.setTag(String.valueOf(id)); + + boolean hasPhoto = photoUrl != null && !photoUrl.equals(""); + + ImageView imageView = view.findViewById(R.id.list_item_photo); + ImageView textGradient = view.findViewById(R.id.list_item_text_gradient); + imageView.setVisibility(hasPhoto ? View.VISIBLE : View.GONE); + textGradient.setVisibility(hasPhoto ? View.VISIBLE : View.GONE); + imageView.setImageBitmap(null); + + if (hasPhoto) { + int height = getPhotoHeight(activity); + ViewGroup.LayoutParams params = imageView.getLayoutParams(); + params.height = height; + imageView.setLayoutParams(params); + + if (getBitmapFromMemCache(String.valueOf(id)) != null || !scroll) { + asyncLoadPhoto(view, imageView, photoUrl, id); + } + } + + ListItemUtils.setListItem(activity, view, false, true, iconId, R.string.image_marker, name, null, null, 0, time, false, category, description, hasPhoto); + } + + public void clear() { + executorService.shutdown(); + } + + @Override + public void onViewVisible(View view, int position) { + scroll = true; + + Cursor cursor = getCursor(); + if (!cursor.moveToPosition(position)) { + return; + } + + long id = cursor.getLong(cursor.getColumnIndex(MarkerColumns._ID)); + String photoUrl = cursor.getString(cursor.getColumnIndex(MarkerColumns.PHOTOURL)); + + boolean hasPhoto = photoUrl != null && !photoUrl.equals(""); + if (hasPhoto) { + ImageView imageView = view.findViewById(R.id.list_item_photo); + asyncLoadPhoto(view, imageView, photoUrl, id); + } + } + + public void markerInvalid(long id) { + memoryCache.remove(String.valueOf(id)); + scroll = false; + } + + private void addBitmapToMemoryCache(String key, Bitmap bitmap) { + synchronized (memoryCache) { + if (getBitmapFromMemCache(key) == null) { + memoryCache.put(key, bitmap); + } + } + } + + private Bitmap getBitmapFromMemCache(String key) { + return memoryCache.get(key); + } + + /** + * It loads the photoUrl in the imageView from view. + * It takes the photo from cache or from storage if isn't in the cache. + * + * @param view item's view. + * @param imageView view object where photo will be loaded. + * @param photoUrl photo's url. + * @param id marker's id where photo belong. + */ + private void asyncLoadPhoto(View view, ImageView imageView, String photoUrl, long id) { + Bitmap photo = getBitmapFromMemCache(String.valueOf(id)); + imageView.setImageBitmap(photo); + + if (photo == null) { + executorService.execute(id, () -> { + try (InputStream inputStream = activity.getContentResolver().openInputStream(Uri.parse(photoUrl))) { + Bitmap bitmap = BitmapFactory.decodeStream(inputStream); + int height = getPhotoHeight(activity); + Log.d(TAG, "Width : " + (bitmap.getWidth() / (bitmap.getHeight() / height)) + " | Height: " + height); + final Bitmap finalPhoto = ThumbnailUtils.extractThumbnail(bitmap, bitmap.getWidth() / (bitmap.getHeight() / height), height); + addBitmapToMemoryCache(String.valueOf(id), finalPhoto); + if (view.getTag().equals(String.valueOf(id))) { + activity.runOnUiThread(() -> imageView.setImageBitmap(finalPhoto)); + } + } catch (IOException e) { + Log.e(TAG, "Failed to image " + e); + } + }); + } + } + + /** + * Gets the photo height. + * + * @param context the context + */ + private static int getPhotoHeight(Context context) { + int[] attrs = new int[]{android.R.attr.listPreferredItemHeight}; + TypedArray typeArray = context.obtainStyledAttributes(attrs); + int height = typeArray.getDimensionPixelSize(0, LIST_PREFERRED_ITEM_HEIGHT_DEFAULT); + typeArray.recycle(); + return 2 * height; + } +} diff --git a/src/main/java/de/dennisguse/opentracks/adapters/ScrollVisibleViews.java b/src/main/java/de/dennisguse/opentracks/adapters/ScrollVisibleViews.java new file mode 100644 index 000000000..050fdc980 --- /dev/null +++ b/src/main/java/de/dennisguse/opentracks/adapters/ScrollVisibleViews.java @@ -0,0 +1,42 @@ +package de.dennisguse.opentracks.adapters; + +import android.view.View; +import android.widget.AbsListView; + +import androidx.annotation.NonNull; + +/** + * AbsListView.OnScrollListener class that can be used to know what views in a ListView are currently visible while scrolling. + */ +public class ScrollVisibleViews implements AbsListView.OnScrollListener { + private int from = -1; + private int to = -1; + + private VisibleViewsListener visibleViewsListener; + + public ScrollVisibleViews(@NonNull VisibleViewsListener visibleViewsListener) { + this.visibleViewsListener = visibleViewsListener; + } + + @Override + public void onScrollStateChanged(AbsListView view, int scrollState) { + if (scrollState == AbsListView.OnScrollListener.SCROLL_STATE_IDLE || scrollState == AbsListView.OnScrollListener.SCROLL_STATE_FLING || scrollState == AbsListView.OnScrollListener.SCROLL_STATE_TOUCH_SCROLL) { + if (from >= 0 && to >= 0) { + for (int i = from; i < to; i++) { + View viewChild = view.getChildAt(i - from); + visibleViewsListener.onViewVisible(viewChild, i); + } + } + } + } + + @Override + public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) { + from = firstVisibleItem; + to = firstVisibleItem + visibleItemCount; + } + + interface VisibleViewsListener { + void onViewVisible(View view, int position); + } +} diff --git a/src/main/java/de/dennisguse/opentracks/util/ListItemUtils.java b/src/main/java/de/dennisguse/opentracks/util/ListItemUtils.java index 6483bf4e7..853149a48 100644 --- a/src/main/java/de/dennisguse/opentracks/util/ListItemUtils.java +++ b/src/main/java/de/dennisguse/opentracks/util/ListItemUtils.java @@ -16,10 +16,9 @@ package de.dennisguse.opentracks.util; -import android.app.Activity; import android.content.Context; -import android.content.res.TypedArray; -import android.net.Uri; +import android.graphics.Bitmap; +import android.graphics.BitmapFactory; import android.text.format.DateUtils; import android.view.Gravity; import android.view.View; @@ -37,15 +36,13 @@ import de.dennisguse.opentracks.R; */ public class ListItemUtils { - private static final int LIST_PREFERRED_ITEM_HEIGHT_DEFAULT = 128; - private ListItemUtils() { } /** * Sets a list item. * - * @param activity the activity + * @param context the context * @param view the list item view * @param isRecording true if recording * @param isPaused true if paused @@ -59,23 +56,9 @@ public class ListItemUtils { * @param useRelativeTime true to display relative time if appropriate * @param category the category value * @param description the description value - * @param photoUrl the photo url + * @param hasPhoto true if this list item has photo */ - public static void setListItem(Activity activity, View view, boolean isRecording, boolean isPaused, int iconId, int iconContentDescriptionId, String name, String totalTime, String totalDistance, int markerCount, long startTime, boolean useRelativeTime, String category, String description, String photoUrl) { - // Set photo - ImageView photo = view.findViewById(R.id.list_item_photo); - ImageView textGradient = view.findViewById(R.id.list_item_text_gradient); - boolean hasPhoto = photoUrl != null && !photoUrl.equals(""); - - photo.setVisibility(hasPhoto ? View.VISIBLE : View.GONE); - textGradient.setVisibility(hasPhoto ? View.VISIBLE : View.GONE); - - if (hasPhoto) { - photo.getLayoutParams().height = getPhotoHeight(activity); - photo.setImageResource(android.R.color.transparent); - photo.setImageURI(Uri.parse(photoUrl)); - } - + public static void setListItem(Context context, View view, boolean isRecording, boolean isPaused, int iconId, int iconContentDescriptionId, String name, String totalTime, String totalDistance, int markerCount, long startTime, boolean useRelativeTime, String category, String description, boolean hasPhoto) { // Set icon if (isRecording) { iconId = isPaused ? R.drawable.ic_track_paused : R.drawable.ic_track_recording; @@ -84,27 +67,27 @@ public class ListItemUtils { ImageView iconImageView = view.findViewById(R.id.list_item_icon); iconImageView.setImageResource(iconId); - iconImageView.setContentDescription(activity.getString(iconContentDescriptionId)); + iconImageView.setContentDescription(context.getString(iconContentDescriptionId)); // Set name TextView nameTextView = view.findViewById(R.id.list_item_name); - setTextView(activity, nameTextView, name, hasPhoto); + setTextView(context, nameTextView, name, hasPhoto); // Set totalTime/totalDistance TextView timeDistanceTextView = view.findViewById(R.id.list_item_time_distance); String timeDistanceText; if (isRecording) { - timeDistanceTextView.setTextColor(activity.getResources().getColor(isPaused ? android.R.color.white : R.color.recording_text)); - timeDistanceText = activity.getString(isPaused ? R.string.generic_paused : R.string.generic_recording); + timeDistanceTextView.setTextColor(context.getResources().getColor(isPaused ? android.R.color.white : R.color.recording_text)); + timeDistanceText = context.getString(isPaused ? R.string.generic_paused : R.string.generic_recording); } else { // Match list_item_time_distance in list_item.xml - timeDistanceTextView.setTextAppearance(activity, R.style.TextSmall); + timeDistanceTextView.setTextAppearance(context, R.style.TextSmall); timeDistanceText = getTimeDistance(totalTime, totalDistance); if (markerCount > 0) { timeDistanceText += " \u2027"; } } - setTextView(activity, timeDistanceTextView, timeDistanceText, hasPhoto); + setTextView(context, timeDistanceTextView, timeDistanceText, hasPhoto); // Set markerCount ImageView markerCountIcon = view.findViewById(R.id.list_item_marker_count_icon); @@ -119,15 +102,15 @@ public class ListItemUtils { layoutParams.width = lineHeight; layoutParams.height = lineHeight; } - setTextView(activity, markerCountTextView, markerCountValue, hasPhoto); + setTextView(context, markerCountTextView, markerCountValue, hasPhoto); // Set date/time - String[] dateTime = getDateTime(isRecording, activity, startTime, useRelativeTime); + String[] dateTime = getDateTime(isRecording, context, startTime, useRelativeTime); TextView dateTextView = view.findViewById(R.id.list_item_date); - setTextView(activity, dateTextView, dateTime[0], hasPhoto); + setTextView(context, dateTextView, dateTime[0], hasPhoto); TextView timeTextView = view.findViewById(R.id.list_item_time); - setTextView(activity, timeTextView, dateTime[1], hasPhoto); + setTextView(context, timeTextView, dateTime[1], hasPhoto); // Set category and description TextView categoryDescriptionTextView = view.findViewById(R.id.list_item_category_description); @@ -135,15 +118,15 @@ public class ListItemUtils { // Place categoryDescription in either ownerTimeDistanceTextView or categoryDescriptionTextView if (timeDistanceTextView.getVisibility() == View.GONE && markerCountIcon.getVisibility() == View.GONE) { - setTextView(activity, categoryDescriptionTextView, null, hasPhoto); + setTextView(context, categoryDescriptionTextView, null, hasPhoto); // Match list_item_category_description in list_item.xml timeDistanceTextView.setSingleLine(false); timeDistanceTextView.setMaxLines(2); - setTextView(activity, timeDistanceTextView, categoryDescription, hasPhoto); + setTextView(context, timeDistanceTextView, categoryDescription, hasPhoto); } else { // Match list_item_time_distance in list_item.xml timeDistanceTextView.setSingleLine(true); - setTextView(activity, categoryDescriptionTextView, categoryDescription, hasPhoto); + setTextView(context, categoryDescriptionTextView, categoryDescription, hasPhoto); } // Adjust iconImageView layout gravity @@ -151,6 +134,41 @@ public class ListItemUtils { params.gravity = timeDistanceTextView.getVisibility() == View.GONE && markerCountIcon.getVisibility() == View.GONE ? Gravity.TOP : Gravity.CENTER_VERTICAL; } + private static Bitmap decodeSampledBitmapFromResource(String photoUrl, int reqWidth, int reqHeight) { + // First decode with inJustDecodeBounds=true to check dimensions + final BitmapFactory.Options options = new BitmapFactory.Options(); + options.inJustDecodeBounds = true; + BitmapFactory.decodeFile(photoUrl, options); + + // Calculate inSampleSize + options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight); + + // Decode bitmap with inSampleSize set + options.inJustDecodeBounds = false; + return BitmapFactory.decodeFile(photoUrl, options); + } + + private static int calculateInSampleSize(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) { + + final int halfHeight = height / 2; + final int halfWidth = width / 2; + + // Calculate the largest inSampleSize value that is a power of 2 and keeps both height and width larger than the requested height and width. + while ((halfHeight / inSampleSize) >= reqHeight + && (halfWidth / inSampleSize) >= reqWidth) { + inSampleSize *= 2; + } + } + + return inSampleSize; + } + /** * Gets a string for share owner, total time, and total distance. * @@ -220,17 +238,4 @@ public class ListItemUtils { textView.setShadowLayer(0, 0, 0, 0); } } - - /** - * Gets the photo height. - * - * @param context the context - */ - private static int getPhotoHeight(Context context) { - int[] attrs = new int[]{android.R.attr.listPreferredItemHeight}; - TypedArray typeArray = context.obtainStyledAttributes(attrs); - int height = typeArray.getDimensionPixelSize(0, LIST_PREFERRED_ITEM_HEIGHT_DEFAULT); - typeArray.recycle(); - return 2 * height; - } } diff --git a/src/main/res/layout/list_item.xml b/src/main/res/layout/list_item.xml index c0028cb64..344a00f99 100644 --- a/src/main/res/layout/list_item.xml +++ b/src/main/res/layout/list_item.xml @@ -36,6 +36,8 @@ limitations under the License. android:layout_width="match_parent" android:layout_height="match_parent" android:alpha=".6" + android:visibility="gone" + tools:visibility="visible" android:contentDescription="@string/image_photo_marker" android:src="@drawable/text_gradient" />