I clicked the download button in the song list activity, but after a few seconds, the system recycled my current activity, and did not generate an error, but entered the onDestroy() method.
Specific steps: Start a service, download it in the service, and call back to the page to update the progress through EventBus. I haven't started that activity.
-I want to know why I recycled my current activity
my code is as following:
fun download(){
if (downloadList.size == 0)return
if (current >= downloadList.size){
ToastUtil.setSuccessToast(HomePageActivity.MA,"下载完成!")
current = 0
downloadList.clear()
return
}
DownloadUtils.startDownload(downloadList[current],object : DownloadUtils.FileDownloaderCallback{
override fun pending(task: BaseDownloadTask) {
//status = 6
EventBus.getDefault().postSticky(DownloadingBean(task.status,"",percentage,"", downloadList[current]))
}
override fun start(task: BaseDownloadTask) {
//已经进入下载队列,正在等待下载
//status = 6
EventBus.getDefault().postSticky(DownloadingBean(task.status,"",percentage,"", downloadList[current]))
}
override fun running(task: BaseDownloadTask, speed: Int, current: Int, total: Int) {
//status = 3
// kb/s-> KB/s
Log.d("downloadTAG","running:$speed")
percentage = ((current*1.0 /total)*100).toInt()
EventBus.getDefault().postSticky(DownloadingBean(task.status,"${remainDigit(speed/8.0)}KB/s",percentage,"", downloadList[DownloadBinder.current]))
}
override fun pause(task: BaseDownloadTask) {
Log.d("downloadTAG","pause:${task.status}")
EventBus.getDefault().postSticky(DownloadingBean(task.status,"",percentage,"", downloadList[current]))
}
override fun completed(task: BaseDownloadTask) {
//status = -3
/**
* 除2个1024的到大小MB
* 记得最后保留一位小数*/
val primary = "${downloadList[current].songId}${downloadList[current].songName}"
/**
* 下载完成之后,更新数据库字段*/
PlayListDataBase.getDBInstance().downloadDao().updateComplete(primary,true)
PlayListDataBase.getDBInstance().downloadDao().updatePath(primary,task.path)
PlayListDataBase.getDBInstance().downloadDao().updateUrl(primary,task.url)
val size = remainDigit(task.smallFileTotalBytes*1.0/1024/1024)
PlayListDataBase.getDBInstance().downloadDao().updateSize(primary,"${size}MB")
EventBus.getDefault().postSticky(DownloadingBean(task.status,"",percentage,"", downloadList[current]))
current++
download()
}
override fun failed(task: BaseDownloadTask, message: String?) {
// error = -1
Log.d("downloadTAG","failed:${task.status}")
Log.d("downloadTAG","failed:$message")
EventBus.getDefault().postSticky(DownloadingBean(task.status,"",percentage,message!!, downloadList[current]))
}
override fun exist(task: BaseDownloadTask) {
/**
* 不会进入此处
* 因为外面已经判断过重复项*/
Log.d("downloadTAG","exist:${task.status}")
EventBus.getDefault().postSticky(DownloadingBean(task.status,"",percentage,"", downloadList[current]))
}
})
}
Related
I Have Recycler view with its adapter and holder. Each element has "delete" button, which has to update recyclerview. But after deleting first element, when I'm trying to delete others, from datasource are removing items with old indices of items, which I want to delete
Hope, I can explain it with an example:
Old data: (1 2 3 4 5 6 7) -> deleting "1" -> (2 3 4 5 6 7) -> deleting "2" -> (2 4 5 6 7) -> deleting "2" -> (2 5 6 7)
Here is ViewHolder source code:
inner class ViewHolder(binding: RecyclerItemBinding) : RecyclerView.ViewHolder(binding.root) {
val idView: TextView = binding.itemNumber
val contentView: Button = binding.deleteButton
init {
contentView.setOnClickListener {
removeAt(adapterPosition)
}
}
private fun removeAt(position: Int) {
values.removeAt(position)
notifyItemRemoved(position)
notifyItemRangeChanged(position, itemCount)
Log.d(TAG, "remove $position left $itemCount")
}
override fun toString(): String {
return super.toString() + " '" + contentView.text + "'"
}
}
Content code:
object PlaceholderContent {
/**
* An array of sample (placeholder) items.
*/
val ITEMS: MutableList<PlaceholderItem> = ArrayList()
/**
* A map of sample (placeholder) items, by ID.
*/
private val ITEM_MAP: MutableMap<Int, PlaceholderItem> = HashMap()
private val deletedNumbers : Queue<Int> = LinkedList()
private const val INITIAL_COUNT = 15
private var biggest: Int
init {
// Add some sample items.
for (i in 1..INITIAL_COUNT) {
addItem(createPlaceholderItem(i))
}
biggest = ITEMS.last().id
}
fun size() : Int = ITEMS.size
fun addNext() {
addItem(createPlaceholderItem(deletedNumbers.poll() ?: ++biggest))
Log.d(TAG, "add $biggest")
}
fun removeAt(position: Int) {
val toRemove = ITEMS.removeAt(position)
if (toRemove.id == biggest) {
biggest--
}
deletedNumbers.add(toRemove.content.toInt())
}
private fun addItem(item: PlaceholderItem) {
ITEMS.add(item)
ITEM_MAP[item.id] = item
}
private fun createPlaceholderItem(position: Int): PlaceholderItem {
return PlaceholderItem(size(), "$position")
}
/**
* A placeholder item representing a piece of content.
*/
data class PlaceholderItem(val id: Int, val content: String): Comparable<PlaceholderItem> {
override fun toString(): String = content
override fun compareTo(other: PlaceholderItem): Int = this.id.compareTo(other.id)
}
private const val TAG = "PLACEHOLDER_CONTENT"
}
To remove a specific positioned value, I just called the getAdapterPosition() to specify the item position to be removed. Simply try this in your onBindViewHolder with the arrayList that is using like Old data: (1 2 3 4 5 6 7).
override fun onBindViewHolder(holder: RecyclerViewAdapter.ViewHolder, position: Int) {
holder.buttonDelete2.setOnClickListener {
arrayList.removeAt(holder.adapterPosition)
notifyItemRemoved(holder.adapterPosition)
}
}
Add increment in adapterPosition by 1
adapterPosition + 1
Alternatively for performance-wise, you can use DiffUtil class which will help calculates the item position change.
you can check some tutorials here:
https://blog.mindorks.com/the-powerful-tool-diff-util-in-recyclerview-android-tutorial
Problem:
I get 40 items at the beginning of the list, then it starts to count from 11, and after this, everything is good. So, 1...40,11,12,13,...,300.
And when I scroll a lot to the bottom and then scroll up to see first items, the items have been changed to 1,2,...,10,1,2,...,10,1,2,...,10,11,12,...,300.
But, when I pass false to enablePlaceholders in the PagingConfig, when I scroll to the bottom, I see the issue as I said above(1,2,..,40,11,...,300) and suddenly the 40 items vanish and I only see 1,2,...,10 + 11,12,...,300(the correct way); And it doesn't change or get worse again.
ProductsPagingSource:
#Singleton
class ProductsPagingSource #Inject constructor(
private val productsApi: ProductsApi
//private val query: String
) : RxPagingSource<Int, RecyclerItem>() {
override fun loadSingle(params: LoadParams<Int>): Single<LoadResult<Int, RecyclerItem>> {
val position = params.key ?: STARTING_PAGE_INDEX
//val apiQuery = query
return productsApi.getBeersList(position, params.loadSize)
.subscribeOn(Schedulers.io())
.map { listBeerResponse ->
listBeerResponse.map { beerResponse ->
beerResponse.toDomain()
}
}
.map { toLoadResult(it, position) }
.onErrorReturn { LoadResult.Error(it) }
}
private fun toLoadResult(
#NonNull response: List<RecyclerItem>,
position: Int
): LoadResult<Int, RecyclerItem> {
return LoadResult.Page(
data = response,
prevKey = if (position == STARTING_PAGE_INDEX) null else position - 1,
nextKey = if (response.isEmpty()) null else position + 1,
itemsBefore = LoadResult.Page.COUNT_UNDEFINED,
itemsAfter = LoadResult.Page.COUNT_UNDEFINED
)
}
}
ProductsListRepositoryImpl:
#Singleton
class ProductsListRepositoryImpl #Inject constructor(
private val pagingSource: ProductsPagingSource
) : ProductsListRepository {
override fun getBeers(ids: String): Flowable<PagingData<RecyclerItem>> = Pager(
config = PagingConfig(
pageSize = 10,
enablePlaceholders = true,
maxSize = 30,
prefetchDistance = 5,
initialLoadSize = 40
),
pagingSourceFactory = { pagingSource }
).flowable
}
ProductsListViewModel:
class ProductsListViewModel #ViewModelInject constructor(
private val getBeersUseCase: GetBeersUseCase
) : BaseViewModel() {
private val _ldProductsList: MutableLiveData<PagingData<RecyclerItem>> = MutableLiveData()
val ldProductsList: LiveData<PagingData<RecyclerItem>> = _ldProductsList
init {
loading(true)
getProducts("")
}
private fun getProducts(ids: String) {
loading(false)
getBeersUseCase(GetBeersParams(ids = ids))
.cachedIn(viewModelScope)
.observeOn(AndroidSchedulers.mainThread())
.subscribe {
_ldProductsList.value = it
}.addTo(compositeDisposable)
}
}
ProductsListFragment:
#AndroidEntryPoint
class ProductsListFragment : Fragment(R.layout.fragment_product_list) {
private val productsListViewModel: ProductsListViewModel by viewModels()
private val productsListAdapter: ProductsListAdapter by lazy {
ProductsListAdapter(::navigateToProductDetail)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
setupRecycler()
setupViewModel()
}
private fun setupRecycler() {
itemErrorContainer.gone()
productListRecyclerView.adapter = productsListAdapter
}
private fun setupViewModel() {
productsListViewModel.run {
observe(ldProductsList, ::addProductsList)
observe(ldLoading, ::loadingUI)
observe(ldFailure, ::handleFailure)
}
}
private fun addProductsList(productsList: PagingData<RecyclerItem>) {
loadingUI(false)
productListRecyclerView.visible()
productsListAdapter.submitData(lifecycle, productsList)
}
...
BASE_DIFF_CALLBACK:
val BASE_DIFF_CALLBACK = object : DiffUtil.ItemCallback<RecyclerItem>() {
override fun areItemsTheSame(oldItem: RecyclerItem, newItem: RecyclerItem): Boolean {
return oldItem.id == newItem.id
}
override fun areContentsTheSame(oldItem: RecyclerItem, newItem: RecyclerItem): Boolean {
return oldItem == newItem
}
}
BasePagedListAdapter:
abstract class BasePagedListAdapter(
vararg types: Cell<RecyclerItem>,
private val onItemClick: (RecyclerItem, ImageView) -> Unit
) : PagingDataAdapter<RecyclerItem, RecyclerView.ViewHolder>(BASE_DIFF_CALLBACK) {
private val cellTypes: CellTypes<RecyclerItem> = CellTypes(*types)
override fun getItemViewType(position: Int): Int {
getItem(position).let {
return cellTypes.of(it).type()
}
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
return cellTypes.of(viewType).holder(parent)
}
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
getItem(position).let {
cellTypes.of(it).bind(holder, it, onItemClick)
}
}
}
I had to set the same number for pageSize and initialLoadSize. Also, I had to set the enablePlaceholders to false.
config = PagingConfig(
pageSize = 10,
enablePlaceholders = false,
maxSize = 30,
prefetchDistance = 5,
initialLoadSize = 10
),
But still, I want to know if it's the normal way? If yes, I couldn't find anywhere point to this! If not, why's that? Why initialLoadSize can not have value more than the pageSize?!
As we can see, the default value for the initialLoadSize is:
internal const val DEFAULT_INITIAL_PAGE_MULTIPLIER = 3
val initialLoadSize: Int = pageSize * DEFAULT_INITIAL_PAGE_MULTIPLIER,
Your issue is most likely because at the repository layer your results page numbers are indexed with numbers derived from the total divided by your page size.
This page number scheme assumes that you will page through the items with pages of the same size. However, Paging wants to get an initial page that's three times bigger by default, and then each page should be the page size.
So, the indexes might be like 0 through totalElements/PageSize, with the number for page that includes a given position equaling itemsIndex/PageSize.
This part is especially relevant:
return LoadResult.Page(
data = response,
prevKey = if (position == STARTING_PAGE_INDEX) null else position - 1,
nextKey = if (response.isEmpty()) null else position + 1,
itemsBefore = LoadResult.Page.COUNT_UNDEFINED,
itemsAfter = LoadResult.Page.COUNT_UNDEFINED
)
Let's imagine a pagesize of 10, an initial load of 30, and 100 elements total. We start with page 0, and we request 30 items. Position is 0, and params.loadSize is 30.
productsApi.getBeersList(0, 30)
Then we return a page object with those 30 elements and a nextPage key of 1.
If we imagine all our objects as list, the first page would be the asterisks in this span: [***-------]
Let's get the next page:
productsApi.getBeersList(1, 10)
That returns elements that are this from our span: [-*--------]
And then you get: [--*-------]
So the 0th page is fine, but the 1th and 2nd page overlap with it. Then, pages 3 and onward contain new elements.
Because you're getting the keys for the next and previous pages by adding or subtracting to get the adjacent keys to the current page of the current length, the index can't scale with the page size. This isn't always easy without knowing what is inside the PagingConfig.
However you can make it work with dynamic page sizes if you can guarantee your initial load size is your regular page size times some integer and that you'll always get the requested number of items except for the last page, you could store an offset as your next/previous page keys, and turn that offset into the next page at load like:
/* Value for api pageNo */
val pageNo = (params.key/params.loadSize + STARTING_PAGE_INDEX) ?: STARTING_PAGE_INDEX
productsApi.getBeersList(pageNo, params.loadSize) // and so on
/* for offset keys in PageData */
nextKey = (pageNo + 1) * loadSize
prevKey = (pageNo - 1) * loadSize // Integer division rounds down so larger windows start where they should
I'm working on an ExoPlayer based media player for Android, and I'm attempting to write my own Equalizer.
I've looked fairly deeply into ExoPlayer, and I believe the best place to manipulate samples in order to apply Equalier changes, is in a custom AudioProcessor.
I've used ChannelMappingAudioProcessor as a starting point, and cloned what I think are the relevant aspects:
class EqualizerAudioProcessor : BaseAudioProcessor() {
private lateinit var outputChannels: IntArray
override fun configure(sampleRateHz: Int, channelCount: Int, encoding: Int): Boolean {
outputChannels = IntArray(channelCount)
for (i in 0 until channelCount) {
outputChannels[i] = i
}
return true
}
override fun isActive(): Boolean {
return true
}
override fun getOutputChannelCount(): Int {
return outputChannels.size
}
override fun queueInput(inputBuffer: ByteBuffer) {
var position = inputBuffer.position()
val limit = inputBuffer.limit()
val frameCount = (limit - position) / (2 * channelCount)
val outputSize = frameCount * outputChannels.size * 2
val buffer = replaceOutputBuffer(outputSize)
while (position < limit) {
for (element in outputChannels) {
var sample = inputBuffer.getShort(position + 2 * element)
// Todo: Manipulate sample
buffer.putShort(sample)
}
position += channelCount * 2
}
inputBuffer.position(limit)
buffer.flip()
}
override fun onReset() {
}
}
It seems that if I enable this AudioProcessor, playback doesn't occur (it seems stuck in a 'paused state', as if the samples aren't being passed along, and interestingly, queueInput() is not called. If I disable the AudioProcessor, playback works fine.
I'm hoping someone can help me understand if I'm making a mistake here, and how to get this working.
For reference, the ExoPlayer instance is initialised like so:
private fun initPlayer(context: Context): ExoPlayer {
val audioProcessor = EqualizerAudioProcessor()
val renderersFactory = object : DefaultRenderersFactory(context) {
override fun buildAudioProcessors(): Array<AudioProcessor> {
return arrayOf(audioProcessor)
}
}
val player: SimpleExoPlayer = ExoPlayerFactory.newSimpleInstance(
context,
renderersFactory,
DefaultTrackSelector(),
DefaultLoadControl()
)
player.addListener(object : Player.EventListener {
override fun onPlayerStateChanged(playWhenReady: Boolean, playbackState: Int) {
callback?.onPlayStateChanged(playWhenReady)
}
})
return player
}
Thanks in advance
The problem is that you must call setInputFormat() in configure() of the AudioProcessor, or queueInput() will not be called.
Hello I have a problem with asynctask.I play a song then I update duration to progressbar. But when I play a new song progressbar don't back to 0th position and progressbar is continuing with old value
Here is my code:
class Task(context: Context, progressBar: ProgressBar) : AsyncTask<Int, Int, String>() {
#SuppressLint("StaticFieldLeak")
private var progressBar: ProgressBar? = progressBar
private var count = 0
override fun doInBackground(vararg input: Int?): String {
while (count <= input[0]!!) {
count++
publishProgress(count)
Thread.sleep(1000)
if (isCancelled){
count=0
}
}
return "Task completed"
}
override fun onPreExecute() {
progressBar!!.progress = 0
}
override fun onProgressUpdate(vararg values: Int?) {
progressBar!!.progress = values[0]!!
}
}
when I play song :
override fun onItemClicked(position: Int, song: Song) {
val secondsDuration = song.duration!! / 1000
activity!!.pgbSong.max = secondsDuration
val task = Task(context!!, activity!!.pgbSong)
if (task.status == AsyncTask.Status.RUNNING) {
task.cancel(true)
}
task.execute(song.duration)
}
Well, what to say - you never cancel previous async tasks. Cause you're calling cancel(true) on just created async tasks every time:
val task = Task(context!!, activity!!.pgbSong)
if (task.status == AsyncTask.Status.RUNNING) {
task.cancel(true)
}
task.execute(song.duration)
Instead, you should save previous async task in an object variable (something like this):
private var asyncTask : AsyncTask<*,*,*>? = null
And after in the method call:
override fun onItemClicked(position: Int, song: Song) {
if (asyncTask.status == AsyncTask.Status.RUNNING) {
asyncTask.cancel(true)
}
val secondsDuration = song.duration!! / 1000
activity!!.pgbSong.max = secondsDuration
asyncTask = Task(context!!, activity!!.pgbSong)
asyncTask.execute(song.duration)
}
And, I guess, you should do a return in an AsyncTask when you're checking if it canceled or not.
But please don't use AsyncTask in this manner. Cause it holds links views and activity which can prevent those of being garbage collected and so cause a memory leak.
And please don't use !! with Kotlin. Instead use null check or provide default value if null. Examples:
val int = object?.int ?: 0
val context = activity ?: return
val view = activity?.pgbSong ?: return // or if (view != null)
Recently, I was creating a test app to familiarize myself with RecyclerViewand the Android Palette library when I came across this semantic error in my fragment that deals with Palette. When I take a picture in the fragment, it saves the photo in the File for the current orientation, for the landscape orientation but when I rotate my phone back to portrait, the File resets back to null. I have discovered this based off my Log tests and reading stack traces.
Currently I've wrapped the null absolute path in a null check to prevent further errors but I'm not sure how to proceed. Below is my Kotlin file.
class PicFragment : Fragment() {
private var imgFile: File? = null
private lateinit var cameraPic: ImageView
private lateinit var cycleLayout: View
private var swatchIndex: Int = 0
override fun onCreateView(inflater: LayoutInflater?, container: ViewGroup?, savedInstanceState: Bundle?): View? {
val view: View? = inflater?.inflate(R.layout.camera_fragment, container, false)
// init
val cameraButton: ImageButton = view!!.findViewById(R.id.click_pic)
val colorCycler: ImageButton = view.findViewById(R.id.color_clicker)
cameraPic = view.findViewById(R.id.camera_pic)
cycleLayout = view.findViewById(R.id.color_selector)
val swatchDisplay: ImageView = view.findViewById(R.id.main_color)
val swatchName: TextView = view.findViewById(R.id.main_color_name)
// restoring the picture taken if it exists
if(savedInstanceState != null){
val path: String? = savedInstanceState.getString("imageFile")
swatchIndex = savedInstanceState.getInt("swatchIndex")
if(path != null) {
val bm: Bitmap = BitmapFactory.decodeFile(path)
cameraPic.setImageBitmap(bm)
animateColorSlides(cycleLayout, duration = 500)
}
}
// taking the picture (full size)
cameraButton.setOnClickListener { _ ->
val intent = Intent(MediaStore.ACTION_IMAGE_CAPTURE)
if (intent.resolveActivity(context.packageManager) != null){
imgFile = createFileName()
val photoURI = FileProvider.getUriForFile(context, "com.github.astronoodles.provider", imgFile)
grantUriPermissions(intent, photoURI)
intent.putExtra(MediaStore.EXTRA_OUTPUT, photoURI)
startActivityForResult(intent, 3)
}
}
// Palette Button (click to go through color values)
colorCycler.setOnClickListener { _ ->
if(cameraPic.drawable is BitmapDrawable){
val img: Bitmap = (cameraPic.drawable as BitmapDrawable).bitmap
Palette.from(img).generate { palette ->
val swatches = palette.swatches
Log.d(MainActivity.TAG, "Swatch Size: ${swatches.size}")
Log.d(MainActivity.TAG, "Counter: $swatchIndex")
val hexCode = "#${Integer.toHexString(swatches[swatchIndex++ % swatches.size].rgb)}"
swatchName.text = hexCode
animateColorDrawableFade(context, swatchDisplay, hexCode)
}
} else Log.e(MainActivity.TAG, "No bitmap found! Cannot cycle images...")
}
return view
}
override fun onSaveInstanceState(outState: Bundle?) {
super.onSaveInstanceState(outState)
outState?.putString("imageFile", imgFile?.absolutePath)
outState?.putInt("swatchIndex", swatchIndex)
}
/**
* Animates the color of an ImageView using its image drawable
* #author Michael + StackOverflow
* #since 6/24/18
* #param ctx Context needed to load the animations
* #param target Target ImageView for switching colors
* #param hexCode The hex code of the colors switching in
*/
private fun animateColorDrawableFade(ctx: Context, target: ImageView, hexCode: String){
val fadeOut = AnimationUtils.loadAnimation(ctx, android.R.anim.fade_out)
val fadeIn = AnimationUtils.loadAnimation(ctx, android.R.anim.fade_in)
fadeOut.setAnimationListener(object: Animation.AnimationListener {
override fun onAnimationStart(animation: Animation?) {}
override fun onAnimationRepeat(animation: Animation?) {}
override fun onAnimationEnd(animation: Animation?) {
target.setImageDrawable(ColorDrawable(Color.parseColor(hexCode)))
target.startAnimation(fadeIn)
}
})
target.startAnimation(fadeOut)
}
/**
* Helper method for animating a layout's visibility from invisible and visible
* #author Michael
* #param layout The layout to animate
* #param duration The length of the alpha animation.
*/
private fun animateColorSlides(layout: View, duration: Long){
layout.alpha = 0f
layout.visibility = View.VISIBLE
layout.animate().alpha(1f).setListener(null).duration = duration
}
/**
* Creates an unique name for the file as suggested here using a SimpleDateFormat
* #author Michael
* #returns A (temporary?) file linking to where the photo will be saved.
*/
private fun createFileName(): File {
val timeStamp: String = SimpleDateFormat("yyyyMd_km", Locale.US).format(Date())
val jpegTitle = "JPEG_${timeStamp}_"
val directory: File = context.getExternalFilesDir(Environment.DIRECTORY_PICTURES)
try {
return File.createTempFile(jpegTitle, ".png", directory)
} catch (e: IOException) {
e.printStackTrace()
}
return File(directory, "$jpegTitle.jpg")
}
/**
* Grants URI permissions for the file provider to successfully save the full size file. <br>
* Code borrowed from https://stackoverflow.com/questions/18249007/how-to-use-support-fileprovider-for-sharing-content-to-other-apps
* #param intent The intent to send the photo
* #param uri The URI retrieved from the FileProvider
* #author Michael and Leszek
*/
private fun grantUriPermissions(intent: Intent, uri: Uri){
val intentHandleList: List<ResolveInfo> = context.packageManager.queryIntentActivities(intent,
PackageManager.MATCH_DEFAULT_ONLY)
intentHandleList.forEach {
val packageName: String = it.activityInfo.packageName
context.grantUriPermission(packageName, uri, Intent.FLAG_GRANT_WRITE_URI_PERMISSION or
Intent.FLAG_GRANT_READ_URI_PERMISSION)
}
}
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
if(requestCode == 3 && resultCode == Activity.RESULT_OK){
val bitmap: Bitmap = BitmapFactory.decodeFile(imgFile!!.absolutePath)
cameraPic.setImageBitmap(bitmap)
animateColorSlides(cycleLayout, duration = 2000)
}
}
}
I also have my WRITE_EXTERNAL_STORAGE permission in the manifest if that helps.
Thanks.
From the Android activity lifecycle documentation, this is the relevant part:
If you override onSaveInstanceState(), you must call the superclass implementation if you want the default implementation to save the state of the view hierarchy
Which will give you something like this:
override fun onSaveInstanceState(outState: Bundle?) {
outState?.putString("imageFile", imgFile?.absolutePath)
outState?.putInt("swatchIndex", swatchIndex)
// Always call the superclass so it can save the view hierarchy state
super.onSaveInstanceState(outState)
}