从即时通讯工具快速转移-QIWI Android Wallet

你好!

我叫Alex,我是QIWI的开发人员。

TL; DR

如何直接从Messenger转移至付款表格:

  1. 在清单中,我们把一个空的活动Ç intent-filter 类型ACTION_VIEW,并 ACTION_DIAL与该计划“电话”。
  2. 在活动中,我们通过现有的深层链接转为付款表格,并使用原始数据进行了充实 intent- “tel:XXXXX”



利润:通过单击使者中突出显示的电话号码,一个人将进入转移表单,其中包含转移接收者的完整字段。
奖励:我将告诉您启用此功能的美观程度,而又无法在运行时更改清单中的意图过滤器列表。

做什么的?


假设某人有为集体生日礼物汇款的任务。接收方可以选择方便的转帐选项:转帐到卡,电子钱包,直接转帐到帐户或其他。提供翻译服务的许多公司都接受电话号码作为辅助或主要用户标识符。为了避免错误的转移,收件人可以通过短信(电子邮件,即时通讯工具或SMS)传达他的电话号码。该消息还指示必要的翻译系统。

典型的用户路径是从Messenger上复制电话号码,然后将其粘贴到应用程序中进行翻译。这种方式涉及在应用程序之间进行切换,可能会访问桌面,甚至在翻译服务提供商的已安装应用程序之间进行搜索。是否可以通过减少步骤数来缩短此路径?有一个选择。

理论


智能手机中的许多即时通讯程序都能识别电话号码,并以超链接的形式突出显示它们。默认情况下,有一个应用程序可以在任何带有GSM模块的移动设备上处理这种链接。通常将其称为“电话”。在Android中,AndroidManifest中声明了能够处理共享电话号码的功能。应用示例:“电话”,Viber,Skype。如果拦截这些事件,则可以立即为用户提供带有电话号码的可能操作列表。在我们的情况下,这将是通过QIWI电子钱包提供的转移优惠。

我们感兴趣的事件与类型Intent.ACTION_VIEWIntent.ACTION_DIAL, Intent.ACTION_CALL与电话方案。事件Intent.ACTION_CALL它对Android安全策略有限制,并且其初始化需要用户从Marshmallow含以下版本开始的权限。根据我们的研究信使使用更一般的Intent.ACTION_VIEWIntent.ACTION_DIAL

有几种专门用于紧急电话的类似类型,但这显然不是我们的情况。意向主体的格式为“ tel:12345678”。无法处理这种意图android.content.UriMatcher,因为此处缺少主机。处理此类Uri的最简单方法是询问schemeSpecificPart 并检查它是否为有效的电话号码。

意向过滤器列表必须在AndroidManifest中指定,在应用程序执行期间无法更改它。如何远程禁用此功能?第一种选择是在打开“活动”时拒绝用户的请求-带有消息或仅关闭应用程序。从我们的角度来看,这不是最佳的UX,因为我们会中断中间的用户流程。如果功能被禁用,最好不要让任何人选择我们的应用程序。在运行时使用intent-filter列表禁用组件会更容易,这不会给用户系统界面中的任何虚假承诺。使用PackageManager时,请注意检查组件是否包含不是很可靠。最好在enabled 不依赖于组件当前状态的情况下使用值强制其标志COMPONENT_ENABLED_STATE_ENABLED COMPONENT_ENABLED_STATE_DISABLED保存通过PackageManager进行的更改,直到从设备上卸载应用程序为止。

实践


为了禁用该功能,我们需要一个Android组件将其禁用。
最简单的方法是使用一个空的Activity,我们将这样做。如果您决定将Service与一组类似的意图过滤器一起使用,请查看Google指南,其中明确指出“请勿为您的服务声明意图过滤器”请注意,默认情况下该组件处于禁用状态:android:enabled="false"

  <activity
            android:name=".messengerP2P.view.MessengerP2PActivity"
            android:configChanges="orientation"
            android:label="@string/title_activity_messenger_p2_p"
            android:enabled="false"
            android:screenOrientation="portrait">
            <!-- Open shared telephone number as dial application -->
            <intent-filter

                android:label="@string/title_activity_messenger_p2_p">
                <action android:name="android.intent.action.VIEW" />
                <action android:name="android.intent.action.DIAL" />

                <category android:name="android.intent.category.DEFAULT" />
                <data android:scheme="tel" />
            </intent-filter>
        </activity>

