1
0
mirror of https://github.com/foobnix/LibreraReader.git synced 2025-10-06 00:02:43 +02:00

optimize cover loading

This commit is contained in:
Ivan Ivanenko
2023-11-28 10:11:44 +02:00
parent 6aa7892e26
commit b534aaca8a
8 changed files with 12 additions and 533 deletions

View File

@@ -101,7 +101,7 @@ android {
flavorDimensions.add("version")
productFlavors {
old {
minSdk 14
//minSdk 14
dimension "version"
applicationId "com.foobnix.pdf.reader.a1"
manifestPlaceholders = [
@@ -341,7 +341,7 @@ dependencies {
exclude group: 'org.greenrobot.greendao.rx'
}
implementation 'org.greenrobot:greendao-api:3.3.0'
implementation 'org.jsoup:jsoup:1.16.2'
implementation 'org.jsoup:jsoup:1.17.1'
implementation 'com.github.albfernandez:juniversalchardet:2.4.0'
@@ -360,9 +360,9 @@ dependencies {
implementation "commons-logging:commons-logging-api:1.1"
implementation "androidx.car.app:app:1.4.0-rc01"
implementation("androidx.media3:media3-ui:1.1.1")
implementation("androidx.media3:media3-session:1.1.1")
//implementation "androidx.car.app:app:1.4.0-rc01"
//implementation("androidx.media3:media3-ui:1.1.1")
//implementation("androidx.media3:media3-session:1.1.1")
//implementation "org.nanohttpd:nanohttpd:2.3.1"

View File

@@ -1,5 +1,5 @@
#Thu Nov 23 10:57:31 EET 2023
appVersionNumberIndex=68
#Mon Nov 27 16:00:58 EET 2023
appVersionNumberIndex=70
appDB=8
appVersionNumberBase=8.9
appCodeNumber=5698
appCodeNumber=5706

View File

@@ -22,11 +22,6 @@
<uses-permission android:name="android.permission.USE_BIOMETRIC" />
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
<uses-feature
android:name="android.hardware.type.automotive"
android:required="true" />
<queries>
<intent>
<action android:name="android.intent.action.PROCESS_TEXT" />
@@ -85,40 +80,6 @@
android:resource="@xml/paths" />
</provider>
<!--
<service
android:name="com.foobnix.car.LibreraAppService"
android:exported="true">
<intent-filter>
<action android:name="androidx.car.app.CarAppService" />
<category android:name="androidx.car.app.category.POI"/>
</intent-filter>
</service>
-->
<meta-data
android:name="com.google.android.gms.car.application"
android:resource="@xml/automotive_app_desc" />
<meta-data
android:name="androidx.car.app.minCarApiLevel"
android:value="1" />
<meta-data
android:name="androidx.car.app.TintableAttributionIcon"
android:resource="@drawable/dropbox" />
<service android:name="com.foobnix.car.MyMediaBrowserService"
android:foregroundServiceType="mediaPlayback"
android:stopWithTask="false"
android:enabled="true"
android:exported="true">
<intent-filter>
<action android:name="android.media.browse.MediaBrowserService" />
</intent-filter>
</service>
<activity
android:name="com.foobnix.zipmanager.SendReceiveActivity"
android:exported="true"

View File

@@ -32,7 +32,9 @@ public class Safe {
}
LOG.d("Safe fromLibrary", fromLibrary);
if (fromLibrary) {
action.run();
if (action != null) {
action.run();
}
return;
}

View File

@@ -1,34 +0,0 @@
package com.foobnix.car;
import android.content.Intent;
import androidx.annotation.NonNull;
import androidx.car.app.CarAppService;
import androidx.car.app.CarContext;
import androidx.car.app.Screen;
import androidx.car.app.Session;
import androidx.car.app.model.MessageTemplate;
import androidx.car.app.model.Template;
import androidx.car.app.validation.HostValidator;
public class LibreraAppService extends CarAppService {
@NonNull
@Override
public HostValidator createHostValidator() {
return HostValidator.ALLOW_ALL_HOSTS_VALIDATOR;
}
@NonNull
@Override
public Session onCreateSession() {
return new LibreraAppSession();
}
}
class LibreraAppSession extends Session {
@NonNull
@Override
public Screen onCreateScreen(@NonNull Intent intent) {
return new RecentBooksScreen(getCarContext());
}
}

View File

@@ -1,193 +0,0 @@
package com.foobnix.car;
import static android.support.v4.media.MediaBrowserCompat.MediaItem.FLAG_PLAYABLE;
import android.content.Intent;
import android.graphics.Bitmap;
import android.graphics.drawable.Drawable;
import android.net.Uri;
import android.os.Bundle;
import android.support.v4.media.MediaBrowserCompat;
import android.support.v4.media.MediaDescriptionCompat;
import android.support.v4.media.MediaMetadataCompat;
import android.support.v4.media.session.MediaSessionCompat;
import android.support.v4.media.session.PlaybackStateCompat;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.media.MediaBrowserServiceCompat;
import androidx.media.utils.MediaConstants;
import com.bumptech.glide.Glide;
import com.bumptech.glide.request.target.CustomTarget;
import com.bumptech.glide.request.transition.Transition;
import com.foobnix.LibreraApp;
import com.foobnix.android.utils.LOG;
import com.foobnix.dao2.FileMeta;
import com.foobnix.model.AppData;
import com.foobnix.pdf.info.IMG;
import com.foobnix.sys.ImageExtractor;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class MyMediaBrowserService extends MediaBrowserServiceCompat {
private static final String MY_MEDIA_ROOT_ID = "media_root_id";
Map<String, Bitmap> cache = new HashMap();
private MediaSessionCompat mediaSession;
private PlaybackStateCompat.Builder stateBuilder;
@Override
public void onCreate() {
super.onCreate();
mediaSession = new MediaSessionCompat(this, "LOG_TAG");
// Set an initial PlaybackState with ACTION_PLAY, so media buttons can start the player
stateBuilder = new PlaybackStateCompat.Builder()
.setActions(
PlaybackStateCompat.ACTION_PLAY |
PlaybackStateCompat.ACTION_PLAY_PAUSE);
mediaSession.setPlaybackState(stateBuilder.build());
// MySessionCallback() has methods that handle callbacks from a media controller
mediaSession.setCallback(new MediaSessionCompat.Callback() {
});
mediaSession.setMetadata(
new MediaMetadataCompat.Builder()
.putString(
MediaMetadataCompat.METADATA_KEY_DISPLAY_TITLE, "Song Name")
.putString(
MediaMetadataCompat.METADATA_KEY_DISPLAY_SUBTITLE, "Artist name")
.putString(
MediaMetadataCompat.METADATA_KEY_ALBUM_ART_URI,
"https://user-images.githubusercontent.com/8379914/197509105-11eaddc3-c855-4c36-9aa2-f4f9f19f907e.png")
.putLong(
MediaConstants.METADATA_KEY_IS_EXPLICIT,
MediaConstants.METADATA_VALUE_ATTRIBUTE_PRESENT)
.putLong(
MediaDescriptionCompat.EXTRA_DOWNLOAD_STATUS,
MediaDescriptionCompat.STATUS_DOWNLOADED)
.build());
// Set the session's token so that client activities can communicate with it.
setSessionToken(mediaSession.getSessionToken());
}
@Override
public void onSearch(@NonNull String query, Bundle extras, @NonNull Result<List<MediaBrowserCompat.MediaItem>> result) {
super.onSearch(query, extras, result);
LOG.d("onSearch", query);
result.detach();
MediaBrowserCompat.MediaItem item = new MediaBrowserCompat.MediaItem(
new MediaDescriptionCompat.Builder()
.setTitle("Title")
.setSubtitle("Subtitile")
//.setIconUri(Uri.parse(it.getPath()))
.setMediaUri(Uri.parse("https://download.samplelib.com/mp3/sample-9s.mp3"))
.setExtras(extras)
.build(), FLAG_PLAYABLE);
result.sendResult(Arrays.asList(item));
}
@Nullable
@Override
public BrowserRoot onGetRoot(@NonNull String s, int i, @Nullable Bundle bundle) {
Bundle b = new Bundle();
b.putBoolean(MediaConstants.BROWSER_SERVICE_EXTRAS_KEY_SEARCH_SUPPORTED, true);
b.putInt(
MediaConstants.DESCRIPTION_EXTRAS_KEY_CONTENT_STYLE_BROWSABLE,
MediaConstants.DESCRIPTION_EXTRAS_VALUE_CONTENT_STYLE_GRID_ITEM
);
b.putInt(
MediaConstants.DESCRIPTION_EXTRAS_KEY_CONTENT_STYLE_PLAYABLE,
MediaConstants.DESCRIPTION_EXTRAS_VALUE_CONTENT_STYLE_GRID_ITEM
);
return new MediaBrowserServiceCompat.BrowserRoot(MY_MEDIA_ROOT_ID, b);
}
@Override
public void onLoadChildren(@NonNull String parentId, @NonNull Result<List<MediaBrowserCompat.MediaItem>> result) {
List<MediaBrowserCompat.MediaItem> mediaItems = new ArrayList<>();
List<FileMeta> allRecent = AppData.get().getAllRecent(false);
for (FileMeta it : allRecent) {
Bitmap bitmap = cache.get(it.getPath());
if (bitmap == null) {
bitmap = ImageExtractor.messageFileBitmap("file", "demo");
}
Bundle extras = new Bundle();
extras.putString(
MediaConstants.DESCRIPTION_EXTRAS_KEY_CONTENT_STYLE_GROUP_TITLE,
"Songs");
extras.putLong(
MediaConstants.METADATA_KEY_IS_EXPLICIT,
MediaConstants.METADATA_VALUE_ATTRIBUTE_PRESENT);
extras.putInt(
MediaConstants.DESCRIPTION_EXTRAS_KEY_COMPLETION_STATUS,
MediaConstants.DESCRIPTION_EXTRAS_VALUE_COMPLETION_STATUS_PARTIALLY_PLAYED);
if (it.getIsRecentProgress() != null) {
extras.putDouble(
MediaConstants.DESCRIPTION_EXTRAS_KEY_COMPLETION_PERCENTAGE, it.getIsRecentProgress());
}
MediaBrowserCompat.MediaItem item = new MediaBrowserCompat.MediaItem(
new MediaDescriptionCompat.Builder()
.setTitle(it.getTitle())
.setMediaId(it.getPath())
.setSubtitle(it.getAuthor())
.setMediaId(it.getPath())
//.setIconUri(Uri.parse(it.getPath()))
.setMediaUri(Uri.parse("https://download.samplelib.com/mp3/sample-9s.mp3"))
.setIconBitmap(bitmap)
.setExtras(extras)
.build(), FLAG_PLAYABLE);
mediaItems.add(item);
String url = IMG.toUrl(it.getPath(), ImageExtractor.COVER_PAGE_WITH_EFFECT, IMG.getImageSize());
Glide.with(LibreraApp.context).asBitmap().load(url).into(new CustomTarget<Bitmap>() {
@Override
public void onResourceReady(@NonNull Bitmap resource, @Nullable Transition<? super Bitmap> transition) {
cache.put(it.getPath(), resource);
notifyChildrenChanged(MY_MEDIA_ROOT_ID);
}
@Override
public void onLoadCleared(Drawable placeholder) {
}
});
}
result.sendResult(mediaItems);
}
}

View File

@@ -1,257 +0,0 @@
package com.foobnix.car;
import static androidx.car.app.model.Row.IMAGE_TYPE_LARGE;
import android.graphics.Bitmap;
import android.graphics.Color;
import android.graphics.drawable.Drawable;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.OptIn;
import androidx.car.app.CarContext;
import androidx.car.app.Screen;
import androidx.car.app.annotations.ExperimentalCarApi;
import androidx.car.app.constraints.ConstraintManager;
import androidx.car.app.model.Action;
import androidx.car.app.model.ActionStrip;
import androidx.car.app.model.Badge;
import androidx.car.app.model.CarColor;
import androidx.car.app.model.CarIcon;
import androidx.car.app.model.GridItem;
import androidx.car.app.model.GridTemplate;
import androidx.car.app.model.ItemList;
import androidx.car.app.model.MessageTemplate;
import androidx.car.app.model.OnClickListener;
import androidx.car.app.model.Pane;
import androidx.car.app.model.PaneTemplate;
import androidx.car.app.model.Row;
import androidx.car.app.model.SearchTemplate;
import androidx.car.app.model.Tab;
import androidx.car.app.model.TabContents;
import androidx.car.app.model.TabTemplate;
import androidx.car.app.model.Template;
import androidx.core.graphics.drawable.IconCompat;
import androidx.lifecycle.LifecycleOwner;
import com.bumptech.glide.Glide;
import com.bumptech.glide.request.target.CustomTarget;
import com.bumptech.glide.request.transition.Transition;
import com.foobnix.LibreraApp;
import com.foobnix.android.utils.LOG;
import com.foobnix.dao2.FileMeta;
import com.foobnix.model.AppData;
import com.foobnix.model.AppSP;
import com.foobnix.model.SimpleMeta;
import com.foobnix.pdf.info.IMG;
import com.foobnix.pdf.info.R;
import com.foobnix.sys.ImageExtractor;
import com.foobnix.tts.TTSEngine;
import com.foobnix.tts.TTSService;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public final class RecentBooksScreen extends Screen {
static String TAG = "RecentBooksScreen";
boolean isPlaying = false;
FileMeta selected;
Map<String, Bitmap> cache = new HashMap();
boolean isSearchTempaate = false;
public RecentBooksScreen(@NonNull CarContext carContext) {
super(carContext);
ConstraintManager manager = getCarContext().getCarService(ConstraintManager.class);
int gridItemLimit = manager.getContentLimit(ConstraintManager.CONTENT_LIMIT_TYPE_GRID);
int listItemLimit = manager.getContentLimit(ConstraintManager.CONTENT_LIMIT_TYPE_LIST);
LOG.d(TAG,"gridItemLimit", gridItemLimit);
LOG.d(TAG,"listItemLimit", listItemLimit);
//getLifecycle().addObserver(this);
}
@OptIn(markerClass = ExperimentalCarApi.class)
@NonNull
@Override
public Template onGetTemplate() {
LOG.d(TAG, "onGetTemplate getCarAppApiLevel", getCarContext().getCarAppApiLevel());
if (isSearchTempaate) {
return new MessageTemplate.Builder("Message template")
.setTitle("Librera").build();
}
if (false) {
return new TabTemplate.Builder(new TabTemplate.TabCallback() {
@Override
public void onTabSelected(@NonNull String tabContentId) {
TabTemplate.TabCallback.super.onTabSelected(tabContentId);
}
})
.addTab(new Tab.Builder().setContentId("id1")
.setIcon(CarIcon.APP_ICON).setTitle("Hello1")
.build())
.addTab(new Tab.Builder()
.setContentId("id2").setIcon(CarIcon.APP_ICON).setTitle("Hello2").build())
.setTabContents(new TabContents.Builder(new MessageTemplate.Builder("ID1").build()).build())
.setLoading(false)
.setActiveTabContentId("id1")
.setHeaderAction(Action.APP_ICON).build();
}
if (false) {
return new PaneTemplate.Builder(
new Pane.Builder()
.addRow(new Row.Builder().setTitle("Title").addText("Hello").build())
.addRow(new Row.Builder().setTitle("title").setImage(CarIcon.APP_ICON, IMAGE_TYPE_LARGE).build())
.addAction(new Action.Builder().setIcon(CarIcon.APP_ICON).setTitle("PLAY/PAUSE").setOnClickListener(() -> {
}).build())
.addAction(new Action.Builder().setIcon(CarIcon.APP_ICON).setTitle("Next").setOnClickListener(() -> {
}).build())
.setImage(CarIcon.BACK)
.build()
)
.setTitle("Demo Pane")
.setHeaderAction(Action.BACK)
.build();
}
ItemList.Builder listBuilder = new ItemList.Builder();
List<FileMeta> allRecent = AppData.get().getAllRecent(false);
if (allRecent.size() > 5) {
allRecent = allRecent.subList(0, 5);
}
CarIcon icon = new CarIcon.Builder(IconCompat.createWithResource(getCarContext(), R.drawable.glyphicons_72_book)).build();
for (FileMeta meta : allRecent) {
Bitmap bitmap = cache.get(meta.getPath());
if (bitmap == null) {
String url = IMG.toUrl(meta.getPath(), ImageExtractor.COVER_PAGE_WITH_EFFECT, IMG.getImageSize());
Glide.with(LibreraApp.context).asBitmap().load(url).into(new CustomTarget<Bitmap>() {
@Override
public void onResourceReady(@NonNull Bitmap resource, @Nullable Transition<? super Bitmap> transition) {
cache.put(meta.getPath(), resource);
invalidate();
}
@Override
public void onLoadCleared(Drawable placeholder) {
}
});
} else {
icon = new CarIcon.Builder(IconCompat.createWithBitmap(bitmap)).build();
}
GridItem build = new GridItem.Builder()
.setTitle(meta.getTitle())
.setOnClickListener(new OnClickListener() {
@Override
public void onClick() {
selected = meta;
AppData.get().addRecent(new SimpleMeta(meta.getPath()));
TTSEngine.get().stop();
AppSP.get().lastBookPath = selected.getPath();
isPlaying = false;
invalidate();
}
})
//.addText(TxtUtils.nullToEmpty(meta.getAuthor()))
.setImage(new CarIcon.Builder(icon).build(), GridItem.IMAGE_TYPE_LARGE, new Badge.Builder().setIcon(CarIcon.PAN).build())
.build();
listBuilder.addItem(build);
}
Action playPause = new Action.Builder()
.setTitle(isPlaying ? getCarContext().getString(R.string.pause) : getCarContext().getString(R.string.play))
.setOnClickListener(
() -> {
if (TTSEngine.get().isPlaying()) {
TTSEngine.get().stop();
isPlaying = false;
} else {
TTSService.playBookPage(AppSP.get().lastBookPage, AppSP.get().lastBookPath, "", AppSP.get().lastBookWidth, AppSP.get().lastBookHeight, AppSP.get().lastFontSize, AppSP.get().lastBookTitle);
isPlaying = true;
}
invalidate();
})
.build();
return new GridTemplate.Builder()
.setItemSize(GridTemplate.ITEM_SIZE_LARGE)
.setItemImageShape(GridTemplate.ITEM_IMAGE_SHAPE_UNSET)
.setSingleList(listBuilder.build())
.setTitle(selected == null ? "Librera - Recent" : selected.getTitle())
.setHeaderAction(new Action.Builder().setIcon(getIcon(R.drawable.glyphicons_422_book_library)).build())
.setActionStrip(
new ActionStrip.Builder()
.addAction(new Action.Builder().setIcon(getIcon(R.drawable.glyphicons_600_menu)).setOnClickListener(() -> invalidate()).build())
//.addAction(playPause)
.addAction(new Action.Builder().setIcon(getIcon(R.drawable.glyphicons_28_search)).setOnClickListener(() -> {
}).build())
.build())
//.addAction(new Action.Builder().setBackgroundColor(CarColor.BLUE).setIcon(CarIcon.ALERT).build())
.build();
// Create a row with the search icon
// return new SearchTemplate.Builder(getCallBack(this))
// .setItemList(listBuilder.build()).build();
}
private SearchTemplate.SearchCallback getCallBack(Screen screen) {
return new SearchTemplate.SearchCallback() {
@Override
public void onSearchTextChanged(@NonNull String searchText) {
LOG.d(TAG, "onSearchTextChanged", searchText);
}
@Override
public void onSearchSubmitted(@NonNull String searchText) {
LOG.d(TAG, "onSearchSubmitted", searchText);
isSearchTempaate = true;
screen.invalidate();
}
};
}
public CarIcon getIcon(int id) {
return new CarIcon.Builder(IconCompat.createWithResource(getCarContext(), id)).setTint(CarColor.createCustom(Color.WHITE, Color.WHITE)).build();
}
public void onDestroy(@NonNull LifecycleOwner owner) {
LOG.d("ListTemplateDemoScreen onDestroy");
for (Bitmap b : cache.values()) {
b.recycle();
b = null;
}
cache.clear();
cache = null;
}
}

View File

@@ -55,7 +55,7 @@ dependencies {
implementation("androidx.core:core-ktx:1.12.0")
implementation("androidx.lifecycle:lifecycle-runtime-ktx:2.6.2")
implementation("androidx.activity:activity-compose:1.8.0")
implementation("androidx.activity:activity-compose:1.8.1")
implementation(platform("androidx.compose:compose-bom:2023.10.01"))
implementation("androidx.compose.material3:material3:1.1.2")