diff --git a/app/build.gradle b/app/build.gradle index 9fd6e9761..82449e414 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -98,15 +98,15 @@ dependencies { implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$versions.kotlin" // Support library - implementation 'androidx.appcompat:appcompat:1.2.0-alpha03' - implementation 'androidx.recyclerview:recyclerview:1.2.0-alpha01' + implementation 'androidx.appcompat:appcompat:1.3.0-alpha01' + implementation 'androidx.recyclerview:recyclerview:1.2.0-alpha04' implementation "android.arch.work:work-runtime:1.0.1" - implementation 'androidx.exifinterface:exifinterface:1.1.0' + implementation 'androidx.exifinterface:exifinterface:1.2.0' implementation 'androidx.cardview:cardview:1.0.0' - implementation 'androidx.preference:preference:1.1.0-alpha02' + implementation 'androidx.preference:preference:1.1.1' implementation 'androidx.legacy:legacy-preference-v14:1.0.0' - implementation 'com.google.android.material:material:1.2.0-alpha05' - implementation 'com.takisoft.preferencex:preferencex:1.0.0-alpha2' + implementation 'com.google.android.material:material:1.3.0-alpha01' + implementation 'com.takisoft.preferencex:preferencex:1.1.0' implementation 'androidx.legacy:legacy-support-v4:1.0.0' implementation 'androidx.browser:browser:1.2.0' implementation 'androidx.constraintlayout:constraintlayout:1.1.3' @@ -117,7 +117,7 @@ dependencies { implementation 'com.google.android.exoplayer:extension-okhttp:2.9.6' implementation 'com.davemorrissey.labs:subsampling-scale-image-view:3.10.0' implementation 'com.google.code.gson:gson:2.8.5' - implementation 'androidx.core:core-ktx:1.3.0-alpha02' + implementation 'androidx.core:core-ktx:1.5.0-alpha01' // Utils implementation 'com.bugsnag:bugsnag-android:4.5.0' diff --git a/app/src/main/kotlin/io/github/feelfreelinux/wykopmobilny/api/patrons/PatronsRetrofitApi.kt b/app/src/main/kotlin/io/github/feelfreelinux/wykopmobilny/api/patrons/PatronsRetrofitApi.kt index 2250953d8..286a525eb 100644 --- a/app/src/main/kotlin/io/github/feelfreelinux/wykopmobilny/api/patrons/PatronsRetrofitApi.kt +++ b/app/src/main/kotlin/io/github/feelfreelinux/wykopmobilny/api/patrons/PatronsRetrofitApi.kt @@ -10,6 +10,6 @@ import retrofit2.http.Headers interface PatronsRetrofitApi { @Headers("@: $REMOVE_USERKEY_HEADER") - @GET("https://patrons.grzywok.eu/api/patrons") + @GET("https://raw.githubusercontent.com/alufers/owm-patrons/master/patrons.json") fun getPatrons(): Single } \ No newline at end of file diff --git a/app/src/main/kotlin/io/github/feelfreelinux/wykopmobilny/ui/modules/embedview/EmbedViewActivity.kt b/app/src/main/kotlin/io/github/feelfreelinux/wykopmobilny/ui/modules/embedview/EmbedViewActivity.kt index 9dc10a97a..a7cd0b4f6 100644 --- a/app/src/main/kotlin/io/github/feelfreelinux/wykopmobilny/ui/modules/embedview/EmbedViewActivity.kt +++ b/app/src/main/kotlin/io/github/feelfreelinux/wykopmobilny/ui/modules/embedview/EmbedViewActivity.kt @@ -11,7 +11,7 @@ import android.os.Environment import android.os.Handler import android.os.Looper import android.provider.MediaStore -import android.util.Base64 +import android.util.Log import android.view.Menu import android.view.MenuItem import android.webkit.MimeTypeMap @@ -45,7 +45,6 @@ import okhttp3.Request import okio.Okio import java.io.File import java.net.URL -import java.nio.charset.Charset import javax.inject.Inject class EmbedViewActivity : BaseActivity(), EmbedView { @@ -96,21 +95,6 @@ class EmbedViewActivity : BaseActivity(), EmbedView { videoView.isFocusable = false } - private fun decodeCoubUrl(input: String): String? { - val source = StringBuilder(input) - for (a in 0 until source.length) { - val c = source[a] - val lower = Character.toLowerCase(c) - source.setCharAt(a, if (c == lower) Character.toUpperCase(c) else lower) - } - return try { - String(Base64.decode(source.toString(), Base64.DEFAULT), Charset.forName("UTF-8")) - } catch (ignore: Exception) { - null - } - - } - override fun checkEmbedSettings() { if (settingsPreferencesApi.enableEmbedPlayer) { openBrowser(extraUrl) @@ -223,40 +207,52 @@ class EmbedViewActivity : BaseActivity(), EmbedView { private fun saveFile() { Single.create { val url = presenter.mp4Url - val path = File( - Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES), - PhotoViewActions.SAVED_FOLDER - ) - val file = File(path, url.substringAfterLast("/")) - val request = Request.Builder() - .url(url) - .build() - val result = OkHttpClient().newCall(request).execute() - if (result.isSuccessful) { - val sink = Okio.buffer(Okio.sink(file)) - sink.writeAll(result.body()!!.source()) - sink.close() - } else { - it.onError(Exception()) + val relativeLocation = Environment.DIRECTORY_MOVIES + var fileUrl = url.substringAfterLast("/") + if (fileUrl.contains("?")) { + fileUrl = fileUrl.substringBefore("?") } - it.onSuccess(path.path) + val contentValues = ContentValues() + contentValues.put(MediaStore.MediaColumns.DISPLAY_NAME, fileUrl) + contentValues.put(MediaStore.MediaColumns.MIME_TYPE, "video/mp4") + contentValues.put(MediaStore.Images.Media.DATE_TAKEN, System.currentTimeMillis()) + contentValues.put(MediaStore.MediaColumns.RELATIVE_PATH, relativeLocation + "/" + PhotoViewActions.SAVED_FOLDER) + val contentUri = MediaStore.Video.Media.EXTERNAL_CONTENT_URI; + val uri = contentResolver.insert(contentUri, contentValues); + uri?.let { + uri -> + val stream = contentResolver.openOutputStream(uri) + val request = Request.Builder() + .url(url) + .build() + val result = OkHttpClient().newCall(request).execute() + if (result.isSuccessful) { + val sink = Okio.buffer(Okio.sink(stream)) + sink.writeAll(result.body()!!.source()) + sink.close() + } else { + it.onError(Exception()) + } + } + it.onSuccess(relativeLocation + "/" + PhotoViewActions.SAVED_FOLDER) }.subscribeOn(WykopSchedulers().backgroundThread()) .observeOn(WykopSchedulers().mainThread()) .subscribe({ - val values = ContentValues() - - values.put(MediaStore.Images.Media.DATE_TAKEN, System.currentTimeMillis()) - - values.put(MediaStore.Images.Media.MIME_TYPE, getMimeType(it)) - - - values.put(MediaStore.MediaColumns.DATA, it) +// val values = ContentValues() +// +// values.put(MediaStore.Images.Media.DATE_TAKEN, System.currentTimeMillis()) +// +// values.put(MediaStore.Images.Media.MIME_TYPE, getMimeType(it)) +// +// +// values.put(MediaStore.MediaColumns.DATA, it) Toast.makeText(this, "Zapisano plik", Toast.LENGTH_SHORT).show() - contentResolver.insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values) + // contentResolver.insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values) }, { + throw it Toast.makeText(this, "Błąd podczas zapisu pliku", Toast.LENGTH_SHORT).show() }) } diff --git a/app/src/main/kotlin/io/github/feelfreelinux/wykopmobilny/ui/modules/photoview/PhotoViewActions.kt b/app/src/main/kotlin/io/github/feelfreelinux/wykopmobilny/ui/modules/photoview/PhotoViewActions.kt index 73abe29f4..284f498b1 100755 --- a/app/src/main/kotlin/io/github/feelfreelinux/wykopmobilny/ui/modules/photoview/PhotoViewActions.kt +++ b/app/src/main/kotlin/io/github/feelfreelinux/wykopmobilny/ui/modules/photoview/PhotoViewActions.kt @@ -9,6 +9,7 @@ import android.graphics.drawable.Drawable import android.os.Environment import android.provider.MediaStore import android.provider.MediaStore.Images +import android.util.Log import android.webkit.MimeTypeMap import android.widget.Toast import androidx.core.app.ActivityCompat @@ -21,6 +22,7 @@ import io.reactivex.Single import io.reactivex.SingleOnSubscribe import java.io.File + interface PhotoViewCallbacks { fun shareImage(url: String) fun getDrawable(): Drawable? @@ -43,19 +45,24 @@ class PhotoViewActions(val context: Context) : PhotoViewCallbacks { Single.create(SingleOnSubscribe { val file = Glide.with(context).downloadOnly().load(url).submit().get() - val newFile = File( - Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES), - "$SAVED_FOLDER/$SHARED_FOLDER/" + url.substringAfterLast("/") - ) - file.copyTo(newFile, true) + var fileUrl = url.substringAfterLast("/") + if (fileUrl.contains("?")) { + fileUrl = fileUrl.substringBefore("?") + } + val newFile = File(file.path.substringBeforeLast("/") + "/" + fileUrl) + file.renameTo(newFile) + + it.onSuccess(newFile) }).subscribeOn(WykopSchedulers().backgroundThread()).observeOn(WykopSchedulers().mainThread()).subscribe { file: File -> - addImageToGallery(file.path, context) + val url = FileProvider.getUriForFile(context, context.applicationContext.packageName + ".fileprovider", file) val share = Intent(Intent.ACTION_SEND) share.type = getMimeType(url.path!!) share.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION) share.putExtra(Intent.EXTRA_STREAM, url) + share.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + photoView.startActivityForResult(Intent.createChooser(share, "Udostępnij obrazek"), PhotoViewActivity.SHARE_REQUEST_CODE) } } @@ -72,14 +79,29 @@ class PhotoViewActions(val context: Context) : PhotoViewCallbacks { return } Completable.fromAction { - val file = Glide.with(context).downloadOnly().load(url).submit().get() - var path = File( - Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES), - SAVED_FOLDER - ) - path = File(path, photoView.url.substringAfterLast('/')) - file.copyTo(path, true) - addImageToGallery(path.path, context) + + val relativeLocation = Environment.DIRECTORY_PICTURES + var fileUrl = url.substringAfterLast("/") + if (fileUrl.contains("?")) { + fileUrl = fileUrl.substringBefore("?") + } + val contentValues = ContentValues() + contentValues.put(MediaStore.MediaColumns.DISPLAY_NAME, fileUrl) + contentValues.put(MediaStore.MediaColumns.MIME_TYPE, getMimeType(url)) + contentValues.put(MediaStore.Images.Media.DATE_TAKEN, System.currentTimeMillis()) + contentValues.put(MediaStore.MediaColumns.RELATIVE_PATH, relativeLocation + "/" + PhotoViewActions.SAVED_FOLDER) + val contentUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI; + val uri = context.contentResolver.insert(contentUri, contentValues) + uri?.let { + uri -> + val stream = context.contentResolver.openOutputStream(uri) + + val file = Glide.with(context).downloadOnly().load(url).submit().get() + + file.inputStream().copyTo(stream!!) + + } + }.subscribeOn(WykopSchedulers().backgroundThread()) .observeOn(WykopSchedulers().mainThread()).subscribe({ showToastMessage("Zapisano plik") diff --git a/app/src/main/kotlin/io/github/feelfreelinux/wykopmobilny/ui/modules/settings/SettingsAppearance.kt b/app/src/main/kotlin/io/github/feelfreelinux/wykopmobilny/ui/modules/settings/SettingsAppearance.kt index b384f541c..26b842720 100644 --- a/app/src/main/kotlin/io/github/feelfreelinux/wykopmobilny/ui/modules/settings/SettingsAppearance.kt +++ b/app/src/main/kotlin/io/github/feelfreelinux/wykopmobilny/ui/modules/settings/SettingsAppearance.kt @@ -30,34 +30,34 @@ class SettingsAppearance : PreferenceFragmentCompat(), SharedPreferences.OnShare override fun onCreatePreferencesFix(savedInstanceState: Bundle?, rootKey: String?) { addPreferencesFromResource(R.xml.app_preferences_appearance) - findPreference("useAmoledTheme").isEnabled = settingsApi.useDarkTheme - findPreference("cutImageProportion").isEnabled = settingsApi.cutImages - findPreference("linkImagePosition").isEnabled = !settingsApi.linkSimpleList && settingsApi.linkShowImage - findPreference("linkShowAuthor").isEnabled = !settingsApi.linkSimpleList + findPreference("useAmoledTheme")?.isEnabled = settingsApi.useDarkTheme + findPreference("cutImageProportion")?.isEnabled = settingsApi.cutImages + findPreference("linkImagePosition")?.isEnabled = !settingsApi.linkSimpleList && settingsApi.linkShowImage + findPreference("linkShowAuthor")?.isEnabled = !settingsApi.linkSimpleList - (findPreference("hotEntriesScreen") as ListPreference).apply { + (findPreference("hotEntriesScreen") as ListPreference?)!!.apply { summary = entry } - (findPreference("fontSize") as ListPreference).apply { + (findPreference("fontSize") as ListPreference?)!!.apply { summary = entry } - (findPreference("defaultScreen") as ListPreference).apply { + (findPreference("defaultScreen") as ListPreference?)!!.apply { summary = entry } - (findPreference("linkImagePosition") as ListPreference).apply { + (findPreference("linkImagePosition") as ListPreference?)!!.apply { summary = entry } } override fun onSharedPreferenceChanged(sharedPrefs: SharedPreferences, key: String) { val pref = findPreference(key) - findPreference("useAmoledTheme").isEnabled = settingsApi.useDarkTheme - findPreference("cutImageProportion").isEnabled = settingsApi.cutImages - findPreference("linkShowAuthor").isEnabled = !settingsApi.linkSimpleList - findPreference("linkImagePosition").isEnabled = !settingsApi.linkSimpleList + findPreference("useAmoledTheme")?.isEnabled = settingsApi.useDarkTheme + findPreference("cutImageProportion")?.isEnabled = settingsApi.cutImages + findPreference("linkShowAuthor")?.isEnabled = !settingsApi.linkSimpleList + findPreference("linkImagePosition")?.isEnabled = !settingsApi.linkSimpleList if (pref is ListPreference) { pref.setSummary(pref.entry) diff --git a/app/src/main/kotlin/io/github/feelfreelinux/wykopmobilny/ui/modules/settings/SettingsFragment.kt b/app/src/main/kotlin/io/github/feelfreelinux/wykopmobilny/ui/modules/settings/SettingsFragment.kt index 1dbb97005..a20f0b41a 100644 --- a/app/src/main/kotlin/io/github/feelfreelinux/wykopmobilny/ui/modules/settings/SettingsFragment.kt +++ b/app/src/main/kotlin/io/github/feelfreelinux/wykopmobilny/ui/modules/settings/SettingsFragment.kt @@ -42,29 +42,29 @@ class SettingsFragment : PreferenceFragmentCompat(), SharedPreferences.OnSharedP override fun onCreatePreferencesFix(savedInstanceState: Bundle?, rootKey: String?) { addPreferencesFromResource(R.xml.app_preferences) - (findPreference("piggyBackPushNotifications") as CheckBoxPreference).isEnabled = + (findPreference("piggyBackPushNotifications") as CheckBoxPreference?)?.isEnabled = (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.JELLY_BEAN_MR2) - (findPreference("notificationsSchedulerDelay") as ListPreference).apply { + (findPreference("notificationsSchedulerDelay") as ListPreference?)?.apply { summary = entry } - (findPreference("showNotifications") as CheckBoxPreference).isEnabled = - !(findPreference("piggyBackPushNotifications") as CheckBoxPreference).isChecked - (findPreference("appearance") as Preference).setOnPreferenceClickListener { + (findPreference("showNotifications") as CheckBoxPreference?)?.isEnabled = + !(findPreference("piggyBackPushNotifications") as CheckBoxPreference?)!!.isChecked + (findPreference("appearance") as Preference?)?.setOnPreferenceClickListener { (activity as SettingsActivity).openFragment(SettingsAppearance(), "appearance") true } - (findPreference("blacklist") as Preference).setOnPreferenceClickListener { - userManagerApi.runIfLoggedIn(activity!!) { - startActivity(BlacklistActivity.createIntent(activity!!)) + (findPreference("blacklist") as Preference?)!!.setOnPreferenceClickListener { + userManagerApi.runIfLoggedIn(requireActivity()) { + startActivity(BlacklistActivity.createIntent(requireActivity())) } true } - (findPreference("clearhistory") as Preference).setOnPreferenceClickListener { - SuggestionDatabase(context!!).clearDb() - Toast.makeText(context!!, "Wyczyszczono historię wyszukiwarki", Toast.LENGTH_LONG).show() + (findPreference("clearhistory") as Preference?)?.setOnPreferenceClickListener { + SuggestionDatabase(requireContext()).clearDb() + Toast.makeText(requireContext(), "Wyczyszczono historię wyszukiwarki", Toast.LENGTH_LONG).show() true } } @@ -74,8 +74,8 @@ class SettingsFragment : PreferenceFragmentCompat(), SharedPreferences.OnSharedP if (pref is CheckBoxPreference) { when (pref.key) { "showNotifications" -> { - findPreference("notificationsSchedulerDelay").isEnabled = pref.isChecked - findPreference("piggyBackPushNotifications").isEnabled = pref.isChecked + findPreference("notificationsSchedulerDelay")?.isEnabled = pref.isChecked + findPreference("piggyBackPushNotifications")?.isEnabled = pref.isChecked if (pref.isChecked) { WykopNotificationsJob.schedule(settingsApi) } else { @@ -84,8 +84,8 @@ class SettingsFragment : PreferenceFragmentCompat(), SharedPreferences.OnSharedP } "piggyBackPushNotifications" -> { - findPreference("notificationsSchedulerDelay").isEnabled = !pref.isChecked - (findPreference("showNotifications") as Preference).isEnabled = !pref.isChecked + findPreference("notificationsSchedulerDelay")?.isEnabled = !pref.isChecked + (findPreference("showNotifications") as Preference?)?.isEnabled = !pref.isChecked if (pref.isChecked) { if (isOfficialAppInstalled()) { if (isNotificationAccessEnabled()) { @@ -93,7 +93,7 @@ class SettingsFragment : PreferenceFragmentCompat(), SharedPreferences.OnSharedP } else { pref.isChecked = false onSharedPreferenceChanged(sharedPrefs, key) - Toast.makeText(activity!!, R.string.toast_allow_notification_access, Toast.LENGTH_SHORT).show() + Toast.makeText(requireActivity(), R.string.toast_allow_notification_access, Toast.LENGTH_SHORT).show() if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP_MR1) { startActivity(Intent(Settings.ACTION_NOTIFICATION_LISTENER_SETTINGS)) } else { @@ -106,7 +106,7 @@ class SettingsFragment : PreferenceFragmentCompat(), SharedPreferences.OnSharedP openWykopMarketPage() } } else { - if ((findPreference("showNotifications") as CheckBoxPreference).isChecked) { + if ((findPreference("showNotifications") as CheckBoxPreference?)!!.isChecked) { WykopNotificationsJob.schedule(settingsApi) } else { WykopNotificationsJob.cancel() @@ -117,7 +117,7 @@ class SettingsFragment : PreferenceFragmentCompat(), SharedPreferences.OnSharedP } else if (pref is ListPreference) { when (pref.key) { "notificationsSchedulerDelay" -> { - (findPreference("notificationsSchedulerDelay") as ListPreference).apply { + (findPreference("notificationsSchedulerDelay") as ListPreference?)?.apply { summary = entry WykopNotificationsJob.schedule(settingsApi) } @@ -128,7 +128,7 @@ class SettingsFragment : PreferenceFragmentCompat(), SharedPreferences.OnSharedP private fun isOfficialAppInstalled(): Boolean { return try { - activity!!.packageManager.getApplicationInfo("pl.wykop.droid", 0) + requireActivity().packageManager.getApplicationInfo("pl.wykop.droid", 0) true } catch (e: PackageManager.NameNotFoundException) { false @@ -136,7 +136,7 @@ class SettingsFragment : PreferenceFragmentCompat(), SharedPreferences.OnSharedP } private fun isNotificationAccessEnabled(): Boolean { - val manager = activity!!.getSystemService(Context.ACTIVITY_SERVICE) as ActivityManager + val manager = requireActivity().getSystemService(Context.ACTIVITY_SERVICE) as ActivityManager return manager.getRunningServices( Integer.MAX_VALUE @@ -144,7 +144,7 @@ class SettingsFragment : PreferenceFragmentCompat(), SharedPreferences.OnSharedP } private fun openWykopMarketPage() { - activity!!.createAlertBuilder().apply { + requireActivity().createAlertBuilder().apply { setTitle(R.string.dialog_piggyback_market_title) setMessage(R.string.dialog_piggyback_market_message) setCancelable(false) diff --git a/app/src/main/res/xml/provider_paths.xml b/app/src/main/res/xml/provider_paths.xml index 6d03a72f2..f6efb9116 100644 --- a/app/src/main/res/xml/provider_paths.xml +++ b/app/src/main/res/xml/provider_paths.xml @@ -3,8 +3,12 @@ xmlns:android="http://schemas.android.com/apk/res/android"> - + + + + + + + \ No newline at end of file diff --git a/build.gradle b/build.gradle index 35b275d14..849e4719b 100755 --- a/build.gradle +++ b/build.gradle @@ -26,8 +26,8 @@ allprojects { project.ext { versionMajor = 1 versionMinor = 0 - versionPatch = 1 - versionBuild = 2 + versionPatch = 2 + versionBuild = 0 } } diff --git a/credentials.properties.example b/credentials.properties.example index bae8d264c..b55ee19c3 100644 --- a/credentials.properties.example +++ b/credentials.properties.example @@ -1,3 +1,3 @@ apiKey="YOUR_WYKOP_API_KEY" apiSecret="YOUR_WYKOP_API_SECRET" -googleKey="YOUR_GOOGLE_API_KEY" \ No newline at end of file +googleKey="YOUR_GOOGLE_API_KEY" diff --git a/versions.gradle b/versions.gradle index 8777c3351..4375181b8 100755 --- a/versions.gradle +++ b/versions.gradle @@ -2,7 +2,7 @@ ext { versions = [ buildTools : '27.0.2', androidPlugin : '2.3.3', - kotlin : '1.3.41', + kotlin : '1.3.50', supportLibrary : '27.1.0', constraintLayout : '1.0.2', glide : '4.11.0',