活动本身只是通过现有的链接链接重定向到付款形式。

class MessengerP2PActivity : Activity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        var phoneNumberFromDial: String? = intent?.data?.schemeSpecificPart
        phoneNumberFromDial?.let {
            if (ru.mw.utils.Utils
                    .isPhoneNumber(phoneNumberFromDial)) {
                val phoneNumberFromDialLink = PaymentActivity.getUriForProviderId(
                        resources.getInteger(R.integer.providerIdQiwiWallet).toLong(), null, null)
                    .buildUpon()
                    .appendQueryParameter(PaymentActivity.QUERY_PARAM_ACCOUNT, phoneNumberFromDial)
                startActivity(
                    Intent(Intent.ACTION_VIEW, phoneNumberFromDialLink.build()))
            }
        }
        finish()
    }
}

android:enabled标志可以由此帮助器类控制。

import android.content.ComponentName
import android.content.Context
import android.content.pm.PackageManager

class MessengerP2PUtils {

    companion object {
        private const val componentName = "ru.mw.messengerP2P.view.MessengerP2PActivity"

        private fun switchMessengerP2P(enabled: Boolean = true, packageName: String, packageManager: PackageManager) {
            val compName = ComponentName(packageName, componentName)
            packageManager.setComponentEnabledSetting(
                compName,
                when (enabled) {
                    true -> PackageManager.COMPONENT_ENABLED_STATE_ENABLED
                    else -> PackageManager.COMPONENT_ENABLED_STATE_DISABLED
                },
                PackageManager.DONT_KILL_APP)
        }

        fun enableMessengerP2P(applicationContext: Context) {
            val packageName = applicationContext.packageName
            val packageManager = applicationContext.packageManager
            switchMessengerP2P(enabled = true, packageName = packageName, packageManager = packageManager);
        }

        fun disableMessengerP2P(applicationContext: Context) {
            val packageName = applicationContext.packageName
            val packageManager = applicationContext.packageManager
            switchMessengerP2P(enabled = false, packageName = packageName, packageManager = packageManager);
        }

        fun isMessengerP2PEnabled(packageName: String, packageManager: PackageManager): Boolean {
            val state = packageManager.getComponentEnabledSetting(ComponentName(packageName, componentName))
            return state == PackageManager.COMPONENT_ENABLED_STATE_ENABLED || state == PackageManager.COMPONENT_ENABLED_STATE_DEFAULT
        }

    }
}

这里不使用“ isMessengerP2PEnabled”方法来检查组件的当前状态,因为在测试过程中它产生了不可靠的结果。如果决定使用它,请在异步业务逻辑的条件下以及下载应用程序时仔细检查其操作。

更改标志值的时间enabled 不是很重要,我们选择功能标志配置完全加载的时刻。

因此,当您在Messenger上单击电话号码时,会出现一个系统选择对话框,您需要向用户提供有关如何取消意图的默认设置的说明-“ tel:XXXXX”。例如:“如果仅当您单击一个号码时才可以拨打电话,请转到智能手机设置→在应用程序列表中选择电话→将设置重置为“默认情况下打开”。


如果您在旅途中禁用该组件,则在向用户显示此系统对话框时,不会发生任何不良情况。相应的项目将立即从列表中消失,在我们的案例中为“转帐”。

如果用户默认情况下通过我们的应用程序处理链接,则会出现有趣的行为。为此,只需拨出“记住选择”或“始终”项即可。如果禁用该组件,则将为用户提供使用哪个应用程序的选择,并且在第一次启动时将没有“记住选择”项。启用功能后,将恢复整个流程,将用户直接转移到应用程序。

结论


这个功能的想法很久以前就出现了,原始票证是在2017年创建的。即使到现在,这个想法也没有失去意义,我找不到具有类似功能的银行应用程序。我们于4月29日发布了“来自Messenger的传输”,第一天就有近8,000个唯一用户利用了这些传输。如果在不久的将来业务指标能够使我们满意,我们将进一步开发此功能。

您如何看待,还有更多有趣的Android意图和平地等待着他们的业务应用程序?

All Articles