update target SDK to 26 and handle fileprovider

This commit is contained in:
tibbi 2017-10-26 23:43:13 +02:00
parent 8686022c89
commit fa2a237593
14 changed files with 110 additions and 146 deletions

View file

@ -3,13 +3,13 @@ apply plugin: 'kotlin-android'
apply plugin: 'kotlin-android-extensions'
android {
compileSdkVersion 25
buildToolsVersion "25.0.3"
compileSdkVersion 26
buildToolsVersion "26.0.2"
defaultConfig {
applicationId "com.simplemobiletools.gallery"
minSdkVersion 16
targetSdkVersion 23
targetSdkVersion 26
versionCode 137
versionName "2.16.1"
}
@ -37,7 +37,7 @@ android {
}
dependencies {
compile 'com.simplemobiletools:commons:2.32.0'
compile 'com.simplemobiletools:commons:2.32.4'
compile 'com.davemorrissey.labs:subsampling-scale-image-view:3.6.0'
compile 'com.theartofdev.edmodo:android-image-cropper:2.4.0'
compile 'com.bignerdranch.android:recyclerview-multiselect:0.2'

View file

@ -83,16 +83,16 @@
android:label="@string/third_party_licences"
android:parentActivityName="com.simplemobiletools.commons.activities.AboutActivity"/>
<activity
android:name=".activities.SettingsActivity"
android:label="@string/settings"
android:parentActivityName=".activities.MainActivity"/>
<activity
android:name="com.simplemobiletools.commons.activities.CustomizationActivity"
android:label="@string/customize_colors"
android:parentActivityName=".activities.SettingsActivity"/>
<activity
android:name=".activities.SettingsActivity"
android:label="@string/settings"
android:parentActivityName=".activities.MainActivity"/>
<activity
android:name=".activities.PhotoVideoActivity"
android:configChanges="orientation|keyboardHidden|screenSize"

View file

@ -196,14 +196,15 @@ class EditActivity : SimpleActivity(), CropImageView.OnCropImageCompleteListener
}
private fun flipImage(horizontally: Boolean) {
if (horizontally)
if (horizontally) {
crop_image_view.flipImageHorizontally()
else
} else {
crop_image_view.flipImageVertically()
}
}
private fun editWith() {
openEditor(uri, true)
openEditor(uri)
isEditingWithThirdParty = true
}

View file

@ -434,7 +434,7 @@ class MainActivity : SimpleActivity(), DirectoryAdapter.DirOperationsListener {
}
} else if ((mIsPickImageIntent || mIsPickVideoIntent)) {
val path = resultData.data.path
val uri = Uri.fromFile(File(path))
val uri = getFilePublicUri(File(path), BuildConfig.APPLICATION_ID)
resultIntent.data = uri
resultIntent.flags = Intent.FLAG_GRANT_READ_URI_PERMISSION
}
@ -467,7 +467,7 @@ class MainActivity : SimpleActivity(), DirectoryAdapter.DirOperationsListener {
private fun fillPickedPaths(resultData: Intent, resultIntent: Intent) {
val paths = resultData.extras.getStringArrayList(PICKED_PATHS)
val uris = paths.map { Uri.fromFile(File(it)) } as ArrayList
val uris = paths.map { getFilePublicUri(File(it), BuildConfig.APPLICATION_ID) } as ArrayList
val clipData = ClipData("Attachment", arrayOf("image/*", "video/*"), ClipData.Item(uris.removeAt(0)))
uris.forEach {
@ -479,7 +479,7 @@ class MainActivity : SimpleActivity(), DirectoryAdapter.DirOperationsListener {
private fun fillIntentPath(resultData: Intent, resultIntent: Intent) {
val path = resultData.data.path
val uri = Uri.fromFile(File(path))
val uri = getFilePublicUri(File(path), BuildConfig.APPLICATION_ID)
val type = path.getMimeTypeFromPath()
resultIntent.setDataAndTypeAndNormalize(uri, type)
resultIntent.flags = Intent.FLAG_GRANT_READ_URI_PERMISSION or Intent.FLAG_GRANT_WRITE_URI_PERMISSION

View file

@ -24,6 +24,7 @@ import com.simplemobiletools.commons.extensions.*
import com.simplemobiletools.commons.helpers.PERMISSION_WRITE_STORAGE
import com.simplemobiletools.commons.models.RadioItem
import com.simplemobiletools.commons.views.MyScalableRecyclerView
import com.simplemobiletools.gallery.BuildConfig
import com.simplemobiletools.gallery.R
import com.simplemobiletools.gallery.adapters.MediaAdapter
import com.simplemobiletools.gallery.asynctasks.GetMediaAsynctask
@ -504,7 +505,8 @@ class MediaActivity : SimpleActivity(), MediaAdapter.MediaOperationsListener {
val file = File(path)
val isVideo = file.isVideoFast()
if (isVideo) {
openWith(file, false)
val uri = getFilePublicUri(file, BuildConfig.APPLICATION_ID)
openFile(uri)
} else {
Intent(this, ViewPagerActivity::class.java).apply {
putExtra(MEDIUM, path)

View file

@ -4,7 +4,7 @@ import android.os.Bundle
class PhotoActivity : PhotoVideoActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
PhotoVideoActivity.mIsVideo = false
mIsVideo = false
super.onCreate(savedInstanceState)
}
}

View file

@ -1,15 +1,14 @@
package com.simplemobiletools.gallery.activities
import android.content.Intent
import android.database.Cursor
import android.graphics.Color
import android.graphics.drawable.ColorDrawable
import android.net.Uri
import android.os.Bundle
import android.provider.MediaStore
import android.view.Menu
import android.view.MenuItem
import android.view.View
import com.simplemobiletools.commons.extensions.getFilenameFromUri
import com.simplemobiletools.commons.extensions.getRealPathFromURI
import com.simplemobiletools.commons.extensions.scanPath
import com.simplemobiletools.commons.extensions.toast
@ -34,9 +33,7 @@ open class PhotoVideoActivity : SimpleActivity(), ViewPagerFragment.FragmentList
lateinit var mUri: Uri
companion object {
var mIsVideo = false
}
var mIsVideo = false
public override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
@ -75,7 +72,8 @@ open class PhotoVideoActivity : SimpleActivity(), ViewPagerFragment.FragmentList
showSystemUI()
val bundle = Bundle()
val file = File(mUri.toString())
mMedium = Medium(file.name, mUri.toString(), mIsVideo, 0, 0, file.length())
mMedium = Medium(getFilenameFromUri(mUri), mUri.toString(), mIsVideo, 0, 0, file.length())
title = mMedium!!.name
bundle.putSerializable(MEDIUM, mMedium)
if (savedInstanceState == null) {
@ -85,22 +83,8 @@ open class PhotoVideoActivity : SimpleActivity(), ViewPagerFragment.FragmentList
supportFragmentManager.beginTransaction().replace(R.id.fragment_holder, mFragment).commit()
}
if (config.darkBackground)
if (config.darkBackground) {
fragment_holder.background = ColorDrawable(Color.BLACK)
val proj = arrayOf(MediaStore.Images.Media.TITLE)
var cursor: Cursor? = null
try {
cursor = contentResolver.query(mUri, proj, null, null, null)
if (cursor != null && cursor.count != 0) {
val columnIndex = cursor.getColumnIndexOrThrow(MediaStore.Images.Media.TITLE)
cursor.moveToFirst()
title = cursor.getString(columnIndex)
}
} catch (e: Exception) {
title = mMedium?.name ?: ""
} finally {
cursor?.close()
}
window.decorView.setOnSystemUiVisibilityChangeListener { visibility ->
@ -137,10 +121,10 @@ open class PhotoVideoActivity : SimpleActivity(), ViewPagerFragment.FragmentList
return true
when (item.itemId) {
R.id.menu_set_as -> trySetAs(File(mMedium!!.path))
R.id.menu_open_with -> openWith(File(mMedium!!.path))
R.id.menu_share -> shareUri(mMedium!!, mUri)
R.id.menu_edit -> openFileEditor(File(mMedium!!.path))
R.id.menu_set_as -> setAs(mUri)
R.id.menu_open_with -> openFile(mUri)
R.id.menu_share -> shareUri(mUri)
R.id.menu_edit -> openEditor(mUri)
else -> return super.onOptionsItemSelected(item)
}
return true

View file

@ -5,7 +5,7 @@ import android.os.Bundle
class VideoActivity : PhotoVideoActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
PhotoVideoActivity.mIsVideo = true
mIsVideo = true
super.onCreate(savedInstanceState)
}
}

View file

@ -246,18 +246,18 @@ class ViewPagerActivity : SimpleActivity(), ViewPager.OnPageChangeListener, View
return true
when (item.itemId) {
R.id.menu_set_as -> trySetAs(getCurrentFile())
R.id.menu_set_as -> setAs(Uri.fromFile(getCurrentFile()))
R.id.slideshow -> initSlideshow()
R.id.menu_copy_to -> copyMoveTo(true)
R.id.menu_move_to -> copyMoveTo(false)
R.id.menu_open_with -> openWith(getCurrentFile())
R.id.menu_open_with -> openFile(Uri.fromFile(getCurrentFile()))
R.id.menu_hide -> toggleFileVisibility(true)
R.id.menu_unhide -> toggleFileVisibility(false)
R.id.menu_share_1 -> shareMedium(getCurrentMedium()!!)
R.id.menu_share_2 -> shareMedium(getCurrentMedium()!!)
R.id.menu_delete -> checkDeleteConfirmation()
R.id.menu_rename -> renameFile()
R.id.menu_edit -> openFileEditor(getCurrentFile())
R.id.menu_edit -> openEditor(Uri.fromFile(getCurrentFile()))
R.id.menu_properties -> showProperties()
R.id.show_on_map -> showOnMap()
R.id.menu_rotate -> rotateImage()

View file

@ -1,6 +1,7 @@
package com.simplemobiletools.gallery.adapters
import android.graphics.PorterDuff
import android.net.Uri
import android.os.Build
import android.support.v7.view.ActionMode
import android.support.v7.widget.RecyclerView
@ -159,7 +160,7 @@ class MediaAdapter(val activity: SimpleActivity, var media: MutableList<Medium>,
}
private fun editFile() {
activity.openFileEditor(getCurrentFile())
activity.openEditor(Uri.fromFile(getCurrentFile()))
actMode?.finish()
}

View file

@ -2,16 +2,10 @@ package com.simplemobiletools.gallery.extensions
import android.app.Activity
import android.content.Intent
import android.database.Cursor
import android.net.Uri
import android.os.Build
import android.provider.MediaStore
import android.support.v7.app.AppCompatActivity
import android.util.DisplayMetrics
import android.view.KeyCharacterMap
import android.view.KeyEvent
import android.view.View
import android.view.ViewConfiguration
import com.bumptech.glide.Glide
import com.bumptech.glide.load.DecodeFormat
import com.bumptech.glide.load.engine.DiskCacheStrategy
@ -34,12 +28,12 @@ import com.simplemobiletools.gallery.views.MySquareImageView
import java.io.File
import java.util.*
fun Activity.shareUri(medium: Medium, uri: Uri) {
fun Activity.shareUri(uri: Uri) {
val shareTitle = resources.getString(R.string.share_via)
Intent().apply {
action = Intent.ACTION_SEND
putExtra(Intent.EXTRA_STREAM, uri)
type = medium.getMimeType()
putExtra(Intent.EXTRA_STREAM, ensurePublicUri(uri))
type = getMimeTypeFromUri(uri)
addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
startActivity(Intent.createChooser(this, shareTitle))
}
@ -48,12 +42,12 @@ fun Activity.shareUri(medium: Medium, uri: Uri) {
fun Activity.shareMedium(medium: Medium) {
val shareTitle = resources.getString(R.string.share_via)
val file = File(medium.path)
val uri = Uri.fromFile(file)
val uri = getFilePublicUri(file, BuildConfig.APPLICATION_ID)
Intent().apply {
action = Intent.ACTION_SEND
putExtra(Intent.EXTRA_STREAM, uri)
type = medium.getMimeType()
type = getMimeTypeFromUri(uri)
addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
startActivity(Intent.createChooser(this, shareTitle))
}
@ -61,7 +55,7 @@ fun Activity.shareMedium(medium: Medium) {
fun Activity.shareMedia(media: List<Medium>) {
val shareTitle = resources.getString(R.string.share_via)
val uris = media.map { Uri.fromFile(File(it.path)) } as ArrayList
val uris = media.map { getFilePublicUri(File(it.path), BuildConfig.APPLICATION_ID) } as ArrayList
Intent().apply {
action = Intent.ACTION_SEND_MULTIPLE
@ -72,119 +66,55 @@ fun Activity.shareMedia(media: List<Medium>) {
}
}
fun Activity.trySetAs(file: File) {
try {
var uri = Uri.fromFile(file)
if (!setAs(uri, file)) {
uri = getFileContentUri(file)
setAs(uri, file, false)
}
} catch (e: Exception) {
toast(R.string.unknown_error_occurred)
}
}
fun Activity.setAs(uri: Uri, file: File, showToast: Boolean = true): Boolean {
var success = false
fun Activity.setAs(uri: Uri) {
val newUri = ensurePublicUri(uri)
Intent().apply {
action = Intent.ACTION_ATTACH_DATA
setDataAndType(uri, file.getMimeType())
setDataAndType(newUri, getMimeTypeFromUri(newUri))
addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
val chooser = Intent.createChooser(this, getString(R.string.set_as))
success = if (resolveActivity(packageManager) != null) {
if (resolveActivity(packageManager) != null) {
startActivityForResult(chooser, REQUEST_SET_AS)
true
} else {
if (showToast) {
toast(R.string.no_capable_app_found)
}
false
toast(R.string.no_capable_app_found)
}
}
return success
}
fun Activity.getFileContentUri(file: File): Uri? {
val uri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI
val projection = arrayOf(MediaStore.Images.Media._ID)
val selection = "${MediaStore.Images.Media.DATA} = ?"
val selectionArgs = arrayOf(file.absolutePath)
var cursor: Cursor? = null
try {
cursor = contentResolver.query(uri, projection, selection, selectionArgs, null)
if (cursor?.moveToFirst() == true) {
val id = cursor.getIntValue(MediaStore.Images.Media._ID)
return Uri.withAppendedPath(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, "$id")
}
} finally {
cursor?.close()
}
return null
}
fun Activity.openWith(file: File, forceChooser: Boolean = true) {
val uri = Uri.fromFile(file)
fun Activity.openFile(uri: Uri) {
val newUri = ensurePublicUri(uri)
Intent().apply {
action = Intent.ACTION_VIEW
setDataAndType(uri, file.getMimeType())
setDataAndType(newUri, getMimeTypeFromUri(newUri))
addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
putExtra(IS_FROM_GALLERY, true)
if (resolveActivity(packageManager) != null) {
val chooser = Intent.createChooser(this, getString(R.string.open_with))
startActivity(if (forceChooser) chooser else this)
startActivity(chooser)
} else {
toast(R.string.no_app_found)
}
}
}
fun Activity.openFileEditor(file: File) {
openEditor(Uri.fromFile(file))
}
fun Activity.openEditor(uri: Uri, forceChooser: Boolean = false) {
fun Activity.openEditor(uri: Uri) {
val newUri = ensurePublicUri(uri)
Intent().apply {
action = Intent.ACTION_EDIT
setDataAndType(uri, "image/*")
setDataAndType(newUri, getMimeTypeFromUri(newUri))
addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
if (resolveActivity(packageManager) != null) {
val chooser = Intent.createChooser(this, getString(R.string.edit_image_with))
startActivityForResult(if (forceChooser) chooser else this, REQUEST_EDIT_IMAGE)
startActivityForResult(chooser, REQUEST_EDIT_IMAGE)
} else {
toast(R.string.no_editor_found)
}
}
}
fun Activity.hasNavBar(): Boolean {
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
val display = windowManager.defaultDisplay
val realDisplayMetrics = DisplayMetrics()
display.getRealMetrics(realDisplayMetrics)
val realHeight = realDisplayMetrics.heightPixels
val realWidth = realDisplayMetrics.widthPixels
val displayMetrics = DisplayMetrics()
display.getMetrics(displayMetrics)
val displayHeight = displayMetrics.heightPixels
val displayWidth = displayMetrics.widthPixels
realWidth - displayWidth > 0 || realHeight - displayHeight > 0
} else {
val hasMenuKey = ViewConfiguration.get(applicationContext).hasPermanentMenuKey()
val hasBackKey = KeyCharacterMap.deviceHasKey(KeyEvent.KEYCODE_BACK)
!hasMenuKey && !hasBackKey
}
}
fun Activity.launchCamera() {
val intent = Intent(MediaStore.INTENT_ACTION_STILL_IMAGE_CAMERA)
if (intent.resolveActivity(packageManager) != null) {

View file

@ -5,12 +5,18 @@ import android.content.Intent
import android.content.res.Configuration
import android.graphics.Point
import android.media.AudioManager
import android.net.Uri
import android.os.Build
import android.view.WindowManager
import com.simplemobiletools.commons.extensions.getFilePublicUri
import com.simplemobiletools.commons.extensions.getMimeTypeFromPath
import com.simplemobiletools.commons.extensions.getRealPathFromURI
import com.simplemobiletools.commons.extensions.humanizePath
import com.simplemobiletools.gallery.BuildConfig
import com.simplemobiletools.gallery.activities.SettingsActivity
import com.simplemobiletools.gallery.helpers.Config
import com.simplemobiletools.gallery.models.Directory
import java.io.File
val Context.portrait get() = resources.configuration.orientation == Configuration.ORIENTATION_PORTRAIT
val Context.audioManager get() = getSystemService(Context.AUDIO_SERVICE) as AudioManager
@ -73,3 +79,21 @@ fun Context.getSortedDirectories(source: ArrayList<Directory>): ArrayList<Direct
dirs.sort()
return movePinnedDirectoriesToFront(dirs)
}
fun Context.getMimeTypeFromUri(uri: Uri): String {
val path = getRealPathFromURI(uri)
var mimetype = uri.path.getMimeTypeFromPath()
if (mimetype.isEmpty()) {
mimetype = contentResolver.getType(uri)
}
return mimetype
}
fun Context.ensurePublicUri(uri: Uri): Uri {
return if (uri.scheme == "content") {
uri
} else {
val file = File(uri.path)
getFilePublicUri(file, BuildConfig.APPLICATION_ID)
}
}

View file

@ -35,7 +35,6 @@ import it.sephiroth.android.library.exif2.ExifInterface
import kotlinx.android.synthetic.main.pager_photo_item.view.*
import java.io.File
import java.io.FileOutputStream
import java.io.IOException
class PhotoFragment : ViewPagerFragment() {
private var isFragmentVisible = false
@ -50,10 +49,9 @@ class PhotoFragment : ViewPagerFragment() {
view = inflater.inflate(R.layout.pager_photo_item, container, false) as ViewGroup
medium = arguments.getSerializable(MEDIUM) as Medium
if (medium.path.startsWith("content://")) {
val originalPath = medium.path
medium.path = context.getRealPathFromURI(Uri.parse(medium.path)) ?: ""
medium.path = context.getRealPathFromURI(Uri.parse(originalPath)) ?: ""
if (medium.path.isEmpty()) {
var out: FileOutputStream? = null
@ -63,7 +61,6 @@ class PhotoFragment : ViewPagerFragment() {
exif.readExif(inputStream, ExifInterface.Options.OPTION_ALL)
val tag = exif.getTag(ExifInterface.TAG_ORIENTATION)
val orientation = tag?.getValueAsInt(-1) ?: -1
inputStream = context.contentResolver.openInputStream(Uri.parse(originalPath))
val original = BitmapFactory.decodeStream(inputStream)
val rotated = rotateViaMatrix(original, orientation)
@ -78,10 +75,7 @@ class PhotoFragment : ViewPagerFragment() {
activity.toast(R.string.unknown_error_occurred)
return view
} finally {
try {
out?.close()
} catch (e: IOException) {
}
out?.close()
}
}
}

View file

@ -65,7 +65,11 @@ class VideoFragment : ViewPagerFragment(), SurfaceHolder.Callback, SeekBar.OnSee
mTimeHolder = mView.video_time_holder
medium = arguments.getSerializable(MEDIUM) as Medium
mIsFragmentVisible = activity is VideoActivity // setMenuVisibility is not called at VideoActivity (third party intent)
// setMenuVisibility is not called at VideoActivity (third party intent)
if (!mIsFragmentVisible && activity is VideoActivity) {
mIsFragmentVisible = true
}
setupPlayer()
if (savedInstanceState != null) {
mCurrTime = savedInstanceState.getInt(PROGRESS)
@ -287,7 +291,7 @@ class VideoFragment : ViewPagerFragment(), SurfaceHolder.Callback, SeekBar.OnSee
var right = res.getDimension(R.dimen.timer_padding).toInt()
var bottom = 0
if (activity.hasNavBar()) {
if (hasNavBar()) {
if (res.configuration.orientation == Configuration.ORIENTATION_PORTRAIT) {
bottom += height
} else {
@ -305,6 +309,30 @@ class VideoFragment : ViewPagerFragment(), SurfaceHolder.Callback, SeekBar.OnSee
mTimeHolder.beInvisible()
}
private fun hasNavBar(): Boolean {
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
val display = context.windowManager.defaultDisplay
val realDisplayMetrics = DisplayMetrics()
display.getRealMetrics(realDisplayMetrics)
val realHeight = realDisplayMetrics.heightPixels
val realWidth = realDisplayMetrics.widthPixels
val displayMetrics = DisplayMetrics()
display.getMetrics(displayMetrics)
val displayHeight = displayMetrics.heightPixels
val displayWidth = displayMetrics.widthPixels
realWidth - displayWidth > 0 || realHeight - displayHeight > 0
} else {
val hasMenuKey = ViewConfiguration.get(context).hasPermanentMenuKey()
val hasBackKey = KeyCharacterMap.deviceHasKey(KeyEvent.KEYCODE_BACK)
!hasMenuKey && !hasBackKey
}
}
private fun setupTimeHolder() {
mSeekBar!!.max = mDuration
mView.video_duration.text = mDuration.getFormattedDuration()