Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Converter #1

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 17 additions & 0 deletions .idea/deploymentTargetDropDown.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions .idea/gradle.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 8 additions & 0 deletions .idea/misc.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 6 additions & 0 deletions .idea/vcs.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

18 changes: 18 additions & 0 deletions app/build.gradle
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
plugins {
id 'com.android.application'
id 'kotlin-android'
id 'kotlin-kapt'
}

android {
compileSdk 30
buildToolsVersion "30.0.3"

defaultConfig {
applicationId "ru.android.jpgtopngconverter"
Expand All @@ -16,6 +18,10 @@ android {
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}

buildFeatures {
viewBinding true
}

buildTypes {
release {
minifyEnabled false
Expand All @@ -33,6 +39,18 @@ android {

dependencies {

implementation "io.reactivex.rxjava2:rxjava:2.2.20"
implementation "io.reactivex.rxjava2:rxkotlin:2.4.0"
implementation "io.reactivex.rxjava2:rxandroid:2.1.1"

implementation 'com.github.terrakok:cicerone:7.0'
implementation 'com.github.kirich1409:viewbindingpropertydelegate:1.4.6'

implementation "com.github.moxy-community:moxy:2.2.2"
implementation "com.github.moxy-community:moxy-ktx:2.2.2"
implementation "com.github.moxy-community:moxy-androidx:2.2.2"
kapt "com.github.moxy-community:moxy-compiler:2.2.2"

implementation 'androidx.core:core-ktx:1.6.0'
implementation 'androidx.appcompat:appcompat:1.3.1'
implementation 'com.google.android.material:material:1.4.0'
Expand Down
1 change: 1 addition & 0 deletions app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="ru.android.jpgtopngconverter">

<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
Expand Down
35 changes: 32 additions & 3 deletions app/src/main/java/ru/android/jpgtopngconverter/MainActivity.kt
Original file line number Diff line number Diff line change
@@ -1,11 +1,40 @@
package ru.android.jpgtopngconverter

import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import com.github.terrakok.cicerone.androidx.AppNavigator
import io.reactivex.disposables.CompositeDisposable
import moxy.MvpAppCompatActivity
import ru.android.jpgtopngconverter.Navigator.Navigation.navigatorHolder
import ru.android.jpgtopngconverter.Navigator.Navigation.router
import ru.android.jpgtopngconverter.presentation.converter.ConverterScreen

class MainActivity : MvpAppCompatActivity() {

private val navigator = AppNavigator(this, android.R.id.content)

override fun onResumeFragments() {
super.onResumeFragments()
navigatorHolder.setNavigator(navigator)
}

private val disposables = CompositeDisposable()

class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)

savedInstanceState ?: router.newRootScreen(ConverterScreen)

}

override fun onPause() {
navigatorHolder.removeNavigator()
super.onPause()
}

override fun onDestroy() {
super.onDestroy()

disposables.dispose()
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package ru.android.jpgtopngconverter

import android.view.View

fun View.click(click: () -> Unit) = setOnClickListener { click() }
18 changes: 18 additions & 0 deletions app/src/main/java/ru/android/jpgtopngconverter/Navigator.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package ru.android.jpgtopngconverter

import com.github.terrakok.cicerone.Cicerone
import com.github.terrakok.cicerone.Router

class Navigator {

companion object Navigation {

private val cicerone : Cicerone<Router> by lazy {
Cicerone.create()
}

val navigatorHolder = cicerone.getNavigatorHolder()
val router = cicerone.router

}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package ru.android.jpgtopngconverter.models

import android.net.Uri
import io.reactivex.Single

interface Converter {

fun convert(uri: Uri): Single<Uri>
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package ru.android.jpgtopngconverter.models

import android.content.Context

object ConverterFactory {

fun create(context: Context): Converter {
return ConverterImpl(context)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package ru.android.jpgtopngconverter.models

import android.content.Context
import android.net.Uri
import io.reactivex.Single

class ConverterImpl(private val context: Context): Converter {

override fun convert(uri: Uri): Single<Uri> {
return ConverterSingle(context, uri)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
package ru.android.jpgtopngconverter.models

import android.content.Context
import android.graphics.Bitmap
import android.net.Uri
import android.provider.MediaStore
import io.reactivex.SingleObserver
import io.reactivex.android.MainThreadDisposable
import java.io.BufferedOutputStream
import java.io.File
import java.io.FileOutputStream
import java.util.concurrent.Executors

class ConverterListener(
private val context: Context,
private val uri: Uri,
private val observer: SingleObserver<in Uri>
): MainThreadDisposable(), Runnable {

private val converterTemporary = File.createTempFile("converter", null)
private val converterTask by lazy {
Executors
.newSingleThreadExecutor()
.submit(this)
}

fun convert() { converterTask }

override fun onDispose() {
converterTask
?.takeIf { !isDisposed }
?.takeIf { task -> !task.isDone }
?.takeIf { task -> !task.isCancelled }
?.cancel(true)
?.also { clearConverterTemporary() }
}

override fun run() {
try {
BufferedOutputStream(FileOutputStream(converterTemporary)).use { fos ->
MediaStore.Images.Media
.getBitmap(context.contentResolver, uri)
.compress(Bitmap.CompressFormat.PNG, 100, fos)
}

converterTask
?.takeIf { !isDisposed }
?.takeIf { task -> !task.isDone }
?.takeIf { task -> !task.isCancelled }
?.let { observer.onSuccess(uri) }
} catch (error: Throwable) {
observer.onError(error)
} finally {
clearConverterTemporary()
}
}

private fun clearConverterTemporary() {
converterTemporary
.takeIf(File::exists)
?.let(File::delete)
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package ru.android.jpgtopngconverter.models

import android.content.Context
import android.net.Uri
import io.reactivex.Single
import io.reactivex.SingleObserver

class ConverterSingle(
private val context: Context,
private val uri: Uri
): Single<Uri>() {

override fun subscribeActual(observer: SingleObserver<in Uri>) {
val listener = ConverterListener(context, uri, observer)
observer.onSubscribe(listener)
listener.convert()
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package ru.android.jpgtopngconverter.presentation

import moxy.MvpView
import moxy.viewstate.strategy.alias.SingleState

interface ScreenView: MvpView {

@SingleState
fun showError(error: Throwable)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
package ru.android.jpgtopngconverter.presentation.converter

import android.content.Intent
import android.content.Intent.ACTION_GET_CONTENT
import android.graphics.Bitmap
import android.net.Uri
import android.os.Bundle
import android.provider.MediaStore
import android.view.View
import android.widget.Toast
import androidx.fragment.app.Fragment
import by.kirich1409.viewbindingdelegate.viewBinding
import moxy.MvpAppCompatFragment
import moxy.ktx.moxyPresenter
import ru.android.jpgtopngconverter.R
import ru.android.jpgtopngconverter.R.layout.view_converter
import ru.android.jpgtopngconverter.click
import ru.android.jpgtopngconverter.databinding.ViewConverterBinding
import ru.android.jpgtopngconverter.models.ConverterFactory
import ru.android.jpgtopngconverter.presentation.scheduler.SchedulersFactory

class ConverterFragment : MvpAppCompatFragment(view_converter), ConverterView {

companion object {
fun newInstance(): Fragment = ConverterFragment()
}

private val presenter by moxyPresenter {
ConverterPresenter(
converter = ConverterFactory.create(requireContext()),
schedulers = SchedulersFactory.create()
)
}

private val vb: ViewConverterBinding by viewBinding()

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
requireActivity().title = getString(R.string.converter_title)
vb.button.click(::pickImage)
}

private fun pickImage() {
val getIntent = Intent(ACTION_GET_CONTENT)
getIntent.type = "image/*"
startActivityForResult(getIntent, 1)
}

override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)

data?.data?.let(presenter::convert)
?: Toast.makeText(requireContext(), "Изображение не выбрано", Toast.LENGTH_SHORT).show()
}

override fun showContent(uri: Uri?) {
val bitmap: Bitmap? =
uri?.let { MediaStore.Images.Media.getBitmap(requireContext().contentResolver, uri) }

vb.progress.visibility = View.GONE
vb.imageView.setImageBitmap(bitmap)

vb.button.click(::pickImage)
vb.button.text = getString(R.string.choose_image)
}

override fun showLoading() {
vb.progress.visibility = View.VISIBLE

vb.button.click(presenter::cancel)
vb.button.text = getString(R.string.cancel)
}

override fun showError(error: Throwable) {
Toast.makeText(requireContext(), error.message, Toast.LENGTH_SHORT).show()
}
}
Loading