diff --git a/.github/workflows/android-build.yml b/.github/workflows/android-build.yml new file mode 100644 index 00000000..6466f798 --- /dev/null +++ b/.github/workflows/android-build.yml @@ -0,0 +1,45 @@ +name: Android Build CI + +on: + push: + branches: + - '*' + - '!master' + - '!release/*' +concurrency: + group: build-${{ github.event.pull_request.number || github.ref }} + cancel-in-progress: true + +jobs: + check_spotless: + name: Check spotless + runs-on: ubuntu-20.04 + steps: + - uses: actions/checkout@v3 + - name: Set up JDK 11 + uses: actions/setup-java@v3 + with: + distribution: "temurin" + java-version: 11 + - name: Check formatting using spotless + uses: gradle/gradle-build-action@v2.4.2 + with: + arguments: spotlessCheck + build: + name: Build debug + runs-on: ubuntu-20.04 + steps: + - uses: actions/checkout@v3 + with: + fetch-depth: 0 + - name: Set up JDK 11 + uses: actions/setup-java@v3 + with: + distribution: "temurin" + java-version: 11 + - name: Build with Gradle + uses: gradle/gradle-build-action@v2.4.2 + with: + arguments: assembledebug --stacktrace + env: + TZ: UTC \ No newline at end of file diff --git a/.github/workflows/android-main.yml b/.github/workflows/android-main.yml index b46834d8..83e81f71 100644 --- a/.github/workflows/android-main.yml +++ b/.github/workflows/android-main.yml @@ -40,7 +40,7 @@ jobs: - name: Build with Gradle uses: gradle/gradle-build-action@v2.4.2 with: - arguments: assembledebug + arguments: assembledebug --stacktrace env: TZ: UTC - name: Publish on Telegram diff --git a/app/build.gradle b/app/build.gradle index b61da4d4..3f2358d0 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -2,7 +2,7 @@ buildscript { ext { jacocoVersion = "0.8.7" androidXLifecycleVersion = "2.3.1" - glideVersion = '4.11.0' + glideVersion = '4.16.0' fragment_version = "1.3.6" roomVersion = "2.4.0" billing_version = "6.0.1" @@ -287,7 +287,7 @@ dependencies { api "com.caverock:androidsvg:1.4" implementation 'com.burhanrashid52:photoeditor:2.0.0' implementation 'com.github.CanHub:Android-Image-Cropper:4.3.1' - api 'com.github.TeamAmaze:mupdf-android-viewer:1.0.26' + api 'com.github.TeamAmaze:mupdf-android-viewer:1.0.27' implementation "androidx.documentfile:documentfile:1.0.1" implementation 'id.zelory:compressor:3.0.1' implementation('com.github.AbedElazizShe:LightCompressor:1.3.1') { diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index c43662cd..278da6d2 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -57,7 +57,6 @@ android:theme="@style/Theme.AmazeFileUtilities" android:screenOrientation="portrait" tools:ignore="LockedOrientationActivity" - android:launchMode="singleInstance" > @@ -96,7 +95,6 @@ android:name=".image_viewer.ImageViewerActivity" android:label="@string/image_viewer" android:theme="@style/Theme.AmazeFileUtilities.FullScreen.Dark" - android:launchMode="singleInstance" android:exported="true" > @@ -198,7 +194,7 @@ android:exported="true" android:label="@string/docx_viewer" android:theme="@style/Theme.AmazeFileUtilities" - android:launchMode="singleInstance"> + > @@ -214,7 +210,7 @@ android:name=".home_page.ui.options.AboutActivity" android:exported="true" android:theme="@style/Custom.Dialog.Dark" - android:launchMode="singleInstance"> + > ?, + target: Target, isFirstResource: Boolean ): Boolean { // do nothing @@ -274,15 +274,15 @@ interface IAudioPlayerInterfaceHandler : OnPlaybackInfoUpdate, LifecycleOwner { } override fun onResourceReady( - resource: Drawable?, - model: Any?, + resource: Drawable, + model: Any, target: Target?, - dataSource: DataSource?, + dataSource: DataSource, isFirstResource: Boolean ): Boolean { getContextWeakRef().get()?.let { context -> - resource?.let { + resource.let { getAudioPlayerHandlerViewModel().getPaletteColor( it, context.getColor(R.color.navy_blue_alt_3) diff --git a/app/src/main/java/com/amaze/fileutilities/home_page/ui/analyse/AnalyseFragment.kt b/app/src/main/java/com/amaze/fileutilities/home_page/ui/analyse/AnalyseFragment.kt index 6cc9c5ee..47021587 100644 --- a/app/src/main/java/com/amaze/fileutilities/home_page/ui/analyse/AnalyseFragment.kt +++ b/app/src/main/java/com/amaze/fileutilities/home_page/ui/analyse/AnalyseFragment.kt @@ -22,6 +22,7 @@ package com.amaze.fileutilities.home_page.ui.analyse import android.content.Intent import android.content.SharedPreferences +import android.net.Uri import android.os.Build import android.os.Bundle import android.os.Handler @@ -482,6 +483,7 @@ class AnalyseFragment : AbstractMediaFileInfoOperationsFragment() { if (!isUsageStatsPermissionGranted()) { unusedAppsPreview.loadRequireElevatedPermission({ val intent = Intent(Settings.ACTION_USAGE_ACCESS_SETTINGS) + intent.data = Uri.parse("package:${requireActivity().packageName}") startActivity(intent) }, { reloadFragment() @@ -508,12 +510,14 @@ class AnalyseFragment : AbstractMediaFileInfoOperationsFragment() { if (!isUsageStatsPermissionGranted()) { mostUsedAppsPreview.loadRequireElevatedPermission({ val intent = Intent(Settings.ACTION_USAGE_ACCESS_SETTINGS) + intent.data = Uri.parse("package:${requireActivity().packageName}") startActivity(intent) }, { reloadFragment() }) leastUsedAppsPreview.loadRequireElevatedPermission({ val intent = Intent(Settings.ACTION_USAGE_ACCESS_SETTINGS) + intent.data = Uri.parse("package:${requireActivity().packageName}") startActivity(intent) }, { reloadFragment() @@ -556,6 +560,7 @@ class AnalyseFragment : AbstractMediaFileInfoOperationsFragment() { ) { networkIntensiveAppsPreview.loadRequireElevatedPermission({ val intent = Intent(Settings.ACTION_USAGE_ACCESS_SETTINGS) + intent.data = Uri.parse("package:${requireActivity().packageName}") startActivity(intent) }, { reloadFragment() diff --git a/app/src/main/java/com/amaze/fileutilities/home_page/ui/analyse/ReviewAnalysisAdapter.kt b/app/src/main/java/com/amaze/fileutilities/home_page/ui/analyse/ReviewAnalysisAdapter.kt index b9fbea90..17f8c102 100644 --- a/app/src/main/java/com/amaze/fileutilities/home_page/ui/analyse/ReviewAnalysisAdapter.kt +++ b/app/src/main/java/com/amaze/fileutilities/home_page/ui/analyse/ReviewAnalysisAdapter.kt @@ -35,7 +35,7 @@ import java.lang.ref.WeakReference class ReviewAnalysisAdapter( val context: Context, val analysisType: Int?, - private val preloader: MediaAdapterPreloader, + private val preloader: MediaAdapterPreloader, private val mediaFileInfoList: MutableList, toggleCheckCallback: ( checkedSize: Int, @@ -55,10 +55,10 @@ class ReviewAnalysisAdapter( value.clear() for (i in mediaFileInfoList.indices) { value.add(ListItem(mediaFileInfo = mediaFileInfoList[i])) - preloader.addItem(mediaFileInfoList[i].path) + preloader.addItem(mediaFileInfoList[i]) } if (mediaFileInfoList.size != 0) { - preloader.addItem("") + preloader.addItem(null) value.add(ListItem(EMPTY_LAST_ITEM)) } field = value diff --git a/app/src/main/java/com/amaze/fileutilities/home_page/ui/analyse/ReviewImagesFragment.kt b/app/src/main/java/com/amaze/fileutilities/home_page/ui/analyse/ReviewImagesFragment.kt index 0e6b6743..70c022a3 100644 --- a/app/src/main/java/com/amaze/fileutilities/home_page/ui/analyse/ReviewImagesFragment.kt +++ b/app/src/main/java/com/amaze/fileutilities/home_page/ui/analyse/ReviewImagesFragment.kt @@ -68,8 +68,8 @@ class ReviewImagesFragment : ItemsActionBarFragment() { private lateinit var viewModel: AnalyseViewModel private var gridLayoutManager: GridLayoutManager? = GridLayoutManager(context, 3) private var mediaFileAdapter: ReviewAnalysisAdapter? = null - private var preloader: MediaAdapterPreloader? = null - private var recyclerViewPreloader: RecyclerViewPreloader? = null + private var preloader: MediaAdapterPreloader? = null + private var recyclerViewPreloader: RecyclerViewPreloader? = null private val MAX_PRELOAD = 50 private var analysisType: Int? = null @@ -146,9 +146,10 @@ class ReviewImagesFragment : ItemsActionBarFragment() { // set list adapter preloader = MediaAdapterPreloader( requireContext(), - R.drawable.ic_outline_insert_drive_file_32 + R.drawable.ic_outline_insert_drive_file_32, + true ) - val sizeProvider = ViewPreloadSizeProvider() + val sizeProvider = ViewPreloadSizeProvider() recyclerViewPreloader = RecyclerViewPreloader( Glide.with(requireActivity()), preloader!!, diff --git a/app/src/main/java/com/amaze/fileutilities/home_page/ui/files/AbstractMediaInfoListFragment.kt b/app/src/main/java/com/amaze/fileutilities/home_page/ui/files/AbstractMediaInfoListFragment.kt index e64faba2..e53d8a12 100644 --- a/app/src/main/java/com/amaze/fileutilities/home_page/ui/files/AbstractMediaInfoListFragment.kt +++ b/app/src/main/java/com/amaze/fileutilities/home_page/ui/files/AbstractMediaInfoListFragment.kt @@ -53,9 +53,10 @@ abstract class AbstractMediaInfoListFragment : private var gridLayoutManager: GridLayoutManager? = null private val MAX_PRELOAD = 100 private var log: Logger = LoggerFactory.getLogger(AbstractMediaInfoListFragment::class.java) + private var mediaPreloader: MediaAdapterPreloader? = null override fun onDestroyView() { - getMediaAdapterPreloader().clear() + mediaPreloader?.clear() super.onDestroyView() } @@ -83,7 +84,7 @@ abstract class AbstractMediaInfoListFragment : abstract fun getFileStorageSummaryAndMediaFileInfoPair(): Pair?>? - abstract fun getMediaAdapterPreloader(): MediaAdapterPreloader + abstract fun getMediaAdapterPreloader(isGrid: Boolean): MediaAdapterPreloader abstract fun getRecyclerView(): RecyclerView @@ -116,21 +117,22 @@ abstract class AbstractMediaInfoListFragment : storageSummary.totalUsedSpace!! ) // set list adapter - val sizeProvider = ViewPreloadSizeProvider() - val recyclerViewPreloader = RecyclerViewPreloader( - Glide.with(requireActivity()), - getMediaAdapterPreloader(), - sizeProvider, - MAX_PRELOAD - ) + val sizeProvider = ViewPreloadSizeProvider() val isList = requireContext() .getAppCommonSharedPreferences().getBoolean( "${getMediaListType()}_${PreferencesConstants.KEY_MEDIA_LIST_TYPE}", PreferencesConstants.DEFAULT_MEDIA_LIST_TYPE ) + mediaPreloader = getMediaAdapterPreloader(!isList) + val recyclerViewPreloader = RecyclerViewPreloader( + Glide.with(requireActivity()), + mediaPreloader!!, + sizeProvider, + MAX_PRELOAD + ) mediaFileAdapter = MediaFileAdapter( requireActivity(), - getMediaAdapterPreloader(), + mediaPreloader!!, this@AbstractMediaInfoListFragment, !isList, MediaFileListSorter.SortingPreference.newInstance( requireContext() diff --git a/app/src/main/java/com/amaze/fileutilities/home_page/ui/files/AudiosListFragment.kt b/app/src/main/java/com/amaze/fileutilities/home_page/ui/files/AudiosListFragment.kt index 44eb8e3b..94951a5a 100644 --- a/app/src/main/java/com/amaze/fileutilities/home_page/ui/files/AudiosListFragment.kt +++ b/app/src/main/java/com/amaze/fileutilities/home_page/ui/files/AudiosListFragment.kt @@ -72,8 +72,8 @@ class AudiosListFragment : AbstractMediaInfoListFragment(), IAudioPlayerInterfac Pair?>? = null private lateinit var audioPlaybackServiceConnection: AudioPlaybackServiceConnection - private var filesPreloader: MediaAdapterPreloader? = null - private var playlistsPreloader: MediaAdapterPreloader? = null + private var filesPreloader: MediaAdapterPreloader? = null + private var playlistsPreloader: MediaAdapterPreloader? = null private var isWaveformProcessing = false private var lastPaletteColor: Int = 0 private var lastLyrics: String? = null @@ -368,7 +368,7 @@ class AudiosListFragment : AbstractMediaInfoListFragment(), IAudioPlayerInterfac } } - override fun getMediaAdapterPreloader(): MediaAdapterPreloader { + override fun getMediaAdapterPreloader(isGrid: Boolean): MediaAdapterPreloader { val sharedPrefs = requireContext().getAppCommonSharedPreferences() val groupByPref = sharedPrefs.getInt( MediaFileListSorter.SortingPreference.getGroupByKey(MediaFileAdapter.MEDIA_TYPE_AUDIO), @@ -378,7 +378,8 @@ class AudiosListFragment : AbstractMediaInfoListFragment(), IAudioPlayerInterfac if (filesPreloader == null) { filesPreloader = MediaAdapterPreloader( requireContext(), - R.drawable.ic_outline_audio_file_32 + R.drawable.ic_outline_audio_file_32, + isGrid ) } return filesPreloader!! @@ -386,7 +387,8 @@ class AudiosListFragment : AbstractMediaInfoListFragment(), IAudioPlayerInterfac if (playlistsPreloader == null) { playlistsPreloader = MediaAdapterPreloader( requireContext(), - R.drawable.ic_outline_audio_file_32 + R.drawable.ic_outline_audio_file_32, + isGrid ) } return playlistsPreloader!! diff --git a/app/src/main/java/com/amaze/fileutilities/home_page/ui/files/DocumentsListFragment.kt b/app/src/main/java/com/amaze/fileutilities/home_page/ui/files/DocumentsListFragment.kt index db2ea5c2..23082284 100644 --- a/app/src/main/java/com/amaze/fileutilities/home_page/ui/files/DocumentsListFragment.kt +++ b/app/src/main/java/com/amaze/fileutilities/home_page/ui/files/DocumentsListFragment.kt @@ -47,7 +47,7 @@ class DocumentsListFragment : AbstractMediaInfoListFragment() { // This property is only valid between onCreateView and // onDestroyView. private val binding get() = _binding!! - private var preloader: MediaAdapterPreloader? = null + private var preloader: MediaAdapterPreloader? = null override fun onCreateView( inflater: LayoutInflater, @@ -79,11 +79,12 @@ class DocumentsListFragment : AbstractMediaInfoListFragment() { fileStorageSummaryAndMediaFileInfo else null } - override fun getMediaAdapterPreloader(): MediaAdapterPreloader { + override fun getMediaAdapterPreloader(isGrid: Boolean): MediaAdapterPreloader { if (preloader == null) { preloader = MediaAdapterPreloader( requireContext(), - R.drawable.ic_outline_insert_drive_file_32 + R.drawable.ic_outline_insert_drive_file_32, + isGrid ) } return preloader!! diff --git a/app/src/main/java/com/amaze/fileutilities/home_page/ui/files/FilesFragment.kt b/app/src/main/java/com/amaze/fileutilities/home_page/ui/files/FilesFragment.kt index d2d6ec5f..7410d8fc 100644 --- a/app/src/main/java/com/amaze/fileutilities/home_page/ui/files/FilesFragment.kt +++ b/app/src/main/java/com/amaze/fileutilities/home_page/ui/files/FilesFragment.kt @@ -25,8 +25,6 @@ import android.os.Bundle import android.view.LayoutInflater import android.view.View import android.view.ViewGroup -import android.view.animation.Animation -import android.view.animation.AnimationUtils import androidx.core.graphics.ColorUtils import androidx.fragment.app.Fragment import androidx.fragment.app.activityViewModels @@ -52,8 +50,8 @@ class FilesFragment : ItemsActionBarFragment() { private val filesViewModel: FilesViewModel by activityViewModels() private var _binding: FragmentFilesBinding? = null private var mediaFileAdapter: RecentMediaFilesAdapter? = null - private var preloader: MediaAdapterPreloader? = null - private var recyclerViewPreloader: RecyclerViewPreloader? = null + private var preloader: MediaAdapterPreloader? = null + private var recyclerViewPreloader: RecyclerViewPreloader? = null private var linearLayoutManager: LinearLayoutManager? = null private val MAX_PRELOAD = 100 @@ -297,9 +295,10 @@ class FilesFragment : ItemsActionBarFragment() { } preloader = MediaAdapterPreloader( applicationContext, - R.drawable.ic_outline_insert_drive_file_32 + R.drawable.ic_outline_insert_drive_file_32, + false ) - val sizeProvider = ViewPreloadSizeProvider() + val sizeProvider = ViewPreloadSizeProvider() recyclerViewPreloader = RecyclerViewPreloader( Glide.with(applicationContext), preloader!!, @@ -370,10 +369,6 @@ class FilesFragment : ItemsActionBarFragment() { } binding.storagePercent.setAdaptiveColorProvider(colorProvider) - val slideUpAnimation: Animation = - AnimationUtils.loadAnimation(requireContext(), R.anim.slide_up_fade_in) - binding.listFragmentParent.startAnimation(slideUpAnimation) - binding.listFragmentParent.visibility = View.VISIBLE return root } diff --git a/app/src/main/java/com/amaze/fileutilities/home_page/ui/files/FilesViewModel.kt b/app/src/main/java/com/amaze/fileutilities/home_page/ui/files/FilesViewModel.kt index 66d400c5..5cd37124 100644 --- a/app/src/main/java/com/amaze/fileutilities/home_page/ui/files/FilesViewModel.kt +++ b/app/src/main/java/com/amaze/fileutilities/home_page/ui/files/FilesViewModel.kt @@ -657,6 +657,22 @@ class FilesViewModel(val applicationContext: Application) : } } + /** + * We find histogram for a given image, divide it in 256/8 windows. + * For each window of this histogram, we find the red, green and blue channel peaks individually, along with it's position for a given window. + * All these data points are then saved in database table for all images. + * This process starts as soon as app starts and keeps running in background (ofcourse there are multiple optimisations done so as to not fill up RAM, for all the background running analysis in the app). + * Then once we have all the histograms data (all analysis resumes from last point in case user closes the app, or users take new images till the time we have processed all the images), so however multiple runs the app takes, once we have all the histogram data, it's time to figure out the similar images. + * To figure out the similar images, we compare the peaks of the rgb channel of each window, once we have x number of windows that matches, say 6 out of 8 windows, we can say that image may be similar (the number was decided based on multiple tests in real life). + * In order to make results more realistic, we also compare the position of peak along with the peak rgb channel for a given window. Ofcourse, you can't have the exact same pixel position for a peak, so a normalization was also done. + * Finally, when we have a matching image that satisfies above criteria, we take a checksum of all the rgb channel windows along with it's parent path (this is done so that we only match similar images in the same directory, as chances are, similar images will be in the same directory and helps to minimize false positives), and then save checksum value of all the images in the table. + * This second part of the analysis is also done in the background. + * So till here we have figured out the similar images and can uniquely identify them from their checksum, so that we don't have to repeat the second step at the runtime. + * Finally when a user opens the analysis section of the app, all we have to do is to group images by same checksum and list them to the user. + * Ofcourse there may be some false positives in this (or any other) analysis algorithm in the app, so we already have a feature where all user have to do is select the analysis result, and press the thumbs down button at top of the screen, and then that analysis will be marked false positive in the database and will not be shown to user again. + * Phew, this was something, I had to write this down so that I don't forget in future again instead of going through the code. + * I know there are multiple algorithms online, too complicated for me to understand and write from scratch in java (they're native functions in python). So I had to resort to write on my own. + */ fun analyseSimilarImages( mediaFileInfoList: List, pathPreferencesList: List @@ -1956,6 +1972,12 @@ class FilesViewModel(val applicationContext: Application) : } } + /** + * installedAppsDao contains all the apps installed at a given instance, the list gets updated each time we fetch all apps. + * We fetch list of installed apps right now here again, and compare with the installedAppsDao list + * apps installed right now will be a subset of installedAppsDao + * We find the difference first, then for apps that aren't installed now, we check to see if there is any data directory present or not. + */ fun getJunkFilesLiveData(): LiveData, String>?> { if (junkFilesLiveData == null) { junkFilesLiveData = MutableLiveData() @@ -1969,38 +1991,39 @@ class FilesViewModel(val applicationContext: Application) : viewModelScope.launch(Dispatchers.IO) { loadAllInstalledApps(packageManager) val dao = AppDatabase.getInstance(applicationContext).installedAppsDao() - val savedInstalledApps = dao.findAll().filter { - savedAppData -> - allApps.get()?.any { - !it.first.packageName.equals(savedAppData.packageName) - } == false - } - log.info("found following apps not installed {}", savedInstalledApps) - val result = ArrayList() - savedInstalledApps.forEach { - savedApp -> - savedApp.dataDirs.forEach { - result.add( - MediaFileInfo.fromFile( - File(it), - MediaFileInfo.ExtraInfo( - MediaFileInfo.MEDIA_TYPE_UNKNOWN, - null, null, null + val savedInstalledApps = dao.findAll() + allApps.get()?.map { it.first.packageName }?.toSet()?.let { + installedAppsPackageNames -> + val difference = savedInstalledApps.filter { + it.packageName !in installedAppsPackageNames + } + log.info("found following apps not installed {}", difference) + val result = ArrayList() + difference.forEach { + savedApp -> + savedApp.dataDirs.forEach { + result.add( + MediaFileInfo.fromFile( + File(it), + MediaFileInfo.ExtraInfo( + MediaFileInfo.MEDIA_TYPE_UNKNOWN, + null, null, null + ) ) ) - ) + } } - } - var size = 0L - result.forEach { - size += it.longSize - } - junkFilesLiveData?.postValue( - Pair( - result, - Formatter.formatFileSize(applicationContext, size) + var size = 0L + result.forEach { + size += it.longSize + } + junkFilesLiveData?.postValue( + Pair( + result, + Formatter.formatFileSize(applicationContext, size) + ) ) - ) + } } } @@ -2399,7 +2422,13 @@ class FilesViewModel(val applicationContext: Application) : allApps.get()?.let { infoListPair -> val installedApps = infoListPair.map { - InstalledApps(it.first.packageName, listOf(it.first.sourceDir, it.first.dataDir)) + InstalledApps( + it.first.packageName, + listOf( + it.first.sourceDir, + it.first.dataDir, it.first.publicSourceDir + ) + ) } val dao = AppDatabase.getInstance(applicationContext).installedAppsDao() dao.insert(installedApps) diff --git a/app/src/main/java/com/amaze/fileutilities/home_page/ui/files/ImagesListFragment.kt b/app/src/main/java/com/amaze/fileutilities/home_page/ui/files/ImagesListFragment.kt index c425ab22..9798da8b 100644 --- a/app/src/main/java/com/amaze/fileutilities/home_page/ui/files/ImagesListFragment.kt +++ b/app/src/main/java/com/amaze/fileutilities/home_page/ui/files/ImagesListFragment.kt @@ -47,7 +47,7 @@ class ImagesListFragment : AbstractMediaInfoListFragment() { // This property is only valid between onCreateView and // onDestroyView. private val binding get() = _binding!! - private var preloader: MediaAdapterPreloader? = null + private var preloader: MediaAdapterPreloader? = null override fun onCreateView( inflater: LayoutInflater, @@ -79,11 +79,12 @@ class ImagesListFragment : AbstractMediaInfoListFragment() { fileStorageSummaryAndMediaFileInfo else null } - override fun getMediaAdapterPreloader(): MediaAdapterPreloader { + override fun getMediaAdapterPreloader(isGrid: Boolean): MediaAdapterPreloader { if (preloader == null) { preloader = MediaAdapterPreloader( requireContext(), - R.drawable.ic_outline_image_32 + R.drawable.ic_outline_image_32, + isGrid ) } return preloader!! diff --git a/app/src/main/java/com/amaze/fileutilities/home_page/ui/files/MediaAdapterPreloader.kt b/app/src/main/java/com/amaze/fileutilities/home_page/ui/files/MediaAdapterPreloader.kt index f4169228..a1de5cb1 100644 --- a/app/src/main/java/com/amaze/fileutilities/home_page/ui/files/MediaAdapterPreloader.kt +++ b/app/src/main/java/com/amaze/fileutilities/home_page/ui/files/MediaAdapterPreloader.kt @@ -30,6 +30,7 @@ import com.bumptech.glide.Glide import com.bumptech.glide.ListPreloader.PreloadModelProvider import com.bumptech.glide.RequestBuilder import com.bumptech.glide.load.DataSource +import com.bumptech.glide.load.engine.DiskCacheStrategy import com.bumptech.glide.load.engine.GlideException import com.bumptech.glide.load.resource.bitmap.CenterCrop import com.bumptech.glide.load.resource.bitmap.GranularRoundedCorners @@ -37,48 +38,44 @@ import com.bumptech.glide.request.RequestListener import com.bumptech.glide.request.RequestOptions import com.bumptech.glide.request.target.Target -class MediaAdapterPreloader(private val context: Context, private val loadingDrawable: Int) : - PreloadModelProvider { - private var request: RequestBuilder = Glide.with(context).asDrawable().fitCenter() - private var items: MutableList? = null +class MediaAdapterPreloader( + private val context: Context, + private val loadingDrawable: Int, + private val isGrid: Boolean +) : + PreloadModelProvider { + private var request: RequestBuilder = initRequestBuilder() + private var items: MutableList? = null - fun addItem(item: String) { + fun addItem(item: T?) { if (items == null) { items = arrayListOf() } items!!.add(item) } + // be sure to call clear once you're done with this, to avoid memory leaks fun clear() { items?.clear() } - override fun getPreloadItems(position: Int): List { - if (items == null) return emptyList() + override fun getPreloadItems(position: Int): List { + if (items == null || items!![position] == null) return emptyList() return listOf(items!![position]) } - override fun getPreloadRequestBuilder(item: String): RequestBuilder<*> { - return request.clone().fallback(R.drawable.ic_outline_broken_image_24) - .placeholder(loadingDrawable).load(item) + override fun getPreloadRequestBuilder(item: T): RequestBuilder<*> { +// return request.clone().fallback(R.drawable.ic_outline_broken_image_24) +// .placeholder(loadingDrawable).load(item) +// return request.load(item) + return getReadyRequestBuilder(item) } - fun loadImage(item: MediaFileInfo, view: ImageView, isGrid: Boolean) { - val toLoadPath: String = item.path - val toLoadBitmap: Bitmap? = item.extraInfo?.audioMetaData?.albumArt - val toLoadDrawable = item.extraInfo?.apkMetaData?.drawable - var transformedRequest = request.fallback(R.drawable.ic_outline_broken_image_24) - .placeholder(loadingDrawable).load(toLoadDrawable ?: toLoadBitmap ?: toLoadPath) - if (toLoadBitmap == null) { - // apply size constraint when we don't have bitmap, as bitmap already is resized see CursorUtils - transformedRequest = transformedRequest - .apply( - RequestOptions().override( - if (isGrid) 500 else 100, - if (isGrid) 500 else 100 - ) - ) - } + private fun initRequestBuilder(): RequestBuilder { + var transformedRequest = Glide.with(context).asDrawable() + .diskCacheStrategy(DiskCacheStrategy.AUTOMATIC) + .fallback(R.drawable.ic_outline_broken_image_24) + .placeholder(loadingDrawable).fitCenter() transformedRequest = if (isGrid) { transformedRequest.centerCrop() .transform(CenterCrop(), GranularRoundedCorners(24.px, 24.px, 0f, 0f)) @@ -91,25 +88,54 @@ class MediaAdapterPreloader(private val context: Context, private val loadingDra override fun onLoadFailed( e: GlideException?, model: Any?, - target: Target?, + target: Target, isFirstResource: Boolean ): Boolean { if (isGrid) { - view.setPadding(16.px.toInt(), 16.px.toInt(), 16.px.toInt(), 16.px.toInt()) + // fallback drawable in case image fails to load. + // we add padding because svg placeholder is too big and looks ugly. +// view.setPadding(16.px.toInt(), 16.px.toInt(), 16.px.toInt(), 16.px.toInt()) } return false } override fun onResourceReady( - resource: Drawable?, - model: Any?, + resource: Drawable, + model: Any, target: Target?, - dataSource: DataSource?, + dataSource: DataSource, isFirstResource: Boolean ): Boolean { // do nothing return false } - }).into(view) + }) + return transformedRequest + } + + private fun getReadyRequestBuilder(item: MediaFileInfo): RequestBuilder { + var transformedRequest = request.load(getLoadingModel(item)) + if (item.extraInfo?.audioMetaData?.albumArt == null) { + // apply size constraint when we don't have bitmap, as bitmap already is resized see CursorUtils + transformedRequest = transformedRequest + .apply( + RequestOptions().override( + if (isGrid) 250 else 100, + if (isGrid) 250 else 100 + ).diskCacheStrategy(DiskCacheStrategy.AUTOMATIC) + ) + } + return transformedRequest + } + + private fun getLoadingModel(mediaFileInfo: MediaFileInfo): Any { + val toLoadPath: String = mediaFileInfo.path + val toLoadBitmap: Bitmap? = mediaFileInfo.extraInfo?.audioMetaData?.albumArt + val toLoadDrawable = mediaFileInfo.extraInfo?.apkMetaData?.drawable + return toLoadDrawable ?: toLoadBitmap ?: toLoadPath + } + + fun loadImage(item: MediaFileInfo, view: ImageView) { + getReadyRequestBuilder(item).into(view) } } diff --git a/app/src/main/java/com/amaze/fileutilities/home_page/ui/files/MediaFileAdapter.kt b/app/src/main/java/com/amaze/fileutilities/home_page/ui/files/MediaFileAdapter.kt index bc71f986..c0dba8da 100644 --- a/app/src/main/java/com/amaze/fileutilities/home_page/ui/files/MediaFileAdapter.kt +++ b/app/src/main/java/com/amaze/fileutilities/home_page/ui/files/MediaFileAdapter.kt @@ -44,7 +44,7 @@ import com.amaze.fileutilities.utilis.getAppCommonSharedPreferences class MediaFileAdapter( val context: Context, - private val preloader: MediaAdapterPreloader, + private val preloader: MediaAdapterPreloader, private val optionsMenuSelected: OptionsMenuSelected, isGrid: Boolean, private var sortingPreference: MediaFileListSorter.SortingPreference, @@ -86,11 +86,11 @@ class MediaFileAdapter( headerListItems.clear() var lastHeader: String? = null value.add(ListItem(TYPE_BANNER)) - preloader.addItem("") + preloader.addItem(null) for (i in 0 until mediaFileInfoList.size) { if (lastHeader == null || mediaFileInfoList[i].listHeader != lastHeader) { value.add(ListItem(TYPE_HEADER, mediaFileInfoList[i].listHeader)) - preloader.addItem("") + preloader.addItem(null) headerListItems.add( ListItem( TYPE_HEADER, mediaFileInfoList[i].listHeader.trim() @@ -105,10 +105,10 @@ class MediaFileAdapter( header = mediaFileInfoList[i].listHeader ) ) - preloader.addItem(mediaFileInfoList[i].path) + preloader.addItem(mediaFileInfoList[i]) onlyItemsCounts++ } - preloader.addItem("") + preloader.addItem(null) value.add(ListItem(EMPTY_LAST_ITEM)) field = value } diff --git a/app/src/main/java/com/amaze/fileutilities/home_page/ui/files/MediaInfoRecyclerViewHolder.kt b/app/src/main/java/com/amaze/fileutilities/home_page/ui/files/MediaInfoRecyclerViewHolder.kt index 057682d7..5f58c43d 100644 --- a/app/src/main/java/com/amaze/fileutilities/home_page/ui/files/MediaInfoRecyclerViewHolder.kt +++ b/app/src/main/java/com/amaze/fileutilities/home_page/ui/files/MediaInfoRecyclerViewHolder.kt @@ -37,6 +37,9 @@ class MediaInfoRecyclerViewHolder(view: View) : RecyclerView.ViewHolder(view) { @JvmField val infoSummary: TextView = view.findViewById(R.id.info_summary) + @JvmField + val infoSubSummary: TextView = view.findViewById(R.id.info_sub_summary) + @JvmField val extraInfo: TextView = view.findViewById(R.id.extra_info) diff --git a/app/src/main/java/com/amaze/fileutilities/home_page/ui/files/RecentMediaFilesAdapter.kt b/app/src/main/java/com/amaze/fileutilities/home_page/ui/files/RecentMediaFilesAdapter.kt index c9e1ee4e..64e6bdbd 100644 --- a/app/src/main/java/com/amaze/fileutilities/home_page/ui/files/RecentMediaFilesAdapter.kt +++ b/app/src/main/java/com/amaze/fileutilities/home_page/ui/files/RecentMediaFilesAdapter.kt @@ -28,7 +28,7 @@ import com.amaze.fileutilities.utilis.AbstractMediaFilesAdapter class RecentMediaFilesAdapter( val context: Context, - val preloader: MediaAdapterPreloader, + val preloader: MediaAdapterPreloader, private val mediaFileInfoList: MutableList, toggleCheckCallback: (Int, Int, String) -> Unit, ) : @@ -42,10 +42,10 @@ class RecentMediaFilesAdapter( value.clear() mediaFileInfoList.forEach { value.add(ListItem(mediaFileInfo = it)) - preloader.addItem(it.path) + preloader.addItem(it) } if (mediaFileInfoList.size != 0) { - preloader.addItem("") + preloader.addItem(null) value.add(ListItem(EMPTY_LAST_ITEM)) } field = value diff --git a/app/src/main/java/com/amaze/fileutilities/home_page/ui/files/SearchListFragment.kt b/app/src/main/java/com/amaze/fileutilities/home_page/ui/files/SearchListFragment.kt index 08415995..28733be0 100644 --- a/app/src/main/java/com/amaze/fileutilities/home_page/ui/files/SearchListFragment.kt +++ b/app/src/main/java/com/amaze/fileutilities/home_page/ui/files/SearchListFragment.kt @@ -55,8 +55,8 @@ class SearchListFragment : private var searchEditText: AutoCompleteTextView? = null private var mediaFileAdapter: RecentMediaFilesAdapter? = null - private var preloader: MediaAdapterPreloader? = null - private var recyclerViewPreloader: RecyclerViewPreloader? = null + private var preloader: MediaAdapterPreloader? = null + private var recyclerViewPreloader: RecyclerViewPreloader? = null private var linearLayoutManager: LinearLayoutManager? = null private val searchQueryInput: SearchQueryInput = SearchQueryInput(AggregatedMediaFileInfoObserver.AggregatedMediaFiles(), SearchFilter()) @@ -117,9 +117,10 @@ class SearchListFragment : searchEditText?.requestFocus() preloader = MediaAdapterPreloader( requireContext(), - R.drawable.ic_outline_insert_drive_file_32 + R.drawable.ic_outline_insert_drive_file_32, + false ) - val sizeProvider = ViewPreloadSizeProvider() + val sizeProvider = ViewPreloadSizeProvider() recyclerViewPreloader = RecyclerViewPreloader( Glide.with(requireContext()), preloader!!, diff --git a/app/src/main/java/com/amaze/fileutilities/home_page/ui/files/VideosListFragment.kt b/app/src/main/java/com/amaze/fileutilities/home_page/ui/files/VideosListFragment.kt index 75999234..b5efe3fb 100644 --- a/app/src/main/java/com/amaze/fileutilities/home_page/ui/files/VideosListFragment.kt +++ b/app/src/main/java/com/amaze/fileutilities/home_page/ui/files/VideosListFragment.kt @@ -47,7 +47,7 @@ class VideosListFragment : AbstractMediaInfoListFragment() { // This property is only valid between onCreateView and // onDestroyView. private val binding get() = _binding!! - private var preloader: MediaAdapterPreloader? = null + private var preloader: MediaAdapterPreloader? = null override fun onCreateView( inflater: LayoutInflater, @@ -79,11 +79,12 @@ class VideosListFragment : AbstractMediaInfoListFragment() { fileStorageSummaryAndMediaFileInfo else null } - override fun getMediaAdapterPreloader(): MediaAdapterPreloader { + override fun getMediaAdapterPreloader(isGrid: Boolean): MediaAdapterPreloader { if (preloader == null) { preloader = MediaAdapterPreloader( requireContext(), - R.drawable.ic_outline_video_library_32 + R.drawable.ic_outline_video_library_32, + isGrid ) } return preloader!! diff --git a/app/src/main/java/com/amaze/fileutilities/image_viewer/ImageViewerFragment.kt b/app/src/main/java/com/amaze/fileutilities/image_viewer/ImageViewerFragment.kt index 312acfdc..fd2e9dee 100644 --- a/app/src/main/java/com/amaze/fileutilities/image_viewer/ImageViewerFragment.kt +++ b/app/src/main/java/com/amaze/fileutilities/image_viewer/ImageViewerFragment.kt @@ -678,7 +678,7 @@ class ImageViewerFragment : AbstractMediaFragment() { override fun onLoadFailed( e: GlideException?, model: Any?, - target: Target?, + target: Target, isFirstResource: Boolean ): Boolean { // do nothing @@ -714,13 +714,13 @@ class ImageViewerFragment : AbstractMediaFragment() { } override fun onResourceReady( - resource: Drawable?, - model: Any?, + resource: Drawable, + model: Any, target: Target?, - dataSource: DataSource?, + dataSource: DataSource, isFirstResource: Boolean ): Boolean { - resource?.let { + resource.let { filesViewModel.getPaletteColors(it) .observe(this@ImageViewerFragment) { colorPair -> diff --git a/app/src/main/java/com/amaze/fileutilities/image_viewer/editor/EditImageActivity.kt b/app/src/main/java/com/amaze/fileutilities/image_viewer/editor/EditImageActivity.kt index 91a6347c..0b5b9f5d 100644 --- a/app/src/main/java/com/amaze/fileutilities/image_viewer/editor/EditImageActivity.kt +++ b/app/src/main/java/com/amaze/fileutilities/image_viewer/editor/EditImageActivity.kt @@ -202,7 +202,7 @@ class EditImageActivity : override fun onLoadFailed( e: GlideException?, model: Any?, - target: Target?, + target: Target, isFirstResource: Boolean ): Boolean { // do nothing @@ -211,13 +211,13 @@ class EditImageActivity : } override fun onResourceReady( - resource: Drawable?, - model: Any?, + resource: Drawable, + model: Any, target: Target?, - dataSource: DataSource?, + dataSource: DataSource, isFirstResource: Boolean ): Boolean { - loadedBitmap = resource?.toBitmap() + loadedBitmap = resource.toBitmap() addFilterViews() return false } @@ -242,7 +242,7 @@ class EditImageActivity : override fun onLoadFailed( e: GlideException?, model: Any?, - target: Target?, + target: Target, isFirstResource: Boolean ): Boolean { // do nothing @@ -251,13 +251,13 @@ class EditImageActivity : } override fun onResourceReady( - resource: Drawable?, - model: Any?, + resource: Drawable, + model: Any, target: Target?, - dataSource: DataSource?, + dataSource: DataSource, isFirstResource: Boolean ): Boolean { - loadedBitmap = resource?.toBitmap() + loadedBitmap = resource.toBitmap() addFilterViews() return false } diff --git a/app/src/main/java/com/amaze/fileutilities/utilis/AbstractMediaFilesAdapter.kt b/app/src/main/java/com/amaze/fileutilities/utilis/AbstractMediaFilesAdapter.kt index 5fc79a90..1b41e649 100644 --- a/app/src/main/java/com/amaze/fileutilities/utilis/AbstractMediaFilesAdapter.kt +++ b/app/src/main/java/com/amaze/fileutilities/utilis/AbstractMediaFilesAdapter.kt @@ -40,7 +40,7 @@ import java.util.Collections abstract class AbstractMediaFilesAdapter( private val superContext: Context, - private val superPreloader: MediaAdapterPreloader, + private val superPreloader: MediaAdapterPreloader, private val isGrid: Boolean, private val listItemPressedCallback: ((mediaFileInfo: MediaFileInfo) -> Unit)?, private val toggleCheckCallback: ( @@ -214,8 +214,9 @@ abstract class AbstractMediaFilesAdapter( holder.infoTitle.text = mediaFileInfo.title Utils.marqueeAfterDelay(3000, holder.infoTitle) Utils.marqueeAfterDelay(3000, holder.infoSummary) + Utils.marqueeAfterDelay(3000, holder.infoSubSummary) Glide.with(superContext).clear(holder.iconView) - superPreloader.loadImage(mediaFileInfo, holder.iconView, isGrid) + superPreloader.loadImage(mediaFileInfo, holder.iconView) if (isChecked) { if (isGrid) { holder.checkIconGrid.visibility = View.VISIBLE @@ -233,6 +234,9 @@ abstract class AbstractMediaFilesAdapter( superContext.resources.getDrawable(R.drawable.ripple) } } + if (!isGrid) { + holder.infoSubSummary.text = mediaFileInfo.path + } holder.root.setOnLongClickListener { toggleChecked(this, position) invalidateCheckedTitle(getOnlyItemsCount()) @@ -305,13 +309,14 @@ abstract class AbstractMediaFilesAdapter( formattedDate: String, formattedSize: String ) { - holder.infoSummary.text = if (mediaFileInfo.extraInfo?.imageMetaData?.width != null) { - "${mediaFileInfo.extraInfo!!.imageMetaData?.width}" + + if (mediaFileInfo.extraInfo?.imageMetaData?.width != null) { + holder.infoSummary.visibility = View.GONE + holder.extraInfo.text = "${mediaFileInfo.extraInfo!!.imageMetaData?.width}" + "x${mediaFileInfo.extraInfo!!.imageMetaData?.height}" } else { - "$formattedDate | $formattedSize" + holder.infoSummary.text = "$formattedDate | $formattedSize" + holder.extraInfo.visibility = View.GONE } - holder.extraInfo.text = "" } private fun processAudioMediaInfo( @@ -320,7 +325,7 @@ abstract class AbstractMediaFilesAdapter( formattedDate: String, formattedSize: String ) { - if (mediaFileInfo.extraInfo?.audioMetaData?.duration != null) { + if (mediaFileInfo.extraInfo?.audioMetaData != null) { holder.infoSummary.text = "${mediaFileInfo.extraInfo!!.audioMetaData?.albumName} " + "| ${mediaFileInfo.extraInfo!!.audioMetaData?.artistName}" mediaFileInfo.extraInfo!!.audioMetaData?.duration?.let { @@ -328,7 +333,7 @@ abstract class AbstractMediaFilesAdapter( } } else { holder.infoSummary.text = "$formattedDate | $formattedSize" - holder.extraInfo.text = "" + holder.extraInfo.visibility = View.GONE } } @@ -338,7 +343,7 @@ abstract class AbstractMediaFilesAdapter( formattedDate: String, formattedSize: String ) { - if (mediaFileInfo.extraInfo?.videoMetaData?.duration != null) { + if (mediaFileInfo.extraInfo?.videoMetaData != null) { holder.infoSummary.text = "${mediaFileInfo.extraInfo!!.videoMetaData?.width}" + "x${mediaFileInfo.extraInfo!!.videoMetaData?.height}" @@ -347,7 +352,7 @@ abstract class AbstractMediaFilesAdapter( } } else { holder.infoSummary.text = "$formattedDate | $formattedSize" - holder.extraInfo.text = "" + holder.extraInfo.visibility = View.GONE } } diff --git a/app/src/main/java/com/amaze/fileutilities/utilis/Extensions.kt b/app/src/main/java/com/amaze/fileutilities/utilis/Extensions.kt index e4b792d3..0680aa0a 100644 --- a/app/src/main/java/com/amaze/fileutilities/utilis/Extensions.kt +++ b/app/src/main/java/com/amaze/fileutilities/utilis/Extensions.kt @@ -33,6 +33,7 @@ import android.os.Environment import android.os.ParcelFileDescriptor import android.os.TransactionTooLargeException import android.provider.MediaStore +import android.provider.Settings import android.util.DisplayMetrics import android.view.Gravity import android.view.View @@ -686,3 +687,15 @@ fun View.fadInAnimation(duration: Long = 300, completion: (() -> Unit)? = null) } } } + +fun Context.getScreenBrightness(): Float { + var brightness = 0 + try { + val contentResolver = this.contentResolver + brightness = Settings.System.getInt(contentResolver, Settings.System.SCREEN_BRIGHTNESS) + } catch (e: Exception) { + log.warn("failed to get system brightness", e) + return -1f + } + return (brightness / 255).toFloat() +} diff --git a/app/src/main/java/com/amaze/fileutilities/video_player/BaseVideoPlayerActivity.kt b/app/src/main/java/com/amaze/fileutilities/video_player/BaseVideoPlayerActivity.kt index 4ad39931..717b868e 100644 --- a/app/src/main/java/com/amaze/fileutilities/video_player/BaseVideoPlayerActivity.kt +++ b/app/src/main/java/com/amaze/fileutilities/video_player/BaseVideoPlayerActivity.kt @@ -78,6 +78,7 @@ import com.amaze.fileutilities.utilis.dialog_picker.FileFilter import com.amaze.fileutilities.utilis.getAppCommonSharedPreferences import com.amaze.fileutilities.utilis.getExternalStorageDirectory import com.amaze.fileutilities.utilis.getFileFromUri +import com.amaze.fileutilities.utilis.getScreenBrightness import com.amaze.fileutilities.utilis.hideFade import com.amaze.fileutilities.utilis.isNetworkAvailable import com.amaze.fileutilities.utilis.removeExtension @@ -990,7 +991,16 @@ abstract class BaseVideoPlayerActivity : private fun invalidateBrightness() { val layout = window.attributes - layout.screenBrightness = videoPlayerViewModel?.brightnessLevel ?: 0.3f + val systemBrightness = this.getScreenBrightness() + layout.screenBrightness = if (videoPlayerViewModel != null && + videoPlayerViewModel?.brightnessLevel != 0.3f + ) { + videoPlayerViewModel!!.brightnessLevel + } else if (systemBrightness != -1f) { + systemBrightness + } else { + 0.3f + } window.attributes = layout } diff --git a/app/src/main/java/com/amaze/fileutilities/video_player/VideoPlayerActivity.kt b/app/src/main/java/com/amaze/fileutilities/video_player/VideoPlayerActivity.kt index 98cfcfff..3a516278 100644 --- a/app/src/main/java/com/amaze/fileutilities/video_player/VideoPlayerActivity.kt +++ b/app/src/main/java/com/amaze/fileutilities/video_player/VideoPlayerActivity.kt @@ -20,7 +20,13 @@ package com.amaze.fileutilities.video_player +import android.content.res.Configuration +import android.os.Build import android.os.Bundle +import android.view.View +import android.view.WindowInsets +import android.view.WindowInsetsController +import android.view.WindowManager class VideoPlayerActivity : BaseVideoPlayerActivity() { @@ -36,5 +42,30 @@ class VideoPlayerActivity : BaseVideoPlayerActivity() { initLocalVideoModel(intent) super.onCreate(savedInstanceState) handleVideoPlayerActivityResources() + val orientation = resources.configuration.orientation + if (orientation == Configuration.ORIENTATION_LANDSCAPE) { + // allow to go in notch area in landscape mode + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { + // Set the system UI visibility flags + window.decorView.systemUiVisibility = View.SYSTEM_UI_FLAG_LAYOUT_STABLE or + View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION or + View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN + + // Get the WindowInsetsController + val controller = window.insetsController + + // Hide the system bars (navigation bar, status bar) + controller?.hide(WindowInsets.Type.systemBars()) + + // Enable the extended layout to be displayed in the notch area + controller?.systemBarsBehavior = + WindowInsetsController.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE + } else { + window.setFlags( + WindowManager.LayoutParams.FLAG_FULLSCREEN, + WindowManager.LayoutParams.FLAG_FULLSCREEN + ) + } + } } } diff --git a/app/src/main/res/drawable/background_curved_dark_2.xml b/app/src/main/res/drawable/background_curved_dark_2.xml deleted file mode 100644 index 81ca2bcd..00000000 --- a/app/src/main/res/drawable/background_curved_dark_2.xml +++ /dev/null @@ -1,7 +0,0 @@ - - - - - - - \ No newline at end of file diff --git a/app/src/main/res/drawable/button_curved_selected.xml b/app/src/main/res/drawable/button_curved_selected.xml index 68dd6ac3..32b6338a 100644 --- a/app/src/main/res/drawable/button_curved_selected.xml +++ b/app/src/main/res/drawable/button_curved_selected.xml @@ -5,7 +5,7 @@ - + diff --git a/app/src/main/res/drawable/circle_shape.xml b/app/src/main/res/drawable/circle_shape.xml index 44637831..be481c48 100644 --- a/app/src/main/res/drawable/circle_shape.xml +++ b/app/src/main/res/drawable/circle_shape.xml @@ -3,6 +3,6 @@ android:shape="oval"> + android:width="@dimen/minimal_icon_size" + android:height="@dimen/minimal_icon_size"/> \ No newline at end of file diff --git a/app/src/main/res/drawable/popup_item_background.xml b/app/src/main/res/drawable/popup_item_background.xml new file mode 100644 index 00000000..8f50be99 --- /dev/null +++ b/app/src/main/res/drawable/popup_item_background.xml @@ -0,0 +1,16 @@ + + + + + + + + + + diff --git a/app/src/main/res/drawable/ripple_curved_16_trans_70.xml b/app/src/main/res/drawable/ripple_curved_16_trans_70.xml new file mode 100644 index 00000000..a2aee21d --- /dev/null +++ b/app/src/main/res/drawable/ripple_curved_16_trans_70.xml @@ -0,0 +1,17 @@ + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/media_info_grid_layout.xml b/app/src/main/res/layout/media_info_grid_layout.xml index ccae6c79..c6a5f6ec 100644 --- a/app/src/main/res/layout/media_info_grid_layout.xml +++ b/app/src/main/res/layout/media_info_grid_layout.xml @@ -107,6 +107,17 @@ android:marqueeRepeatLimit="marquee_forever" android:ellipsize="marquee" /> + + + + - \ No newline at end of file diff --git a/app/src/main/res/values-v27/themes.xml b/app/src/main/res/values-v27/themes.xml index 7e2a1cff..0ec0e6b3 100644 --- a/app/src/main/res/values-v27/themes.xml +++ b/app/src/main/res/values-v27/themes.xml @@ -1,8 +1,9 @@ \ No newline at end of file diff --git a/app/src/main/res/values/themes.xml b/app/src/main/res/values/themes.xml index 73bdd710..bdc0c588 100644 --- a/app/src/main/res/values/themes.xml +++ b/app/src/main/res/values/themes.xml @@ -63,6 +63,7 @@