diff --git a/app/build.gradle b/app/build.gradle index 31478b4..4b7d40c 100755 --- a/app/build.gradle +++ b/app/build.gradle @@ -6,14 +6,16 @@ plugins { android { namespace 'com.slavabarkov.tidy' - compileSdk 33 + compileSdk 34 defaultConfig { applicationId "com.slavabarkov.tidy" minSdk 26 targetSdk 33 - versionCode 2 - versionName "1.1" + versionCode 3 + versionName "1.2.1" + // Naming app bundles/APKs + setArchivesBaseName("TIDY-${versionName}-c${versionCode}") testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" } @@ -34,17 +36,17 @@ android { } dependencies { - implementation 'androidx.navigation:navigation-fragment-ktx:2.5.3' - implementation 'androidx.navigation:navigation-ui-ktx:2.5.3' - implementation 'androidx.room:room-runtime:2.5.0' - kapt 'androidx.room:room-compiler:2.5.0' - implementation 'androidx.room:room-ktx:2.5.0' + implementation 'androidx.navigation:navigation-fragment-ktx:2.7.7' + implementation 'androidx.navigation:navigation-ui-ktx:2.7.7' + implementation 'androidx.room:room-runtime:2.6.1' + kapt 'androidx.room:room-compiler:2.6.1' + implementation 'androidx.room:room-ktx:2.6.1' implementation 'com.google.code.gson:gson:2.10.1' implementation 'com.github.bumptech.glide:glide:4.13.2' - api 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.5.1' - implementation 'androidx.core:core-ktx:1.9.0' + api 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.7.0' + implementation 'androidx.core:core-ktx:1.12.0' implementation 'androidx.appcompat:appcompat:1.6.1' - implementation 'com.google.android.material:material:1.8.0' + implementation 'com.google.android.material:material:1.11.0' implementation 'androidx.constraintlayout:constraintlayout:2.1.4' testImplementation 'junit:junit:4.13.2' androidTestImplementation 'androidx.test.ext:junit:1.1.5' diff --git a/app/release/output-metadata.json b/app/release/output-metadata.json new file mode 100644 index 0000000..ea01b2f --- /dev/null +++ b/app/release/output-metadata.json @@ -0,0 +1,20 @@ +{ + "version": 3, + "artifactType": { + "type": "APK", + "kind": "Directory" + }, + "applicationId": "com.slavabarkov.tidy", + "variantName": "release", + "elements": [ + { + "type": "SINGLE", + "filters": [], + "attributes": [], + "versionCode": 3, + "versionName": "1.2.1", + "outputFile": "TIDY-1.2.1-c3-release.apk" + } + ], + "elementType": "File" +} \ No newline at end of file diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index ba62580..7928260 100755 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -14,7 +14,8 @@ android:supportsRtl="true" android:theme="@style/Theme.TIDY" tools:targetApi="31" - android:largeHeap="true"> + android:largeHeap="true" + android:windowSoftInputMode="adjustResize"> = 30 ) { + // Code to handle API level 30 (Android 11) + // When Android >= 11, enable “Display content edge-to-edge” + // See more in https://developer.android.com/develop/ui/views/layout/edge-to-edge + enableEdgeToEdge() + } super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) diff --git a/app/src/main/java/com/slavabarkov/tidy/fragments/IndexFragment.kt b/app/src/main/java/com/slavabarkov/tidy/fragments/IndexFragment.kt index db89b76..4c45259 100755 --- a/app/src/main/java/com/slavabarkov/tidy/fragments/IndexFragment.kt +++ b/app/src/main/java/com/slavabarkov/tidy/fragments/IndexFragment.kt @@ -4,6 +4,7 @@ package com.slavabarkov.tidy.fragments +import android.os.Build import android.os.Bundle import android.view.LayoutInflater import android.view.View @@ -41,8 +42,10 @@ class IndexFragment : Fragment() { val view = inflater.inflate(R.layout.fragment_index, container, false) activity?.window?.addFlags(android.view.WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON) // Request required permissions depending on the Android version - if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.R) { - permissionsRequest.launch(android.Manifest.permission.READ_MEDIA_IMAGES) + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { + permissionsRequest.launch(android.Manifest.permission.READ_MEDIA_IMAGES) + } } else { permissionsRequest.launch(android.Manifest.permission.READ_EXTERNAL_STORAGE) @@ -54,7 +57,10 @@ class IndexFragment : Fragment() { mORTImageViewModel.progress.observe(viewLifecycleOwner) { progress -> var progressPercent: Int = (progress * 100).toInt() progressBarView?.progress = progressPercent - progressBarTextView?.text = "Updating image index: ${progressPercent}%" + + //Reference progress_bar_text in strings.xml for better localisation. + val myString = getString(R.string.progress_bar_text) + "${myString}: ${progressPercent}%".also { progressBarTextView?.text = it } if (progress == 1.0) { activity?.window?.clearFlags(android.view.WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON) diff --git a/app/src/main/java/com/slavabarkov/tidy/fragments/SearchFragment.kt b/app/src/main/java/com/slavabarkov/tidy/fragments/SearchFragment.kt index 5ff6ede..a20189c 100755 --- a/app/src/main/java/com/slavabarkov/tidy/fragments/SearchFragment.kt +++ b/app/src/main/java/com/slavabarkov/tidy/fragments/SearchFragment.kt @@ -10,6 +10,9 @@ import android.view.View import android.view.ViewGroup import android.widget.Button import android.widget.TextView +import androidx.core.view.ViewCompat +import androidx.core.view.WindowInsetsAnimationCompat +import androidx.core.view.WindowInsetsCompat import androidx.fragment.app.Fragment import androidx.fragment.app.activityViewModels import androidx.recyclerview.widget.RecyclerView @@ -52,6 +55,69 @@ class SearchFragment : Fragment() { recyclerView.adapter = ImageAdapter(requireContext(), mSearchViewModel.searchResults!!) recyclerView.scrollToPosition(0) + // If Edge-to-edge display is enabled, + // tweak the top margin so that content will not overlap with status bar + ViewCompat.setOnApplyWindowInsetsListener(view) { v, windowInsets -> + // Handle window insets here + val insets = windowInsets.getInsets(WindowInsetsCompat.Type.systemBars()) + + // Update the layout parameters of the view's parent + (v.layoutParams as? ViewGroup.MarginLayoutParams)?.apply { + topMargin = insets.top + } + + // Use WindowInsets to check the software keyboard visibility. + // val imeVisible = windowInsets.isVisible(WindowInsetsCompat.Type.ime()) + // val imeHeight = windowInsets.getInsets(WindowInsetsCompat.Type.ime()).bottom + + // Return CONSUMED if you don't want want the window insets to keep passing + // down to descendant views. + WindowInsetsCompat.CONSUMED + } + + // New keyboard animation using WindowInsets + ViewCompat.setWindowInsetsAnimationCallback( + view, + object : WindowInsetsAnimationCompat.Callback(DISPATCH_MODE_STOP) { + + var startBottom = 0f + override fun onPrepare( + animation: WindowInsetsAnimationCompat + ) { + startBottom = view.bottom.toFloat() + } + + var endBottom = 0f + override fun onStart( + animation: WindowInsetsAnimationCompat, + bounds: WindowInsetsAnimationCompat.BoundsCompat + ): WindowInsetsAnimationCompat.BoundsCompat { + // Record the position of the view after the IME transition. + endBottom = view.bottom.toFloat() + + return bounds + } + + override fun onProgress( + insets: WindowInsetsCompat, + runningAnimations: MutableList + ): WindowInsetsCompat { + // Find an IME animation. + val imeAnimation = runningAnimations.find { + it.typeMask and WindowInsetsCompat.Type.ime() != 0 + } ?: return insets + + // Offset the view based on the interpolated fraction of the IME animation. + view.translationY = + (startBottom - endBottom) * (1 - imeAnimation.interpolatedFraction) + + return insets + } + + } + ) + + mORTTextViewModel.init() searchText = view?.findViewById(R.id.searchText) diff --git a/app/src/main/res/layout/fragment_index.xml b/app/src/main/res/layout/fragment_index.xml index ec27ded..1d86cbd 100755 --- a/app/src/main/res/layout/fragment_index.xml +++ b/app/src/main/res/layout/fragment_index.xml @@ -13,7 +13,7 @@ android:layout_width="match_parent" android:layout_height="wrap_content" android:ems="10" - android:text="Updating image index" + android:text="@string/progress_bar_text" android:gravity="center"/> - + android:gravity="center" + android:layout_marginTop="12dp" + android:layout_marginHorizontal="12dp"> - + android:hint="@string/search_text" + android:inputType="textWebEditText|text" + /> - + android:gravity="center" + android:layout_marginTop="6dp"> -