From d94bc858c8a0c273d87d705eb06d35cfd9cf9e08 Mon Sep 17 00:00:00 2001 From: Allan Wang Date: Tue, 25 Jul 2017 15:18:12 -0700 Subject: Update image downloads , IAB, and many issue reports (#95) * Remove iab proguard line * Remove dup vending aidl * Fix double calling issue * Change pro logging * Remove async call * Allow for multiple result flags from settings * Rename restore to get * Remove remaining async * Add null checks across web clients * Do not delete temp file on save * Implement image logic * Update file chooser * Update travis * Add intent checker * Update dependencies * Update dependencies * Add debugging option * Switch context for login glide * Scan newly added files * Update theme * Allow image downloading in messages * Finalize beta release * Build to beta * Update strings --- .../pitchedapps/frost/activities/AboutActivity.kt | 1 + .../pitchedapps/frost/activities/ImageActivity.kt | 35 +++++-- .../frost/activities/ImagePickerActivity.kt | 12 +++ .../pitchedapps/frost/activities/LoginActivity.kt | 2 +- .../pitchedapps/frost/activities/MainActivity.kt | 7 +- .../frost/activities/SettingsActivity.kt | 31 +++++-- .../frost/activities/WebOverlayActivity.kt | 2 +- .../com/pitchedapps/frost/contracts/FileChooser.kt | 42 ++++----- .../com/pitchedapps/frost/injectors/CssHider.kt | 2 +- .../com/pitchedapps/frost/settings/Appearance.kt | 6 +- .../com/pitchedapps/frost/settings/Behaviour.kt | 6 +- .../com/pitchedapps/frost/settings/Experimental.kt | 6 +- .../kotlin/com/pitchedapps/frost/settings/Feed.kt | 4 +- .../kotlin/com/pitchedapps/frost/utils/Prefs.kt | 2 +- .../com/pitchedapps/frost/utils/WebContextMenu.kt | 22 ++++- .../com/pitchedapps/frost/utils/iab/IABBinder.kt | 103 ++++++++++++--------- .../com/pitchedapps/frost/utils/iab/IABDialogs.kt | 11 ++- .../kotlin/com/pitchedapps/frost/web/FrostJSI.kt | 3 +- .../pitchedapps/frost/web/FrostWebViewClients.kt | 27 +++++- .../com/pitchedapps/frost/web/LoginWebView.kt | 7 +- 20 files changed, 210 insertions(+), 121 deletions(-) create mode 100644 app/src/main/kotlin/com/pitchedapps/frost/activities/ImagePickerActivity.kt (limited to 'app/src/main/kotlin/com') diff --git a/app/src/main/kotlin/com/pitchedapps/frost/activities/AboutActivity.kt b/app/src/main/kotlin/com/pitchedapps/frost/activities/AboutActivity.kt index b21cc3ab..80d2aa48 100644 --- a/app/src/main/kotlin/com/pitchedapps/frost/activities/AboutActivity.kt +++ b/app/src/main/kotlin/com/pitchedapps/frost/activities/AboutActivity.kt @@ -40,6 +40,7 @@ class AboutActivity : AboutActivityBase(null, { val include = arrayOf( "AboutLibraries", "AndroidIconics", + "androidin_appbillingv3", "androidslidinguppanel", "Crashlytics", "dbflow", diff --git a/app/src/main/kotlin/com/pitchedapps/frost/activities/ImageActivity.kt b/app/src/main/kotlin/com/pitchedapps/frost/activities/ImageActivity.kt index 79d9f782..a7c59deb 100644 --- a/app/src/main/kotlin/com/pitchedapps/frost/activities/ImageActivity.kt +++ b/app/src/main/kotlin/com/pitchedapps/frost/activities/ImageActivity.kt @@ -15,6 +15,7 @@ import android.view.View import android.view.ViewGroup import android.widget.ProgressBar import android.widget.TextView +import ca.allanwang.kau.email.sendEmail import ca.allanwang.kau.permissions.PERMISSION_WRITE_EXTERNAL_STORAGE import ca.allanwang.kau.permissions.kauOnRequestPermissionsResult import ca.allanwang.kau.permissions.kauRequestPermissions @@ -30,10 +31,7 @@ import com.mikepenz.google_material_typeface_library.GoogleMaterial import com.mikepenz.iconics.typeface.IIcon import com.pitchedapps.frost.BuildConfig import com.pitchedapps.frost.R -import com.pitchedapps.frost.utils.ARG_IMAGE_URL -import com.pitchedapps.frost.utils.ARG_TEXT -import com.pitchedapps.frost.utils.L -import com.pitchedapps.frost.utils.Prefs +import com.pitchedapps.frost.utils.* import com.sothree.slidinguppanel.SlidingUpPanelLayout import org.jetbrains.anko.doAsync import org.jetbrains.anko.uiThread @@ -194,6 +192,7 @@ class ImageActivity : AppCompatActivity() { var success = true try { File(tempFilePath).copyTo(destination, true) + scanFile(destination) } catch (e: Exception) { success = false } finally { @@ -201,10 +200,7 @@ class ImageActivity : AppCompatActivity() { uiThread { val text = if (success) R.string.image_download_success else R.string.image_download_fail snackbar(text) - if (success) { - deleteTempFile() - fabAction = FabStates.SHARE - } + if (success) fabAction = FabStates.SHARE } } } @@ -212,6 +208,17 @@ class ImageActivity : AppCompatActivity() { } } + /** + * See Docs + */ + internal fun scanFile(file: File) { + if (!file.exists()) return + val mediaScanIntent = Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE) + val contentUri = Uri.fromFile(file) + mediaScanIntent.data = contentUri + this.sendBroadcast(mediaScanIntent) + } + internal fun deleteTempFile() { if (tempFilePath != null) { File(tempFilePath!!).delete() @@ -233,7 +240,17 @@ class ImageActivity : AppCompatActivity() { internal enum class FabStates(val iicon: IIcon, val iconColor: Int = Prefs.iconColor, val backgroundTint: Int = Prefs.iconBackgroundColor.withAlpha(255)) { ERROR(GoogleMaterial.Icon.gmd_error, Color.WHITE, Color.RED) { override fun onClick(activity: ImageActivity) { - //todo add something + activity.materialDialogThemed { + title(R.string.kau_error) + content(R.string.bad_image_overlay) + positiveText(R.string.kau_yes) + onPositive { _, _ -> + activity.sendEmail(R.string.dev_email, R.string.debug_image_link_subject) { + addItem("Url", activity.imageUrl) + } + } + negativeText(R.string.kau_no) + } } }, NOTHING(GoogleMaterial.Icon.gmd_adjust) { diff --git a/app/src/main/kotlin/com/pitchedapps/frost/activities/ImagePickerActivity.kt b/app/src/main/kotlin/com/pitchedapps/frost/activities/ImagePickerActivity.kt new file mode 100644 index 00000000..f18d358e --- /dev/null +++ b/app/src/main/kotlin/com/pitchedapps/frost/activities/ImagePickerActivity.kt @@ -0,0 +1,12 @@ +package com.pitchedapps.frost.activities + +import android.content.res.ColorStateList +import android.os.Bundle +import ca.allanwang.kau.imagepicker.ImagePickerActivityBase +import ca.allanwang.kau.imagepicker.ImagePickerActivityOverlayBase +import com.pitchedapps.frost.utils.Prefs + +/** + * Created by Allan Wang on 2017-07-23. + */ +class ImagePickerActivity : ImagePickerActivityOverlayBase() \ No newline at end of file diff --git a/app/src/main/kotlin/com/pitchedapps/frost/activities/LoginActivity.kt b/app/src/main/kotlin/com/pitchedapps/frost/activities/LoginActivity.kt index e4897be5..2dccbeb5 100644 --- a/app/src/main/kotlin/com/pitchedapps/frost/activities/LoginActivity.kt +++ b/app/src/main/kotlin/com/pitchedapps/frost/activities/LoginActivity.kt @@ -106,7 +106,7 @@ class LoginActivity : BaseActivity() { fun loadProfile(id: Long) { - Glide.with(this@LoginActivity).load(PROFILE_PICTURE_URL(id)).withRoundIcon().listener(object : RequestListener { + Glide.with(profile).load(PROFILE_PICTURE_URL(id)).withRoundIcon().listener(object : RequestListener { override fun onResourceReady(resource: Drawable?, model: Any?, target: Target?, dataSource: DataSource?, isFirstResource: Boolean): Boolean { profileObservable.onSuccess(true) return false diff --git a/app/src/main/kotlin/com/pitchedapps/frost/activities/MainActivity.kt b/app/src/main/kotlin/com/pitchedapps/frost/activities/MainActivity.kt index a45bd033..978659db 100644 --- a/app/src/main/kotlin/com/pitchedapps/frost/activities/MainActivity.kt +++ b/app/src/main/kotlin/com/pitchedapps/frost/activities/MainActivity.kt @@ -1,12 +1,12 @@ package com.pitchedapps.frost.activities +import android.annotation.SuppressLint import android.app.AlarmManager import android.app.PendingIntent import android.content.Context import android.content.Intent import android.graphics.drawable.ColorDrawable import android.net.Uri -import android.os.Build import android.os.Bundle import android.support.annotation.StringRes import android.support.design.widget.AppBarLayout @@ -392,9 +392,10 @@ class MainActivity : BaseActivity(), SearchWebView.SearchContract, } override fun openFileChooser(filePathCallback: ValueCallback>, fileChooserParams: WebChromeClient.FileChooserParams) { - openFileChooser(this, filePathCallback, fileChooserParams) + openImagePicker(filePathCallback, fileChooserParams) } + @SuppressLint("NewApi") override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { if (onActivityResultWeb(requestCode, resultCode, data)) return super.onActivityResult(requestCode, resultCode, data) @@ -405,7 +406,7 @@ class MainActivity : BaseActivity(), SearchWebView.SearchContract, intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK or Intent.FLAG_ACTIVITY_NEW_TASK) val pending = PendingIntent.getActivity(this, 666, intent, PendingIntent.FLAG_CANCEL_CURRENT) val alarm = getSystemService(Context.ALARM_SERVICE) as AlarmManager - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) + if (buildIsMarshmallowAndUp) alarm.setExactAndAllowWhileIdle(AlarmManager.RTC, System.currentTimeMillis() + 100, pending) else alarm.setExact(AlarmManager.RTC, System.currentTimeMillis() + 100, pending) diff --git a/app/src/main/kotlin/com/pitchedapps/frost/activities/SettingsActivity.kt b/app/src/main/kotlin/com/pitchedapps/frost/activities/SettingsActivity.kt index a7ca6ce5..d073050b 100644 --- a/app/src/main/kotlin/com/pitchedapps/frost/activities/SettingsActivity.kt +++ b/app/src/main/kotlin/com/pitchedapps/frost/activities/SettingsActivity.kt @@ -1,9 +1,11 @@ package com.pitchedapps.frost.activities +import android.app.Activity import android.content.Intent import android.os.Bundle import android.view.Menu import android.view.MenuItem +import ca.allanwang.kau.about.kauLaunchAbout import ca.allanwang.kau.changelog.showChangelog import ca.allanwang.kau.kpref.activity.CoreAttributeContract import ca.allanwang.kau.kpref.activity.KPrefActivity @@ -27,11 +29,12 @@ import com.pitchedapps.frost.utils.iab.IS_FROST_PRO */ class SettingsActivity : KPrefActivity(), FrostBilling by IABSettings() { + var resultFlag = Activity.RESULT_CANCELED + override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { - if (!onActivityResultBilling(requestCode, resultCode, data)) { + if (!onActivityResultBilling(requestCode, resultCode, data)) super.onActivityResult(requestCode, resultCode, data) - adapter.notifyDataSetChanged() - } + adapter.notifyDataSetChanged() } override fun kPrefCoreAttributes(): CoreAttributeContract.() -> Unit = { @@ -65,15 +68,15 @@ class SettingsActivity : KPrefActivity(), FrostBilling by IABSettings() { iicon = CommunityMaterial.Icon.cmd_flask_outline } - plainText(R.string.restore_purchases) { - descRes = R.string.restore_purchases_desc - iicon = GoogleMaterial.Icon.gmd_refresh - onClick = { _, _, _ -> restorePurchases(false); true } + plainText(R.string.get_pro) { + descRes = R.string.get_pro_desc + iicon = GoogleMaterial.Icon.gmd_star + onClick = { _, _, _ -> restorePurchases(); true } } plainText(R.string.about_frost) { iicon = GoogleMaterial.Icon.gmd_info - onClick = { _, _, _ -> startActivity(AboutActivity::class.java, transition = true); true } + onClick = { _, _, _ -> kauLaunchAbout(AboutActivity::class.java); true } } if (BuildConfig.DEBUG) { @@ -87,7 +90,7 @@ class SettingsActivity : KPrefActivity(), FrostBilling by IABSettings() { } fun shouldRestartMain() { - setResult(MainActivity.REQUEST_RESTART) + setFrostResult(MainActivity.REQUEST_RESTART) } override fun onCreate(savedInstanceState: Bundle?) { @@ -107,10 +110,14 @@ class SettingsActivity : KPrefActivity(), FrostBilling by IABSettings() { } override fun onBackPressed() { - if (!super.backPress()) + if (!super.backPress()) { + setResult(resultFlag) finishSlideOut() + } } + + override fun onCreateOptionsMenu(menu: Menu): Boolean { menuInflater.inflate(R.menu.menu_settings, menu) toolbar.tint(Prefs.iconColor) @@ -135,6 +142,10 @@ class SettingsActivity : KPrefActivity(), FrostBilling by IABSettings() { return true } + fun setFrostResult(flag: Int) { + resultFlag = resultFlag and flag + } + override fun onDestroy() { onDestroyBilling() super.onDestroy() diff --git a/app/src/main/kotlin/com/pitchedapps/frost/activities/WebOverlayActivity.kt b/app/src/main/kotlin/com/pitchedapps/frost/activities/WebOverlayActivity.kt index f03c653c..13d72ffe 100644 --- a/app/src/main/kotlin/com/pitchedapps/frost/activities/WebOverlayActivity.kt +++ b/app/src/main/kotlin/com/pitchedapps/frost/activities/WebOverlayActivity.kt @@ -126,7 +126,7 @@ open class WebOverlayActivity : AppCompatActivity(), } override fun openFileChooser(filePathCallback: ValueCallback>, fileChooserParams: WebChromeClient.FileChooserParams) { - openFileChooser(this, filePathCallback, fileChooserParams) + openImagePicker(filePathCallback, fileChooserParams) } override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { diff --git a/app/src/main/kotlin/com/pitchedapps/frost/contracts/FileChooser.kt b/app/src/main/kotlin/com/pitchedapps/frost/contracts/FileChooser.kt index 5b2cfa49..bd31d6ce 100644 --- a/app/src/main/kotlin/com/pitchedapps/frost/contracts/FileChooser.kt +++ b/app/src/main/kotlin/com/pitchedapps/frost/contracts/FileChooser.kt @@ -5,14 +5,16 @@ import android.content.Intent import android.net.Uri import android.webkit.ValueCallback import android.webkit.WebChromeClient -import ca.allanwang.kau.permissions.PERMISSION_READ_EXTERNAL_STORAGE -import ca.allanwang.kau.permissions.kauRequestPermissions +import ca.allanwang.kau.imagepicker.kauLaunchImagePicker +import ca.allanwang.kau.imagepicker.kauOnImagePickerResult +import com.pitchedapps.frost.activities.ImagePickerActivity import com.pitchedapps.frost.utils.L +import java.io.File /** * Created by Allan Wang on 2017-07-04. */ -const val FILE_CHOOSER_REQUEST = 67 +const val IMAGE_CHOOSER_REQUEST = 67 interface FileChooserActivityContract { fun openFileChooser(filePathCallback: ValueCallback>, fileChooserParams: WebChromeClient.FileChooserParams) @@ -20,35 +22,27 @@ interface FileChooserActivityContract { interface FileChooserContract { var filePathCallback: ValueCallback>? - fun openFileChooser(activity: Activity, filePathCallback: ValueCallback>, fileChooserParams: WebChromeClient.FileChooserParams) - fun onActivityResultWeb(requestCode: Int, resultCode: Int, intent: Intent?): Boolean + fun Activity.openImagePicker(filePathCallback: ValueCallback>, fileChooserParams: WebChromeClient.FileChooserParams) + fun Activity.onActivityResultWeb(requestCode: Int, resultCode: Int, intent: Intent?): Boolean } class FileChooserDelegate : FileChooserContract { override var filePathCallback: ValueCallback>? = null - override fun openFileChooser(activity: Activity, filePathCallback: ValueCallback>, fileChooserParams: WebChromeClient.FileChooserParams) { - activity.kauRequestPermissions(PERMISSION_READ_EXTERNAL_STORAGE) { - granted, _ -> - if (!granted) return@kauRequestPermissions - val contentSelectionIntent = Intent(Intent.ACTION_GET_CONTENT) - contentSelectionIntent.addCategory(Intent.CATEGORY_OPENABLE) - contentSelectionIntent.type = fileChooserParams.acceptTypes?.joinToString(separator = "|") ?: "*/*" - activity.startActivityForResult(contentSelectionIntent, FILE_CHOOSER_REQUEST) - this.filePathCallback?.onReceiveValue(null) - this.filePathCallback = filePathCallback - } + override fun Activity.openImagePicker(filePathCallback: ValueCallback>, fileChooserParams: WebChromeClient.FileChooserParams) { + this@FileChooserDelegate.filePathCallback = filePathCallback + kauLaunchImagePicker(ImagePickerActivity::class.java, IMAGE_CHOOSER_REQUEST) } - override fun onActivityResultWeb(requestCode: Int, resultCode: Int, intent: Intent?): Boolean { - L.d("On activity results web $requestCode") - if (requestCode != FILE_CHOOSER_REQUEST) return false - var results: Uri? = null - - if (resultCode == Activity.RESULT_OK && intent != null) results = Uri.parse(intent.dataString) - L.d("Callback received; ${filePathCallback != null}") - filePathCallback?.onReceiveValue(if (results == null) null else arrayOf(results)) + override fun Activity.onActivityResultWeb(requestCode: Int, resultCode: Int, intent: Intent?): Boolean { + L.d("FileChooser On activity results web $requestCode") + if (requestCode != IMAGE_CHOOSER_REQUEST) return false + val results = kauOnImagePickerResult(resultCode, intent).map { it.uri }.toTypedArray() + L.d("FileChooser result ${results.contentToString()}") + //proper path content://com.android.providers.media.documents/document/image%3A36341 + L.d("FileChooser Callback received; ${filePathCallback != null}") + filePathCallback?.onReceiveValue(results) filePathCallback = null return true } diff --git a/app/src/main/kotlin/com/pitchedapps/frost/injectors/CssHider.kt b/app/src/main/kotlin/com/pitchedapps/frost/injectors/CssHider.kt index e8a3128d..a926c8ac 100644 --- a/app/src/main/kotlin/com/pitchedapps/frost/injectors/CssHider.kt +++ b/app/src/main/kotlin/com/pitchedapps/frost/injectors/CssHider.kt @@ -8,7 +8,7 @@ import android.webkit.WebView * List of elements to hide */ enum class CssHider(vararg val items: String) : InjectorContract { - CORE("[data-sigil=m_login_upsell]"), + CORE("[data-sigil=m_login_upsell]", "role=progressbar"), HEADER("#header[data-sigil=MTopBlueBarHeader]", "#header-notices", "[data-sigil*=m-promo-jewel-header]"), ADS( "article[data-xt*=sponsor]", diff --git a/app/src/main/kotlin/com/pitchedapps/frost/settings/Appearance.kt b/app/src/main/kotlin/com/pitchedapps/frost/settings/Appearance.kt index d38270f7..9c9754bb 100644 --- a/app/src/main/kotlin/com/pitchedapps/frost/settings/Appearance.kt +++ b/app/src/main/kotlin/com/pitchedapps/frost/settings/Appearance.kt @@ -108,7 +108,7 @@ fun SettingsActivity.getAppearancePrefs(): KPrefAdapterBuilder.() -> Unit = { checkbox(R.string.rounded_icons, { Prefs.showRoundedIcons }, { Prefs.showRoundedIcons = it - setResult(MainActivity.REQUEST_REFRESH) + setFrostResult(MainActivity.REQUEST_REFRESH) }) { descRes = R.string.rounded_icons_desc } @@ -116,7 +116,7 @@ fun SettingsActivity.getAppearancePrefs(): KPrefAdapterBuilder.() -> Unit = { checkbox(R.string.tint_nav, { Prefs.tintNavBar }, { Prefs.tintNavBar = it frostNavigationBar() - setResult(MainActivity.REQUEST_NAV) + setFrostResult(MainActivity.REQUEST_NAV) }) { descRes = R.string.tint_nav_desc } @@ -124,5 +124,5 @@ fun SettingsActivity.getAppearancePrefs(): KPrefAdapterBuilder.() -> Unit = { list.add(KPrefTextSeekbar( KPrefSeekbar.KPrefSeekbarBuilder( globalOptions, - R.string.web_text_scaling, { Prefs.webTextScaling }, { Prefs.webTextScaling = it; setResult(MainActivity.REQUEST_WEB_ZOOM) }))) + R.string.web_text_scaling, { Prefs.webTextScaling }, { Prefs.webTextScaling = it; setFrostResult(MainActivity.REQUEST_WEB_ZOOM) }))) } \ No newline at end of file diff --git a/app/src/main/kotlin/com/pitchedapps/frost/settings/Behaviour.kt b/app/src/main/kotlin/com/pitchedapps/frost/settings/Behaviour.kt index 00ff00c3..2af67602 100644 --- a/app/src/main/kotlin/com/pitchedapps/frost/settings/Behaviour.kt +++ b/app/src/main/kotlin/com/pitchedapps/frost/settings/Behaviour.kt @@ -15,7 +15,7 @@ fun SettingsActivity.getBehaviourPrefs(): KPrefAdapterBuilder.() -> Unit = { descRes = R.string.fancy_animations_desc } - checkbox(R.string.overlay_swipe, { Prefs.overlayEnabled }, { Prefs.overlayEnabled = it; setResult(MainActivity.REQUEST_REFRESH) }) { + checkbox(R.string.overlay_swipe, { Prefs.overlayEnabled }, { Prefs.overlayEnabled = it; setFrostResult(MainActivity.REQUEST_REFRESH) }) { descRes = R.string.overlay_swipe_desc } @@ -27,6 +27,10 @@ fun SettingsActivity.getBehaviourPrefs(): KPrefAdapterBuilder.() -> Unit = { descRes = R.string.viewpager_swipe_desc } + checkbox(R.string.search_bar, { Prefs.searchBar }, { Prefs.searchBar = it; setFrostResult(MainActivity.REQUEST_SEARCH) }) { + descRes = R.string.search_bar_desc + } + checkbox(R.string.exit_confirmation, { Prefs.exitConfirmation }, { Prefs.exitConfirmation = it }) { descRes = R.string.exit_confirmation_desc } diff --git a/app/src/main/kotlin/com/pitchedapps/frost/settings/Experimental.kt b/app/src/main/kotlin/com/pitchedapps/frost/settings/Experimental.kt index c780948d..05a852ee 100644 --- a/app/src/main/kotlin/com/pitchedapps/frost/settings/Experimental.kt +++ b/app/src/main/kotlin/com/pitchedapps/frost/settings/Experimental.kt @@ -22,9 +22,7 @@ fun SettingsActivity.getExperimentalPrefs(): KPrefAdapterBuilder.() -> Unit = { // Experimental content starts here ------------------ - checkbox(R.string.search_bar, { Prefs.searchBar }, { Prefs.searchBar = it; setResult(MainActivity.REQUEST_SEARCH) }) { - descRes = R.string.search_bar_desc - } + // Experimental content ends here -------------------- @@ -36,7 +34,7 @@ fun SettingsActivity.getExperimentalPrefs(): KPrefAdapterBuilder.() -> Unit = { descRes = R.string.restart_frost_desc onClick = { _, _, _ -> - setResult(MainActivity.REQUEST_RESTART_APPLICATION) + setFrostResult(MainActivity.REQUEST_RESTART_APPLICATION) finish() true } diff --git a/app/src/main/kotlin/com/pitchedapps/frost/settings/Feed.kt b/app/src/main/kotlin/com/pitchedapps/frost/settings/Feed.kt index 9b214c26..a80642bb 100644 --- a/app/src/main/kotlin/com/pitchedapps/frost/settings/Feed.kt +++ b/app/src/main/kotlin/com/pitchedapps/frost/settings/Feed.kt @@ -39,7 +39,7 @@ fun SettingsActivity.getFeedPrefs(): KPrefAdapterBuilder.() -> Unit = { checkbox(R.string.suggested_friends, { Prefs.showSuggestedFriends }, { Prefs.showSuggestedFriends = it - setResult(MainActivity.REQUEST_REFRESH) + setFrostResult(MainActivity.REQUEST_REFRESH) }) { descRes = R.string.suggested_friends_desc dependsOnPro() @@ -47,7 +47,7 @@ fun SettingsActivity.getFeedPrefs(): KPrefAdapterBuilder.() -> Unit = { checkbox(R.string.facebook_ads, { Prefs.showFacebookAds }, { Prefs.showFacebookAds = it - setResult(MainActivity.REQUEST_REFRESH) + setFrostResult(MainActivity.REQUEST_REFRESH) }) { descRes = R.string.facebook_ads_desc dependsOnPro() diff --git a/app/src/main/kotlin/com/pitchedapps/frost/utils/Prefs.kt b/app/src/main/kotlin/com/pitchedapps/frost/utils/Prefs.kt index 63e57554..a20e755f 100644 --- a/app/src/main/kotlin/com/pitchedapps/frost/utils/Prefs.kt +++ b/app/src/main/kotlin/com/pitchedapps/frost/utils/Prefs.kt @@ -119,7 +119,7 @@ object Prefs : KPref() { var analytics: Boolean by kpref("analytics", true) - var searchBar: Boolean by kpref("search_bar", Showcase.experimentalDefault) + var searchBar: Boolean by kpref("search_bar", true) var overlayEnabled: Boolean by kpref("overlay_enabled", true) diff --git a/app/src/main/kotlin/com/pitchedapps/frost/utils/WebContextMenu.kt b/app/src/main/kotlin/com/pitchedapps/frost/utils/WebContextMenu.kt index 40de99bf..ea66030f 100644 --- a/app/src/main/kotlin/com/pitchedapps/frost/utils/WebContextMenu.kt +++ b/app/src/main/kotlin/com/pitchedapps/frost/utils/WebContextMenu.kt @@ -1,12 +1,14 @@ package com.pitchedapps.frost.utils import android.content.Context +import ca.allanwang.kau.email.sendEmail import ca.allanwang.kau.utils.copyToClipboard import ca.allanwang.kau.utils.shareText import ca.allanwang.kau.utils.string import ca.allanwang.kau.utils.toast import com.pitchedapps.frost.R import com.pitchedapps.frost.activities.MainActivity +import com.pitchedapps.frost.facebook.formattedFbUrl /** * Created by Allan Wang on 2017-07-07. @@ -34,12 +36,28 @@ fun Context.showWebContextMenu(wc: WebContext) { } } -class WebContext(val url: String, val text: String?) +class WebContext(val unformattedUrl: String, val text: String?) { + val url = unformattedUrl.formattedFbUrl +} enum class WebContextType(val textId: Int, val onClick: (c: Context, wc: WebContext) -> Unit) { COPY_LINK(R.string.copy_link, { c, wc -> c.copyToClipboard(wc.url) }), COPY_TEXT(R.string.copy_text, { c, wc -> if (wc.text != null) c.copyToClipboard(wc.text) else c.toast(R.string.no_text) }), - SHARE_LINK(R.string.share_link, { c, wc -> c.shareText(wc.url) }) + SHARE_LINK(R.string.share_link, { c, wc -> c.shareText(wc.url) }), + DEBUG_LINK(R.string.debug_link, { c, wc -> + c.materialDialogThemed { + title(R.string.debug_link) + content(R.string.debug_link_desc) + positiveText(R.string.kau_ok) + onPositive { _, _ -> + c.sendEmail(R.string.dev_email, R.string.debug_link_subject) { + message = c.string(R.string.debug_link_content) + addItem("Unformatted url", wc.unformattedUrl) + addItem("Formatted url", wc.url) + } + } + } + }) ; companion object { diff --git a/app/src/main/kotlin/com/pitchedapps/frost/utils/iab/IABBinder.kt b/app/src/main/kotlin/com/pitchedapps/frost/utils/iab/IABBinder.kt index 53d3e058..ab9e37d1 100644 --- a/app/src/main/kotlin/com/pitchedapps/frost/utils/iab/IABBinder.kt +++ b/app/src/main/kotlin/com/pitchedapps/frost/utils/iab/IABBinder.kt @@ -9,8 +9,6 @@ import com.pitchedapps.frost.BuildConfig import com.pitchedapps.frost.utils.L import com.pitchedapps.frost.utils.Prefs import com.pitchedapps.frost.utils.frostAnswers -import org.jetbrains.anko.doAsync -import org.jetbrains.anko.uiThread /** * Created by Allan Wang on 2017-07-22. @@ -24,18 +22,18 @@ interface FrostBilling : BillingProcessor.IBillingHandler { fun Activity.onCreateBilling() fun onDestroyBilling() fun purchasePro() - fun restorePurchases(once: Boolean) + fun restorePurchases() fun onActivityResultBilling(requestCode: Int, resultCode: Int, data: Intent?): Boolean } -open class IABBinder : FrostBilling { +abstract class IABBinder : FrostBilling { var bp: BillingProcessor? = null var activity: Activity? = null override fun Activity.onCreateBilling() { - bp = BillingProcessor.newBillingProcessor(this, PUBLIC_BILLING_KEY, this@IABBinder) activity = this + bp = BillingProcessor.newBillingProcessor(this, PUBLIC_BILLING_KEY, this@IABBinder) bp!!.initialize() } @@ -45,15 +43,11 @@ open class IABBinder : FrostBilling { activity = null } - override fun onBillingInitialized() { - L.d("IAB initialized") - } + override fun onBillingInitialized() = L.d("IAB initialized") - override fun onPurchaseHistoryRestored() { - L.d("IAB restored") - } + override fun onPurchaseHistoryRestored() = L.d("IAB restored") - override fun onProductPurchased(productId: String, details: TransactionDetails) { + override fun onProductPurchased(productId: String, details: TransactionDetails?) { L.d("IAB $productId purchased") frostAnswers { logPurchase(PurchaseEvent() @@ -63,7 +57,7 @@ open class IABBinder : FrostBilling { } } - override fun onBillingError(errorCode: Int, error: Throwable) { + override fun onBillingError(errorCode: Int, error: Throwable?) { frostAnswers { logPurchase(PurchaseEvent() .putCustomAttribute("result", errorCode.toString()) @@ -76,64 +70,81 @@ open class IABBinder : FrostBilling { = bp?.handleActivityResult(requestCode, resultCode, data) ?: false override fun purchasePro() { - if (bp == null) return + if (bp == null) { + frostAnswers { + logPurchase(PurchaseEvent() + .putCustomAttribute("result", "null bp") + .putSuccess(false)) + } + L.eThrow("IAB null bp on purchase attempt") + return + } if (!bp!!.isOneTimePurchaseSupported) activity!!.playStorePurchaseUnsupported() else bp!!.purchase(activity, FROST_PRO) } - override fun restorePurchases(once: Boolean) { - if (bp == null) return - doAsync { - bp?.loadOwnedPurchasesFromGoogle() - if (bp?.isPurchased(FROST_PRO) ?: false) { - uiThread { - if (Prefs.pro) activity!!.playStoreNoLongerPro() - else if (!once) purchasePro() - if (once) onDestroyBilling() - } - } else { - uiThread { - if (!Prefs.pro) activity!!.playStoreFoundPro() - else if (!once) activity!!.purchaseRestored() - if (once) onDestroyBilling() - } - } - } - } } class IABSettings : IABBinder() { - override fun onBillingInitialized() { - super.onBillingInitialized() - - } - - override fun onPurchaseHistoryRestored() { - super.onPurchaseHistoryRestored() - } - - override fun onProductPurchased(productId: String, details: TransactionDetails) { + override fun onProductPurchased(productId: String, details: TransactionDetails?) { super.onProductPurchased(productId, details) + activity?.playStorePurchasedSuccessfully(productId) } - override fun onBillingError(errorCode: Int, error: Throwable) { + override fun onBillingError(errorCode: Int, error: Throwable?) { super.onBillingError(errorCode, error) activity?.playStoreGenericError(null) } + + /** + * Attempts to get pro, or launch purchase flow if user doesn't have it + */ + override fun restorePurchases() { + if (bp == null) return + val load = bp!!.loadOwnedPurchasesFromGoogle() + L.d("IAB settings load from google $load") + if (!bp!!.isPurchased(FROST_PRO)) { + if (Prefs.pro) activity!!.playStoreNoLongerPro() + else purchasePro() + } else { + if (!Prefs.pro) activity!!.playStoreFoundPro() + else activity!!.purchaseRestored() + } + } } class IABMain : IABBinder() { override fun onBillingInitialized() { super.onBillingInitialized() - restorePurchases(true) + restorePurchases() } override fun onPurchaseHistoryRestored() { super.onPurchaseHistoryRestored() - restorePurchases(true) + restorePurchases() + } + + private var restored = false + + /** + * Checks for pro and only does so once + * A null check is added but it should never happen + * given that this is only called with bp is ready + */ + override fun restorePurchases() { + if (restored || bp == null) return + restored = true + val load = bp!!.loadOwnedPurchasesFromGoogle() + L.d("IAB main load from google $load") + if (!bp!!.isPurchased(FROST_PRO)) { + if (Prefs.pro) activity!!.playStoreNoLongerPro() + } else { + if (!Prefs.pro) activity!!.playStoreFoundPro() + } + onDestroyBilling() } } \ No newline at end of file diff --git a/app/src/main/kotlin/com/pitchedapps/frost/utils/iab/IABDialogs.kt b/app/src/main/kotlin/com/pitchedapps/frost/utils/iab/IABDialogs.kt index 4f750b6b..d2f22829 100644 --- a/app/src/main/kotlin/com/pitchedapps/frost/utils/iab/IABDialogs.kt +++ b/app/src/main/kotlin/com/pitchedapps/frost/utils/iab/IABDialogs.kt @@ -4,11 +4,13 @@ import android.app.Activity import ca.allanwang.kau.utils.restart import ca.allanwang.kau.utils.startPlayStoreLink import ca.allanwang.kau.utils.string +import com.crashlytics.android.answers.PurchaseEvent import com.pitchedapps.frost.R import com.pitchedapps.frost.activities.MainActivity import com.pitchedapps.frost.activities.SettingsActivity import com.pitchedapps.frost.utils.L import com.pitchedapps.frost.utils.Prefs +import com.pitchedapps.frost.utils.frostAnswers import com.pitchedapps.frost.utils.materialDialogThemed /** @@ -16,7 +18,7 @@ import com.pitchedapps.frost.utils.materialDialogThemed */ private fun playStoreLog(text: String) { - L.e(Throwable(text), "Play Store Exception") + L.e(Throwable(text), "IAB Play Store Exception") } /** @@ -31,7 +33,12 @@ private fun Activity.playRestart() { fun Activity.playStoreNoLongerPro() { Prefs.pro = false - playStoreLog("No Longer Pro") + L.d("IAB No longer pro") + frostAnswers { + logPurchase(PurchaseEvent() + .putCustomAttribute("result", "no longer pro") + .putSuccess(false)) + } materialDialogThemed { title(R.string.uh_oh) content(R.string.play_store_not_pro) diff --git a/app/src/main/kotlin/com/pitchedapps/frost/web/FrostJSI.kt b/app/src/main/kotlin/com/pitchedapps/frost/web/FrostJSI.kt index 13f1ac90..912a957e 100644 --- a/app/src/main/kotlin/com/pitchedapps/frost/web/FrostJSI.kt +++ b/app/src/main/kotlin/com/pitchedapps/frost/web/FrostJSI.kt @@ -41,7 +41,8 @@ class FrostJSI(val webView: FrostWebViewCore) { @JavascriptInterface fun contextMenu(url: String, text: String?) { - webView.post { context.showWebContextMenu(WebContext(url.formattedFbUrl, text)) } + //url will be formatted through webcontext + webView.post { context.showWebContextMenu(WebContext(url, text)) } } /** diff --git a/app/src/main/kotlin/com/pitchedapps/frost/web/FrostWebViewClients.kt b/app/src/main/kotlin/com/pitchedapps/frost/web/FrostWebViewClients.kt index 89eef258..7d5282d8 100644 --- a/app/src/main/kotlin/com/pitchedapps/frost/web/FrostWebViewClients.kt +++ b/app/src/main/kotlin/com/pitchedapps/frost/web/FrostWebViewClients.kt @@ -1,6 +1,8 @@ package com.pitchedapps.frost.web +import android.content.ActivityNotFoundException import android.content.Context +import android.content.Intent import android.graphics.Bitmap import android.webkit.WebResourceRequest import android.webkit.WebResourceResponse @@ -9,6 +11,7 @@ import android.webkit.WebViewClient import com.pitchedapps.frost.activities.LoginActivity import com.pitchedapps.frost.activities.MainActivity import com.pitchedapps.frost.activities.SelectorActivity +import com.pitchedapps.frost.activities.WebOverlayActivity import com.pitchedapps.frost.facebook.FACEBOOK_COM import com.pitchedapps.frost.facebook.FB_URL_BASE import com.pitchedapps.frost.facebook.FbCookie @@ -40,8 +43,9 @@ open class FrostWebViewClient(val webCore: FrostWebViewCore) : BaseWebViewClient val refreshObservable: Subject = webCore.refreshObservable - override fun onPageStarted(view: WebView, url: String, favicon: Bitmap?) { + override fun onPageStarted(view: WebView, url: String?, favicon: Bitmap?) { super.onPageStarted(view, url, favicon) + if (url == null) return L.i("FWV Loading $url") // L.v("Cookies ${CookieManager.getInstance().getCookie(url)}") refreshObservable.onNext(true) @@ -57,8 +61,9 @@ open class FrostWebViewClient(val webCore: FrostWebViewCore) : BaseWebViewClient c.launchNewTask(LoginActivity::class.java) } - override fun onPageFinished(view: WebView, url: String) { + override fun onPageFinished(view: WebView, url: String?) { super.onPageFinished(view, url) + if (url == null) return L.i("Page finished $url") if (!url.contains(FACEBOOK_COM)) { refreshObservable.onNext(false) @@ -103,9 +108,11 @@ open class FrostWebViewClient(val webCore: FrostWebViewCore) : BaseWebViewClient /** * Helper to format the request and launch it * returns true to override the url + * returns false if we are already in an overlaying activity */ private fun launchRequest(request: WebResourceRequest): Boolean { L.d("Launching Url", request.url.toString()) + if (webCore.context is WebOverlayActivity) return false webCore.context.launchWebOverlay(request.url.toString()) return true } @@ -124,6 +131,13 @@ open class FrostWebViewClient(val webCore: FrostWebViewCore) : BaseWebViewClient if (path.startsWith("/composer/")) return launchRequest(request) if (request.url.toString().contains("scontent-sea1-1.xx.fbcdn.net") && (path.endsWith(".jpg") || path.endsWith(".png"))) return launchImage(request) + if (!request.url.toString().contains(FACEBOOK_COM)) { + val intent = Intent(Intent.ACTION_VIEW, request.url) + if (intent.resolveActivity(view.context.packageManager) != null) { + view.context.startActivity(Intent(Intent.ACTION_VIEW, request.url)) + return true + } + } return super.shouldOverrideUrlLoading(view, request) } @@ -142,8 +156,9 @@ class FrostWebViewClientMenu(webCore: FrostWebViewCore) : FrostWebViewClient(web else -> false } - override fun onPageFinished(view: WebView, url: String) { + override fun onPageFinished(view: WebView, url: String?) { super.onPageFinished(view, url) + if (url == null) return if (url.shouldInjectMenu) jsInject(JsAssets.MENU) } @@ -163,13 +178,15 @@ class FrostWebViewClientMenu(webCore: FrostWebViewCore) : FrostWebViewClient(web */ class HeadlessWebViewClient(val tag: String, val postInjection: InjectorContract) : BaseWebViewClient() { - override fun onPageStarted(view: WebView, url: String, favicon: Bitmap?) { + override fun onPageStarted(view: WebView, url: String?, favicon: Bitmap?) { super.onPageStarted(view, url, favicon) + if (url == null) return L.d("Headless Page $tag Started", url) } - override fun onPageFinished(view: WebView, url: String) { + override fun onPageFinished(view: WebView, url: String?) { super.onPageFinished(view, url) + if (url == null) return L.d("Headless Page $tag Finished", url) postInjection.inject(view) } diff --git a/app/src/main/kotlin/com/pitchedapps/frost/web/LoginWebView.kt b/app/src/main/kotlin/com/pitchedapps/frost/web/LoginWebView.kt index b23d898e..b178f66c 100644 --- a/app/src/main/kotlin/com/pitchedapps/frost/web/LoginWebView.kt +++ b/app/src/main/kotlin/com/pitchedapps/frost/web/LoginWebView.kt @@ -24,10 +24,7 @@ import io.reactivex.subjects.Subject /** * Created by Allan Wang on 2017-05-29. - * */ - - class LoginWebView @JvmOverloads constructor( context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0 ) : WebView(context, attrs, defStyleAttr) { @@ -71,9 +68,9 @@ class LoginWebView @JvmOverloads constructor( inner class LoginClient : BaseWebViewClient() { - override fun onPageFinished(view: WebView, url: String) { + override fun onPageFinished(view: WebView, url: String?) { super.onPageFinished(view, url) - if (!url.contains(FACEBOOK_COM)) { + if (url == null || !url.contains(FACEBOOK_COM)) { view.frostSnackbar(R.string.no_longer_facebook) loadLogin() return -- cgit v1.2.3