|
|
|
import 'dart:async';
|
|
|
|
import 'dart:convert';
|
|
|
|
import 'dart:io';
|
|
|
|
|
|
|
|
// class WebPage extends StatefulWidget {
|
|
|
|
// final String title;
|
|
|
|
// final String url;
|
|
|
|
// final bool need;
|
|
|
|
|
|
|
|
// const WebPage({Key key, this.title, this.url, this.need = false})
|
|
|
|
// : super(key: key);
|
|
|
|
// @override
|
|
|
|
// _WebPageState createState() => _WebPageState();
|
|
|
|
// }
|
|
|
|
|
|
|
|
// class _WebPageState extends State<WebPage> {
|
|
|
|
// final Completer<WebViewController> _controller =
|
|
|
|
// Completer<WebViewController>();
|
|
|
|
|
|
|
|
// @override
|
|
|
|
// void initState() {
|
|
|
|
// super.initState();
|
|
|
|
// // Enable hybrid composition.
|
|
|
|
// if (Platform.isAndroid) WebView.platform = SurfaceAndroidWebView();
|
|
|
|
// }
|
|
|
|
|
|
|
|
// @override
|
|
|
|
// Widget build(BuildContext context) {
|
|
|
|
// return Text('''引言
|
|
|
|
// 欢迎您使用由纳极(北京)科技有限公司(本文简称纳极)提供的“一键订”!我们非常重视用户的隐私和个人信息保护。您在使用我们的产品与/或服务时,我们可能会收集和使用您的相关信息。我们希望通过本《隐私政策》向您说明我们在您使用我们的产品与/或服务时如何收集、使用、保存、共享和转让这些信息,以及我们为您提供的访问、更新、控制和保护这些信息的方式。
|
|
|
|
// 本隐私政策将帮助您了解以下内容:
|
|
|
|
// 一、我们如何收集和使用您的个人信息
|
|
|
|
// 二、我们如何共享、转让、公开披露您的个人信息
|
|
|
|
// 三、我们如何保护和保存您的个人信息
|
|
|
|
// 四、您如何管理个人信息
|
|
|
|
// 五、未成年人的个人信息保护
|
|
|
|
// 六、通知和修订
|
|
|
|
// 七、如何联系我们
|
|
|
|
// 本隐私政策与您所使用的“一键订”服务以及该服务所包括的各种业务功能(以下统称“我们的产品与/或服务”)息息相关,希望您在使用我们的产品与/或服务前仔细阅读并确认您已经充分理解本隐私政策所写明的内容,并让您可以按照本隐私政策的指引做出您认为适当的选择。本隐私政策中涉及的相关术语,我们尽量以简明扼要的表述,以便您更好地理解。
|
|
|
|
// 一、我们如何收集和使用您的个人信息
|
|
|
|
// 个人信息是指以电子或者其他方式记录的能够单独或者与其他信息结合识别特定自然人身份或者反映特定自然人活动情况的各种信息。个人敏感信息是指一旦泄露、非法提供或滥用可能危害人身和财产安全,极易导致个人名誉、身心健康受到损害或歧视性待遇等的个人信息(我们将在本政策中对涉及到的个人敏感信息以粗体进行显著标识)。
|
|
|
|
// 我们会遵循正当、合法、必要的原则,出于本政策所述的以下目的,收集和使用您在使用产品与/或服务过程中主动提供或因使用产品与/或服务而产生的个人信息,以及从第三方获取您的相关个人信息。如果我们要将您的个人信息用于本政策未载明的其它用途,或将基于特定目的收集而来的信息用于其他目的,我们将以合理的方式向您告知,并在使用前再次征得您的同意。
|
|
|
|
// (一)您须授权我们收集和使用您个人信息的情形
|
|
|
|
// 我们的产品与/或服务包括一些核心功能,这些功能包含了实现预订管理所必须的功能。我们可能会收集、保存和使用下列与您有关的信息才能实现上述这些功能。如果您不提供相关信息,您将无法享受我们提供的产品与/或服务。这些功能包括:
|
|
|
|
// (1)账户注册、登录与验证
|
|
|
|
// 当您注册“一键订”账户时,您至少需要向我们提供您准备使用的您本人的手机号码,我们将通过发送短信验证码的方式来验证您的身份是否有效。您可以修改和补充您的姓名、照片、联系电话,这些信息均属于您的“账户信息”。您补充的账户信息将有助于我们为您的客户提供更优的预订体验。
|
|
|
|
// (2)店铺服务信息设置
|
|
|
|
// 为了让您的客户快速地找到您的店铺,我们会收集您店铺的名称、所在位置相关信息(您授权的GPS位置信息,我们仅收集您当时所处的地理位置,但不会将您各时段的位置信息进行结合以判断您的行踪轨迹)来判断您所处的地点。
|
|
|
|
// (3)预订信息录入
|
|
|
|
// 为了方便您快速录入客户的姓名、联系电话,在您同意开启通讯录权限后您在录入预订订单时不需再手动输入您通讯录中联系人的信息,用于订单快速生成。
|
|
|
|
// (4)预订通知管理
|
|
|
|
// 当您准备通知您的客户预订结果时,“一键订”需要调用您的微信给客户发送预订安排。您需要在通知内至少填写您的联系电话;
|
|
|
|
// 您还可以填写您的姓名、职务和微信二维码以方便客户更好的跟您建立联系,但是不填写这些信息不影响您的预订通知。
|
|
|
|
// 当您设置您的通知信息时,“一键订”需要调用基于图片/视频上传的扩展功能:我们会使用您所上传的图片,作为您的形象照片和微信二维码展示,方便客户添加您的微信。
|
|
|
|
// (5)订单结账通知功能
|
|
|
|
// 当您传送订单水单给客户时,“一键订”需要调用基于摄像头(相机)的扩展功能:您可以使用这项扩展功能完成拍照上传水单传送给客户。
|
|
|
|
// (6)基于剪切板的扩展功能:当您复制个人信息、短信验证码,分享或接收被分享信息时,您的剪切板功能可能会被调用。剪切板信息仅会在您的设备上进行处理,我们不会存储您的剪切板信息用于任何其他途径。
|
|
|
|
// 上述功能可能需要您在您的设备中向我们开启您的地理位置(位置信息)、相机(摄像头)、相册(图片库)、通讯录,以实现这些功能所涉及的信息的收集和使用。您可以在“一键订”APP或设备系统中逐项查看您上述权限的开启状态,并可以决定将这些权限随时的开启或关闭。请您注意,您开启这些权限即代表您授权我们可以收集和使用这些个人信息来实现上述的功能,您关闭权限即代表您取消了这些授权,则我们将不再继续收集和使用您的这些个人信息,也无法为您提供上述与这些授权所对应的功能。您关闭权限的决定不会影响此前基于您的授权所进行的个人信息的处理。除此以外,我们在相关业务功能中可能还需要您开启设备的其他访问权限,详细权限和使用目的如下:
|
|
|
|
// (1)android.permission.ACCESS_COARSE_LOCATION(访问位置权限):用于应用创建店铺供用户选择地图位置、定位
|
|
|
|
// (2)android.permission.ACCESS_FINE_LOCATION(访问位置权限):用于应用创建店铺供用户选择地图位置、定位
|
|
|
|
// (3)android.permission.READ_CONTACTS/android.permission.WRITE_CONTACTS(访问联系人权限):获取用户通讯录以便快速录入用户联系电话,方便发送短信
|
|
|
|
// (4)com.huawei.android.launcher.permission.CHANGE_BADGE(设置桌面图标角标权限):应于消息推送设置桌面角标,推送客户预订、提前点餐、买单需求、取消预订等需要推送通知给商家的消息数
|
|
|
|
// (二)您充分知晓,以下情形中,我们收集、使用个人信息无需征得您的授权同意:
|
|
|
|
// 1、与国家安全、国防安全有关的;
|
|
|
|
// 2、与公共安全、公共卫生、重大公共利益有关的;
|
|
|
|
// 3、与犯罪侦查、起诉、审判和判决执行等有关的;
|
|
|
|
// 4、出于维护个人信息主体或其他个人的生命、财产等重大合法权益但又很难得到本人同意的;
|
|
|
|
// 5、所收集的个人信息是个人信息主体或监护人自行向社会公众公开的;
|
|
|
|
// 6、从合法公开披露的信息中收集的您的个人信息的,如合法的新闻报道、政府信息公开等渠道;
|
|
|
|
// 7、根据您的要求签订合同所必需的;
|
|
|
|
// 8、用于维护所提供的产品与/或服务的安全稳定运行所必需的,例如发现、处置产品与/或服务的故障;
|
|
|
|
// 9、为合法的新闻报道所必需的;
|
|
|
|
// 10、学术研究机构基于公共利益开展统计或学术研究所必要,且对外提供学术研究或描述的结果时,对结果中所包含的个人信息进行去标识化处理的;
|
|
|
|
// 11、法律法规规定的其他情形。
|
|
|
|
// (三)我们从第三方获得您个人信息的情形
|
|
|
|
// 我们可能从第三方获取您授权共享的账户信息(头像、昵称及授权页面提示的其他信息),并在您同意本隐私政策后将您的第三方账户与您的纳极账户绑定,使您可以通过第三方账户直接登录并使用我们的产品与/或服务。我们会将依据与第三方的约定、对个人信息来源的合法性进行确认后,在符合相关法律和法规规定的前提下,使用您的这些个人信息。您也可以使用本机号码来快捷登录,我们会将您的网络信息提供给运营商完成注册或登录。
|
|
|
|
// (四)“一键订”APP涉及嵌入第三方代码、插件传输个人信息的情形逐项列举:
|
|
|
|
// 高德地图sdk:用于应用创建店铺供用户选择地图位置、定位
|
|
|
|
// 获取通讯录:获取用户通讯录以便快速录入用户联系电话,方便发送短信
|
|
|
|
// 华为推送sdk:应于消息推送,推送客户预订、提前点餐、买单需求、取消预订等需要推送通知给商家的消息
|
|
|
|
// 腾讯Bugly:用于收集应用使用中报出的错误信息、日志
|
|
|
|
// 微信sdk:用于商家经理微信登录“一键订”app 以及微信分享预订通知、点餐清单等操作
|
|
|
|
// 七牛云sdk:用于上传、获取商家图片、视频等资源
|
|
|
|
// (五)您个人信息使用的规则
|
|
|
|
// 1、我们会根据本隐私政策的约定并为实现我们的产品与/或服务功能对所收集的个人信息进行使用。
|
|
|
|
// 2、在收集您的个人信息后,我们将通过技术手段对数据进行去标识化处理,去标识化处理的信息将无法识别主体。请您了解并同意,在此情况下我们有权使用已经去标识化的信息;并在不透露您个人信息的前提下,我们有权对用户数据库进行分析并予以商业化的利用。
|
|
|
|
// 3、请您了解并同意,您在使用我们的产品与/或服务时所提供的所有个人信息,除非您删除或通过系统设置拒绝我们收集,否则将在您使用我们的产品与/或服务期间持续授权我们使用。
|
|
|
|
// 4、我们会对我们的产品与/或服务使用情况进行统计,并可能会与公众或第三方共享这些统计信息,以展示我们的产品与/或服务的整体使用趋势。但这些统计信息不包含您的任何身份识别信息。
|
|
|
|
// 5、当我们展示您的个人信息时,我们会采用包括内容替换、匿名处理方式对您的信息进行脱敏,以保护您的信息安全。
|
|
|
|
// 6、当我们要将您的个人信息用于本隐私政策未载明的其它用途时,或基于特定目的收集而来的信息用于其他目的时,会实现征求您的明示同意。请您理解,我们向您提供的产品与/或服务将不断更新变化。如果您选择使用本隐私政策中尚未列明的其他产品与/或服务时,我们会在收集您的个人信息前通过协议、页面提示等方式向您详细说明信息收集的目的、方式、范围并征求您的同意。若您不同意提供前述信息,您可能无法使用该项产品与/或服务,但不影响您使用现有产品与/或服务。
|
|
|
|
// 二、我们如何共享、转让、公开披露您的个人信息
|
|
|
|
// (一)共享
|
|
|
|
// 1、我们不会与纳极以外的任何公司、组织和个人共享您的个人信息,但以下情况除外:
|
|
|
|
// (1)事先获得您明确的同意或授权;
|
|
|
|
// (2)根据适用的法律法规、法律程序的要求、强制性的行政或司法要求所必须的情况下进行提供;
|
|
|
|
// (3)在法律法规允许的范围内,为维护纳极、纳极的关联方或合作伙伴、您或其他纳极用户或社会公众利益、财产或安全免遭损害而有必要提供;
|
|
|
|
// (4) 只有共享您的个人信息,才能实现我们的产品与/或服务的核心功能或提供您需要的服务;
|
|
|
|
// (5)应您需求为您处理您与他人的纠纷或争议;
|
|
|
|
// (6)符合与您签署的相关协议(包括在线签署的电子协议以及相应的平台规则)或其他的法律文件约定所提供;
|
|
|
|
// (7)基于学术研究而使用;
|
|
|
|
// (8)基于符合法律法规的社会公共利益而使用。
|
|
|
|
// 2、对我们与之共享个人信息的公司、组织和个人,我们会与其签署严格的保密协定,要求他们按照我们的说明、本隐私政策以及其他任何相关的保密和安全措施来处理个人信息。为了保障数据安全可控,我们在云端提供安全可靠的数据使用和存储环境,确保用户数据的安全性。
|
|
|
|
// 3、为了遵守法律、执行或适用我们的使用条件和其他协议,或者为了保护纳极、您或其他纳极客户的权利及其财产或安全,比如为防止欺诈等违法活动和减少信用风险,而与其他公司和组织交换信息。不过,这并不包括违反本隐私政策中所作的承诺而为获利目的出售、出租、共享或以其它方式披露的个人信息。
|
|
|
|
// (二)转让
|
|
|
|
// 我们不会将您的个人信息转让给任何公司、组织和个人,但以下情况除外:
|
|
|
|
// 1、事先获得您明确的同意或授权;
|
|
|
|
// 2、根据适用的法律法规、法律程序的要求、强制性的行政或司法要求所必须的情况进行提供;
|
|
|
|
// 3、符合与您签署的相关协议(包括在线签署的电子协议以及相应的平台规则)或其他的法律文件约定所提供;
|
|
|
|
// 4、在涉及合并、收购、资产转让或类似的交易时,如涉及到个人信息转让,我们会要求新的持有您个人信息的公司、组织继续受本隐私政策的约束,否则,我们将要求该公司、组织重新向您征求授权同意。
|
|
|
|
// (三)公开披露
|
|
|
|
// 我们仅会在以下情况下,且采取符合业界标准的安全防护措施的前提下,才会公开披露您的个人信息:
|
|
|
|
// 1、根据您的需求,在您明确同意的披露方式下披露您所指定的个人信息;
|
|
|
|
// 2、根据法律、法规的要求、强制性的行政执法或司法要求所必须提供您个人信息的情况下,我们可能会依据所要求的个人信息类型和披露方式公开披露您的个人信息。在符合法律法规的前提下,当我们收到上述披露信息的请求时,我们会要求必须出具与之相应的法律文件,如传票或调查函。我们坚信,对于要求我们提供的信息,应该在法律允许的范围内尽可能保持透明。我们对所有的请求都进行了慎重的审查,以确保其具备合法依据,且仅限于执法部门因特定调查目的且有合法权利获取的数据。在法律法规许可的前提下,我们披露的文件均在加密密钥的保护之下。
|
|
|
|
// 三、我们如何保护和保存您的个人信息
|
|
|
|
// (一)我们保护您个人信息的技术与措施
|
|
|
|
// 我们非常重视个人信息安全,并采取一切合理可行的措施,保护您的个人信息:
|
|
|
|
// 1、数据安全技术措施
|
|
|
|
// 我们会采用符合业界标准的安全防护措施,包括建立合理的制度规范、安全技术来防止您的个人信息遭到未经授权的访问使用、修改,避免数据的损坏或丢失。
|
|
|
|
// 纳极的网络服务采取了传输层安全协议等加密技术,通过https等方式提供浏览服务,确保用户数据在传输过程中的安全。
|
|
|
|
// 纳极采取加密技术对用户个人信息进行加密保存,并通过隔离技术进行隔离。
|
|
|
|
// 在个人信息使用时,例如个人信息展示、个人信息关联计算,我们会采用包括内容替换、SHA256在内多种数据脱敏技术增强个人信息在使用中安全性。
|
|
|
|
// 纳极采用严格的数据访问权限控制和多重身份认证技术保护个人信息,避免数据被违规使用。
|
|
|
|
// 纳极采用代码安全自动检查、数据访问日志分析技术进行个人信息安全审计。
|
|
|
|
// 2、纳极为保护个人信息采取的其他安全措施
|
|
|
|
// 纳极通过建立数据分类分级制度、数据安全管理规范、数据安全开发规范来管理规范个人信息的存储和使用。
|
|
|
|
// 纳极通过信息接触者保密协议、监控和审计机制来对数据进行全面安全控制。
|
|
|
|
// 加强安全意识。我们还会举办安全和隐私保护培训课程,加强员工对于保护个人信息重要性的认识。
|
|
|
|
// 3、我们仅允许有必要知晓这些信息的纳极及纳极关联方的员工、合作伙伴访问个人信息,并为此设置了严格的访问权限控制和监控机制。我们同时要求可能接触到您个人信息的所有人员履行相应的保密义务。如果未能履行这些义务,可能会被追究法律责任或被中止与纳极的合作关系。
|
|
|
|
// 4、我们会采取一切合理可行的措施,确保未收集无关的个人信息。
|
|
|
|
// 5、如果您对我们的个人信息保护有任何疑问,可通过本隐私政策中约定的联系方式联系我们。如您发现自己的个人信息泄密,尤其是您的账户及密码发生泄露,请您立即通过本隐私政策【七、如何联系我们】中约定的联系方式联络我们,以便我们采取相应措施。
|
|
|
|
// (二)您个人信息的保存
|
|
|
|
// 1、您的个人信息将存储于中华人民共和国境内。
|
|
|
|
// 2、在您使用我们的产品与/或服务期间,您的个人信息将在为了实现本政策所述目的之期限内保存,同时将结合法律有强制的留存要求期限的规定确定,如《中华人民共和国电子商务法》要求商品和服务信息、交易信息保存时间自交易完成之日起不少于三年。在超出保存期间后,我们会根据适用法律的要求删除您的个人信息,或进行匿名化处理。
|
|
|
|
// 3、请您注意,当您或我们协助您删除相关信息后,因为适用的法律和安全技术限制,我们可能无法立即从备份系统中删除相应的信息,我们将安全地存储您的个人信息并限制对其的任何进一步的处理,直到备份可以删除或实现匿名化。
|
|
|
|
// 4、如果我们终止服务或运营,我们会至少提前三十日向您通知,并在终止服务或运营后对您的个人信息进行删除或匿名化处理。
|
|
|
|
// 四、您如何管理您的个人信息
|
|
|
|
// 纳极非常重视您对个人信息的关注,并尽全力保护您对于您个人信息访问、更正、删除以及撤回同意的权利,以使您拥有充分的能力保障您的隐私和安全。您的权利包括:
|
|
|
|
// 1、访问和更正您的个人信息
|
|
|
|
// 除法律法规规定外,您有权随时访问和更正您的个人信息,具体包括:
|
|
|
|
// (1)您的账户信息:您可以在“一键订”APP “我的”页面的“编辑”菜单中查阅您提交给纳极的所有个人信息,你也可通过上述途径更新除个人信息。
|
|
|
|
// (2)您的店铺配置信息:您可以在“一键订”APP “我的”页面进行相关预订操作的设置。
|
|
|
|
// 2、删除您的个人信息
|
|
|
|
// 您在我们的产品与/或服务页面中可以直接清除或删除的信息,包括姓名、联系电话、微信二维码信息。
|
|
|
|
// 在以下情形中,您可以向我们提出删除个人信息的请求:
|
|
|
|
// (1)如果我们处理个人信息的行为违反法律法规;
|
|
|
|
// (2)如果我们收集、使用您的个人信息,却未征得您的同意;
|
|
|
|
// (3)如果我们处理个人信息的行为违反了与您的约定;
|
|
|
|
// (4)如果您不再使用我们的产品或服务;
|
|
|
|
// (5)如果我们终止服务及运营。若我们决定响应您的删除请求,我们还将同时通知从我们获得您的个人信息的实体,要求其及时删除,除非法律法规另有规定,或这些实体获得您的独立授权。若我们响应您的删除请求,我们不能保证立即从备份系统中删除相应的信息,但会在备份系统更新时删除这些信息。
|
|
|
|
// 3、改变您授权同意的范围或撤回您的授权
|
|
|
|
// 您可以通过删除信息、关闭设备功能、在“一键订”软件中进行隐私设置等方式改变您授权我们继续收集个人信息的范围或撤回您的授权。
|
|
|
|
// 请您理解,每个业务功能需要一些基本的个人信息才能得以完成,当您撤回同意或授权后,我们无法继续为您提供撤回同意或授权所对应的服务,也不再处理您相应的个人信息。但您撤回同意或授权的决定,不会影响此前基于您的授权而开展的个人信息处理。
|
|
|
|
// 4、获取您的个人信息副本
|
|
|
|
// 您有权获取您的个人信息副本。如您需要获取我们收集的您的个人信息副本,您随时联系我们。在符合相关法律规定且技术可行的前提下,我们将根据您的要求向您提供您的个人信息副本。
|
|
|
|
// 6、响应您的请求
|
|
|
|
// 如果您无法通过上述方式访问、更正或删除您的个人信息,或您需要访问、更正或删除您在使用我们产品与/或服务时所产生的其他个人信息或者获取个人信息副本,或您认为“一键订”存在任何违反法律法规或与您关于个人信息的收集或使用的约定,您均可以通过本隐私政策中的指定方式与我们联系。为了保障安全,我们可能需要您提供书面请求,或提供您的身份证明文件,我们将在收到您反馈并验证您的身份后的15天内答复您的请求。对于您合理的请求,我们原则上不收取费用,但对多次重复、超出合理限度的请求,我们将视情收取一定成本费用。对于那些无端重复、需要过多技术手段(例如,需要开发新系统或从根本上改变现行惯例)、给他人合法权益带来风险或者非常不切实际(例如,涉及备份磁带上存放的信息)的请求,我们可能会予以拒绝。
|
|
|
|
// 在以下情形中,按照法律法规要求,我们将无法响应您的请求:
|
|
|
|
// (1)与个人信息控制者履行法律法规规定的义务相关的;
|
|
|
|
// (2)与国家安全、国防安全有关的;
|
|
|
|
// (3)与公共安全、公共卫生、重大公共利益有关的;
|
|
|
|
// (4)与犯罪侦查、起诉和审判和执行判决等有关的;
|
|
|
|
// (5)有充分证据表明您存在主观恶意或滥用权利的;
|
|
|
|
// (6)出于维护个人信息主体或其他个人的生命、财产等重大合法权益但又很难得到本人同意的;
|
|
|
|
// (7)响应您的请求将导致您或其他个人、组织的合法权益受到严重损害的;
|
|
|
|
// (8)涉及商业秘密的。
|
|
|
|
// 五、未成年人的个人信息保护
|
|
|
|
// 1、“一键订”非常重视对未成年人个人信息的保护。若您是18周岁以下的未成年人,在使用我们的产品与/或服务前,应事先取得您监护人的同意。“一键订”根据国家相关法律法规的规定保护未成年人的个人信息。
|
|
|
|
// 2、我们不会主动直接向未成年人收集其个人信息。对于经监护人同意而收集未成年人个人信息的情况,我们只会在受到法律允许、监护人同意或者保护未成年人所必要的情况下使用、共享、转让或披露此信息。
|
|
|
|
// 3、如果有事实证明未成年人在未取得监护人同意的情况下注册使用了我们的产品与/或服务,我们会与相关监护人协商,并设法尽快删除相关个人信息。
|
|
|
|
// 4、对于可能涉及的不满14周岁的儿童个人信息,我们进一步采取以下措施予以保障:
|
|
|
|
// (1)对于收集到的儿童个人信息,我们除遵守本隐私政策关于用户个人信息的约定外,还会秉持正当必要、知情同意、目的明确、安全保障、依法利用的原则,严格遵循《儿童个人信息网络保护规定》等法律法规的要求进行存储、使用、披露,且不会超过实现收集、使用目的所必须的期限,到期后我们会对儿童个人信息进行删除或匿名化处理。
|
|
|
|
// (2)当您作为监护人为被监护的儿童选择使用“一键订”相关服务时,我们可能需要向您收集被监护的儿童个人信息,用于向您履行相关服务之必要。在具体服务中需要向您收集儿童个人信息的,我们会事先取得您的授权同意,并告知您收集的目的和用途。如果您不提供前述信息,您将无法享受我们提供的相关服务。您作为监护人应当正确履行监护职责,保护儿童个人信息安全。若儿童本人需要注册或使用我们的产品与/或服务,您应正确引导并予以监护。
|
|
|
|
// (3)儿童或其监护人有权随时访问和更正儿童个人信息,还可以向我们提出更正和删除的请求。如您对儿童个人信息相关事宜有任何意见、建议或投诉、举报,请联系我们。我们会随时为您提供帮助。
|
|
|
|
// 六、通知和修订
|
|
|
|
// 1、为给您提供更好的服务以及随着纳极业务的发展,本隐私政策也会随之更新。但未经您明确同意,我们不会削减您依据本隐私政策所应享有的权利。我们会通过在纳极网站、一键订移动端上发出更新版本并在生效前通过网站公告或以其他适当方式提醒您相关内容的更新,也请您访问纳极以便及时了解最新的隐私政策。
|
|
|
|
// 2、对于重大变更,我们还会提供更为显著的通知(我们会通过包括但不限于邮件、短信或在浏览页面做特别提示等方式,说明隐私政策的具体变更内容)。
|
|
|
|
// 本隐私政策所指的重大变更包括但不限于:
|
|
|
|
// (1)我们的服务模式发生重大变化。如处理个人信息的目的、处理的个人信息类型、个人信息的使用方式等;
|
|
|
|
// (2)我们在所有权结构、组织架构等方面发生重大变化。如业务调整、破产并购等引起的所有者变更等;
|
|
|
|
// (3)个人信息共享、转让或公开披露的主要对象发生变化;
|
|
|
|
// (4)您参与个人信息处理方面的权利及其行使方式发生重大变化;
|
|
|
|
// (5)我们负责处理个人信息安全的责任部门、联络方式及投诉渠道发生变化时;
|
|
|
|
// (6)个人信息安全影响评估报告表明存在高风险时。
|
|
|
|
// 3、为了您能及时接收到通知,建议您在联系方式更新时及时通知我们。如您在本政策更新生效后继续使用我们的服务,即代表您已充分阅读、理解并接受更新后的政策并愿意受更新后的政策约束。我们鼓励您在每次使用我们服务时都查阅本政策。
|
|
|
|
// 七、如何联系我们
|
|
|
|
// 1、如您对本隐私政策或您个人信息的相关事宜或者投诉、举报、有任何问题、意见或建议,请通过以下方式与我们联系。
|
|
|
|
// 联系电话:400-000-9690
|
|
|
|
// 2、一般情况下,我们将在15天内回复。如果您对我们的回复不满意,特别是我们的个人信息处理行为损害了您的合法权益,您还可以向网信、电信、公安及工商等监管部门进行投诉或举报。
|
|
|
|
// 注:本隐私政策版本更新日期为2021年【8】月【6】日,将于2021年【8】月【7】日正式生效。
|
|
|
|
// ''');
|
|
|
|
// return Scaffold(
|
|
|
|
// // We're using a Builder here so we have a context that is below the Scaffold
|
|
|
|
// // to allow calling Scaffold.of(context) so we can show a snackbar.
|
|
|
|
// appBar: SystemAppBar(
|
|
|
|
// widget.title,
|
|
|
|
// onTap: () {
|
|
|
|
// if (widget.need) {
|
|
|
|
// Utils.changeStatusColor(color: MyColours.color_199095);
|
|
|
|
// }
|
|
|
|
// Navigator.of(context).pop();
|
|
|
|
// },
|
|
|
|
// ),
|
|
|
|
// body: WillPopScope(
|
|
|
|
// onWillPop: () async {
|
|
|
|
// if (widget.need) {
|
|
|
|
// Utils.changeStatusColor(color: MyColours.color_199095);
|
|
|
|
// }
|
|
|
|
// return true;
|
|
|
|
// },
|
|
|
|
// child: Column(children: [
|
|
|
|
// Builder(builder: (BuildContext context) {
|
|
|
|
// return Expanded(
|
|
|
|
// child: WebView(
|
|
|
|
// initialUrl: "http://colorcun.com/privacy1.html",
|
|
|
|
// javascriptMode: JavascriptMode.unrestricted,
|
|
|
|
// onWebViewCreated: (WebViewController webViewController) {
|
|
|
|
// _controller.complete(webViewController);
|
|
|
|
// },
|
|
|
|
// // TODO(iskakaushik): Remove this when collection literals makes it to stable.
|
|
|
|
// // ignore: prefer_collection_literals
|
|
|
|
// javascriptChannels: <JavascriptChannel>[
|
|
|
|
// _toasterJavascriptChannel(context),
|
|
|
|
// ].toSet(),
|
|
|
|
// navigationDelegate: (NavigationRequest request) {
|
|
|
|
// if (request.url.startsWith('https://www.youtube.com/')) {
|
|
|
|
// print('blocking navigation to $request}');
|
|
|
|
// return NavigationDecision.prevent;
|
|
|
|
// }
|
|
|
|
// print('allowing navigation to $request');
|
|
|
|
// return NavigationDecision.navigate;
|
|
|
|
// },
|
|
|
|
// onPageStarted: (String url) {
|
|
|
|
// print('Page started loading: $url');
|
|
|
|
// },
|
|
|
|
// onPageFinished: (String url) {
|
|
|
|
// print('Page finished loading: $url');
|
|
|
|
// },
|
|
|
|
// gestureNavigationEnabled: true,
|
|
|
|
// ));
|
|
|
|
// }),
|
|
|
|
// ])));
|
|
|
|
// }
|
|
|
|
|
|
|
|
// JavascriptChannel _toasterJavascriptChannel(BuildContext context) {
|
|
|
|
// return JavascriptChannel(
|
|
|
|
// name: 'Toaster',
|
|
|
|
// onMessageReceived: (JavascriptMessage message) {
|
|
|
|
// Scaffold.of(context).showSnackBar(
|
|
|
|
// SnackBar(content: Text(message.message)),
|
|
|
|
// );
|
|
|
|
// });
|
|
|
|
// }
|
|
|
|
// }
|
|
|
|
import 'package:flutter/material.dart';
|
|
|
|
import 'package:one_key/res/colors.dart';
|
|
|
|
import 'package:one_key/utils/event_bus.dart';
|
|
|
|
import 'package:one_key/utils/utils.dart';
|
|
|
|
import 'package:one_key/widgets/app_bar.dart';
|
|
|
|
import 'package:one_key/widgets/system_app_bar.dart';
|
|
|
|
import 'package:webview_flutter/webview_flutter.dart';
|
|
|
|
|
|
|
|
class WebPage extends StatefulWidget {
|
|
|
|
final String title;
|
|
...
|
...
|
@@ -22,14 +262,104 @@ class WebPage extends StatefulWidget { |
|
|
|
}
|
|
|
|
|
|
|
|
class _WebPageState extends State<WebPage> {
|
|
|
|
final Completer<WebViewController> _controller =
|
|
|
|
Completer<WebViewController>();
|
|
|
|
final GlobalKey webViewKey = GlobalKey();
|
|
|
|
|
|
|
|
InAppWebViewController webViewController;
|
|
|
|
InAppWebViewGroupOptions options = InAppWebViewGroupOptions(
|
|
|
|
crossPlatform: InAppWebViewOptions(
|
|
|
|
useShouldOverrideUrlLoading: true,
|
|
|
|
useOnDownloadStart: true,
|
|
|
|
mediaPlaybackRequiresUserGesture: false,
|
|
|
|
),
|
|
|
|
android: AndroidInAppWebViewOptions(
|
|
|
|
useHybridComposition: true,
|
|
|
|
),
|
|
|
|
ios: IOSInAppWebViewOptions(
|
|
|
|
allowsInlineMediaPlayback: true,
|
|
|
|
));
|
|
|
|
|
|
|
|
PullToRefreshController pullToRefreshController;
|
|
|
|
ContextMenu contextMenu;
|
|
|
|
String url = "";
|
|
|
|
double progress = 0;
|
|
|
|
final urlController = TextEditingController();
|
|
|
|
// Future<void> openFile(filePath) async {
|
|
|
|
// final _result = await OpenFile.open(filePath);
|
|
|
|
// print(_result.message);
|
|
|
|
|
|
|
|
// // setState(() {
|
|
|
|
// // _openResult = "type=${_result.type} message=${_result.message}";
|
|
|
|
// // });
|
|
|
|
// }
|
|
|
|
|
|
|
|
@override
|
|
|
|
void initState() {
|
|
|
|
super.initState();
|
|
|
|
|
|
|
|
// Enable hybrid composition.
|
|
|
|
if (Platform.isAndroid) WebView.platform = SurfaceAndroidWebView();
|
|
|
|
_init();
|
|
|
|
}
|
|
|
|
|
|
|
|
// Future<bool> _checkPermission() async {
|
|
|
|
// DeviceInfoPlugin deviceInfo = DeviceInfoPlugin();
|
|
|
|
// AndroidDeviceInfo androidInfo = await deviceInfo.androidInfo;
|
|
|
|
// if (Platform.isAndroid && androidInfo.version.sdkInt <= 28) {
|
|
|
|
// final status = await Permission.storage.status;
|
|
|
|
// if (status != PermissionStatus.granted) {
|
|
|
|
// final result = await Permission.storage.request();
|
|
|
|
// if (result == PermissionStatus.granted) {
|
|
|
|
// return true;
|
|
|
|
// }
|
|
|
|
// } else {
|
|
|
|
// return true;
|
|
|
|
// }
|
|
|
|
// } else {
|
|
|
|
// return true;
|
|
|
|
// }
|
|
|
|
// return false;
|
|
|
|
// }
|
|
|
|
|
|
|
|
// Future<bool> checkPermissionStorage() async {
|
|
|
|
// var status = await Permission.storage.status;
|
|
|
|
// if (!status.isGranted) {
|
|
|
|
// status = await Permission.storage.request();
|
|
|
|
// if (status.isGranted) {
|
|
|
|
// return true;
|
|
|
|
// }
|
|
|
|
// } else {
|
|
|
|
// return true;
|
|
|
|
// }
|
|
|
|
// return false;
|
|
|
|
// }
|
|
|
|
|
|
|
|
_init() async {
|
|
|
|
// WidgetsFlutterBinding.ensureInitialized();
|
|
|
|
// await FlutterDownloader.initialize(debug: true);
|
|
|
|
if (Platform.isAndroid) {
|
|
|
|
await AndroidInAppWebViewController.setWebContentsDebuggingEnabled(true);
|
|
|
|
|
|
|
|
var swAvailable = await AndroidWebViewFeature.isFeatureSupported(
|
|
|
|
AndroidWebViewFeature.SERVICE_WORKER_BASIC_USAGE);
|
|
|
|
var swInterceptAvailable = await AndroidWebViewFeature.isFeatureSupported(
|
|
|
|
AndroidWebViewFeature.SERVICE_WORKER_SHOULD_INTERCEPT_REQUEST);
|
|
|
|
|
|
|
|
if (swAvailable && swInterceptAvailable) {
|
|
|
|
AndroidServiceWorkerController serviceWorkerController =
|
|
|
|
AndroidServiceWorkerController.instance();
|
|
|
|
|
|
|
|
serviceWorkerController.serviceWorkerClient =
|
|
|
|
AndroidServiceWorkerClient(
|
|
|
|
shouldInterceptRequest: (request) async {
|
|
|
|
print(request);
|
|
|
|
return null;
|
|
|
|
},
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// await FlutterDownloader.initialize(
|
|
|
|
// debug: true // optional: set false to disable printing logs to console
|
|
|
|
// );
|
|
|
|
// await Permission.storage.request();
|
|
|
|
}
|
|
|
|
|
|
|
|
@override
|
|
...
|
...
|
@@ -53,52 +383,201 @@ class _WebPageState extends State<WebPage> { |
|
|
|
}
|
|
|
|
return true;
|
|
|
|
},
|
|
|
|
child: Column(children: [
|
|
|
|
Builder(builder: (BuildContext context) {
|
|
|
|
return Expanded(
|
|
|
|
child: WebView(
|
|
|
|
initialUrl: "http://colorcun.com/privacy1.html",
|
|
|
|
javascriptMode: JavascriptMode.unrestricted,
|
|
|
|
onWebViewCreated: (WebViewController webViewController) {
|
|
|
|
_controller.complete(webViewController);
|
|
|
|
child: Column(
|
|
|
|
children: [
|
|
|
|
Expanded(
|
|
|
|
child: InAppWebView(
|
|
|
|
key: webViewKey,
|
|
|
|
// contextMenu: contextMenu,
|
|
|
|
initialUrlRequest: URLRequest(
|
|
|
|
url: Uri.parse("http://colorcun.com/privacy1.html")),
|
|
|
|
// initialFile: "assets/index.html",
|
|
|
|
initialUserScripts: UnmodifiableListView<UserScript>([]),
|
|
|
|
initialOptions: options,
|
|
|
|
pullToRefreshController: pullToRefreshController,
|
|
|
|
onWebViewCreated: (controller) {
|
|
|
|
webViewController = controller;
|
|
|
|
},
|
|
|
|
// TODO(iskakaushik): Remove this when collection literals makes it to stable.
|
|
|
|
// ignore: prefer_collection_literals
|
|
|
|
javascriptChannels: <JavascriptChannel>[
|
|
|
|
_toasterJavascriptChannel(context),
|
|
|
|
].toSet(),
|
|
|
|
navigationDelegate: (NavigationRequest request) {
|
|
|
|
if (request.url.startsWith('https://www.youtube.com/')) {
|
|
|
|
print('blocking navigation to $request}');
|
|
|
|
return NavigationDecision.prevent;
|
|
|
|
onLoadStart: (controller, url) {
|
|
|
|
// setState(() {
|
|
|
|
// this.url = url.toString();
|
|
|
|
// urlController.text = this.url;
|
|
|
|
// });
|
|
|
|
},
|
|
|
|
androidOnPermissionRequest:
|
|
|
|
(controller, origin, resources) async {
|
|
|
|
// return PermissionRequestResponse(
|
|
|
|
// resources: resources,
|
|
|
|
// action: PermissionRequestResponseAction.GRANT);
|
|
|
|
},
|
|
|
|
shouldOverrideUrlLoading:
|
|
|
|
(controller, navigationAction) async {
|
|
|
|
var uri = navigationAction.request.url;
|
|
|
|
|
|
|
|
if (![
|
|
|
|
"http",
|
|
|
|
"https",
|
|
|
|
"file",
|
|
|
|
"chrome",
|
|
|
|
"data",
|
|
|
|
"javascript",
|
|
|
|
"about"
|
|
|
|
].contains(uri.scheme)) {
|
|
|
|
if (await canLaunch(url)) {
|
|
|
|
// Launch the App
|
|
|
|
await launch(
|
|
|
|
url,
|
|
|
|
);
|
|
|
|
// and cancel the request
|
|
|
|
return NavigationActionPolicy.CANCEL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
print('allowing navigation to $request');
|
|
|
|
return NavigationDecision.navigate;
|
|
|
|
|
|
|
|
return NavigationActionPolicy.ALLOW;
|
|
|
|
},
|
|
|
|
onPageStarted: (String url) {
|
|
|
|
print('Page started loading: $url');
|
|
|
|
onLoadStop: (controller, url) async {
|
|
|
|
// pullToRefreshController.endRefreshing();
|
|
|
|
// setState(() {
|
|
|
|
// this.url = url.toString();
|
|
|
|
// urlController.text = this.url;
|
|
|
|
// });
|
|
|
|
},
|
|
|
|
onPageFinished: (String url) {
|
|
|
|
print('Page finished loading: $url');
|
|
|
|
onLoadError: (controller, url, code, message) {
|
|
|
|
pullToRefreshController.endRefreshing();
|
|
|
|
},
|
|
|
|
gestureNavigationEnabled: true,
|
|
|
|
));
|
|
|
|
}),
|
|
|
|
])));
|
|
|
|
}
|
|
|
|
onProgressChanged: (controller, progress) {
|
|
|
|
// if (progress == 100) {
|
|
|
|
pullToRefreshController.endRefreshing();
|
|
|
|
// }
|
|
|
|
// setState(() {
|
|
|
|
// this.progress = progress / 100;
|
|
|
|
// urlController.text = this.url;
|
|
|
|
// });
|
|
|
|
},
|
|
|
|
onUpdateVisitedHistory: (controller, url, androidIsReload) {
|
|
|
|
// setState(() {
|
|
|
|
// this.url = url.toString();
|
|
|
|
// urlController.text = this.url;
|
|
|
|
// });
|
|
|
|
},
|
|
|
|
onConsoleMessage: (controller, consoleMessage) {
|
|
|
|
print(consoleMessage);
|
|
|
|
},
|
|
|
|
onDownloadStart: (controller, url) async {
|
|
|
|
// print("onDownloadStart $url");
|
|
|
|
// Utils.showToast('开始下载...');
|
|
|
|
// // final taskId = await FlutterDownloader.enqueue(
|
|
|
|
// // url: url.toString(),
|
|
|
|
// // savedDir: (await getExternalStorageDirectory()).path,
|
|
|
|
// // showNotification:
|
|
|
|
// // true, // show download progress in status bar (for Android)
|
|
|
|
// // openFileFromNotification:
|
|
|
|
// // true, // click on notification to open downloaded file (for Android)
|
|
|
|
// // );
|
|
|
|
// final dir = await getExternalStorageDirectory();
|
|
|
|
// String savePath = dir.path + "/one_key";
|
|
|
|
|
|
|
|
JavascriptChannel _toasterJavascriptChannel(BuildContext context) {
|
|
|
|
return JavascriptChannel(
|
|
|
|
name: 'Toaster',
|
|
|
|
onMessageReceived: (JavascriptMessage message) {
|
|
|
|
Scaffold.of(context).showSnackBar(
|
|
|
|
SnackBar(content: Text(message.message)),
|
|
|
|
);
|
|
|
|
});
|
|
|
|
// Directory directory = new Directory(savePath);
|
|
|
|
// if (directory.existsSync()) {
|
|
|
|
// //删除历史apk
|
|
|
|
// final List<FileSystemEntity> files = directory.listSync();
|
|
|
|
// for (FileSystemEntity file in files) {
|
|
|
|
// file.deleteSync();
|
|
|
|
// }
|
|
|
|
// }
|
|
|
|
// DioUtil().download(url.toString(), savePath,
|
|
|
|
// onProgress: (received, total) {
|
|
|
|
// // if (_progress != 100) {
|
|
|
|
// // int progress = ((received / total) * 100).round();
|
|
|
|
// // _progress = progress;
|
|
|
|
// // _setState(() {});
|
|
|
|
// // }
|
|
|
|
// if (received == total) {
|
|
|
|
// print('object');
|
|
|
|
// Utils.showToast('文件已保存在${savePath}');
|
|
|
|
// // openFile(savePath);
|
|
|
|
// //下载完成
|
|
|
|
// // InstallPlugin.installApk(savePath, "com.naji.one_key");
|
|
|
|
// }
|
|
|
|
// });
|
|
|
|
// // },
|
|
|
|
// // DioUtil().download(url.toString(),
|
|
|
|
// // (await getExternalStorageDirectory()).path,
|
|
|
|
// // onProgress: (received, total) {
|
|
|
|
// // // if (_progress != 100) {
|
|
|
|
// // // int progress = ((received / total) * 100).round();
|
|
|
|
// // // _progress = progress;
|
|
|
|
// // // _setState(() {});
|
|
|
|
// // // }
|
|
|
|
// // if (received == total) {
|
|
|
|
// // //下载完成
|
|
|
|
// // // InstallPlugin.installApk(savePath, "com.naji.one_key");
|
|
|
|
// // }
|
|
|
|
// // });
|
|
|
|
},
|
|
|
|
))
|
|
|
|
],
|
|
|
|
)
|
|
|
|
// Builder(builder: (BuildContext context) {
|
|
|
|
// return WebView(
|
|
|
|
// initialUrl: "http://colorcun.com/privacy1.html",
|
|
|
|
// javascriptMode: JavascriptMode.unrestricted,
|
|
|
|
// onWebViewCreated: (WebViewController webViewController) {
|
|
|
|
// _controller.complete(webViewController);
|
|
|
|
// },
|
|
|
|
// // TODO(iskakaushik): Remove this when collection literals makes it to stable.
|
|
|
|
// // ignore: prefer_collection_literals
|
|
|
|
// javascriptChannels: <JavascriptChannel>[
|
|
|
|
// _toasterJavascriptChannel(context),
|
|
|
|
// ].toSet(),
|
|
|
|
// navigationDelegate: (NavigationRequest request) {
|
|
|
|
// if (request.url.startsWith('https://www.youtube.com/')) {
|
|
|
|
// print('blocking navigation to $request}');
|
|
|
|
// return NavigationDecision.prevent;
|
|
|
|
// }
|
|
|
|
// print('allowing navigation to $request');
|
|
|
|
// return NavigationDecision.navigate;
|
|
|
|
// },
|
|
|
|
// onPageStarted: (String url) {
|
|
|
|
// print('Page started loading: $url');
|
|
|
|
// },
|
|
|
|
// onPageFinished: (String url) async {
|
|
|
|
// print('Page finished loading: $url');
|
|
|
|
// // showDownloadProgress(context);
|
|
|
|
// final dir = await getExternalStorageDirectory();
|
|
|
|
// String savePath = dir.path + "/one_key";
|
|
|
|
// DioUtil().download(url, savePath,
|
|
|
|
// onProgress: (received, total) {
|
|
|
|
// // if (_progress != 100) {
|
|
|
|
// // int progress = ((received / total) * 100).round();
|
|
|
|
// // _progress = progress;
|
|
|
|
// // _setState(() {});
|
|
|
|
// // }
|
|
|
|
// if (received == total) {
|
|
|
|
// print('object');
|
|
|
|
// //下载完成
|
|
|
|
// // InstallPlugin.installApk(savePath, "com.naji.one_key");
|
|
|
|
// }
|
|
|
|
// });
|
|
|
|
// },
|
|
|
|
// gestureNavigationEnabled: true,
|
|
|
|
// );
|
|
|
|
// }),
|
|
|
|
));
|
|
|
|
}
|
|
|
|
|
|
|
|
// JavascriptChannel _toasterJavascriptChannel(BuildContext context) {
|
|
|
|
// return JavascriptChannel(
|
|
|
|
// name: 'Toaster',
|
|
|
|
// onMessageReceived: (JavascriptMessage message) {
|
|
|
|
// Scaffold.of(context).showSnackBar(
|
|
|
|
// SnackBar(content: Text(message.message)),
|
|
|
|
// );
|
|
|
|
// });
|
|
|
|
// }
|
|
|
|
}
|
|
|
|
|
|
|
|
// class WebPage extends StatefulWidget {
|
|
|
|
// class WebPage extends StatelessWidget {
|
|
|
|
// final String title;
|
|
|
|
// final String url;
|
|
|
|
// final bool need;
|
|
...
|
...
|
@@ -106,382 +585,55 @@ class _WebPageState extends State<WebPage> { |
|
|
|
// const WebPage({Key key, this.title, this.url, this.need = false})
|
|
|
|
// : super(key: key);
|
|
|
|
// @override
|
|
|
|
// _WebPageState createState() => _WebPageState();
|
|
|
|
// }
|
|
|
|
|
|
|
|
// class _WebPageState extends State<WebPage> {
|
|
|
|
// final GlobalKey webViewKey = GlobalKey();
|
|
|
|
|
|
|
|
// InAppWebViewController webViewController;
|
|
|
|
// InAppWebViewGroupOptions options = InAppWebViewGroupOptions(
|
|
|
|
// crossPlatform: InAppWebViewOptions(
|
|
|
|
// useShouldOverrideUrlLoading: true,
|
|
|
|
// useOnDownloadStart: true,
|
|
|
|
// mediaPlaybackRequiresUserGesture: false,
|
|
|
|
// ),
|
|
|
|
// android: AndroidInAppWebViewOptions(
|
|
|
|
// useHybridComposition: true,
|
|
|
|
// ),
|
|
|
|
// ios: IOSInAppWebViewOptions(
|
|
|
|
// allowsInlineMediaPlayback: true,
|
|
|
|
// ));
|
|
|
|
|
|
|
|
// PullToRefreshController pullToRefreshController;
|
|
|
|
// ContextMenu contextMenu;
|
|
|
|
// String url = "";
|
|
|
|
// double progress = 0;
|
|
|
|
// final urlController = TextEditingController();
|
|
|
|
// // Future<void> openFile(filePath) async {
|
|
|
|
// // final _result = await OpenFile.open(filePath);
|
|
|
|
// // print(_result.message);
|
|
|
|
|
|
|
|
// // // setState(() {
|
|
|
|
// // // _openResult = "type=${_result.type} message=${_result.message}";
|
|
|
|
// // // });
|
|
|
|
// // }
|
|
|
|
|
|
|
|
// @override
|
|
|
|
// void initState() {
|
|
|
|
// super.initState();
|
|
|
|
|
|
|
|
// // Enable hybrid composition.
|
|
|
|
// _init();
|
|
|
|
// }
|
|
|
|
|
|
|
|
// // Future<bool> _checkPermission() async {
|
|
|
|
// // DeviceInfoPlugin deviceInfo = DeviceInfoPlugin();
|
|
|
|
// // AndroidDeviceInfo androidInfo = await deviceInfo.androidInfo;
|
|
|
|
// // if (Platform.isAndroid && androidInfo.version.sdkInt <= 28) {
|
|
|
|
// // final status = await Permission.storage.status;
|
|
|
|
// // if (status != PermissionStatus.granted) {
|
|
|
|
// // final result = await Permission.storage.request();
|
|
|
|
// // if (result == PermissionStatus.granted) {
|
|
|
|
// // return true;
|
|
|
|
// // }
|
|
|
|
// // } else {
|
|
|
|
// // return true;
|
|
|
|
// // }
|
|
|
|
// // } else {
|
|
|
|
// // return true;
|
|
|
|
// // }
|
|
|
|
// // return false;
|
|
|
|
// // }
|
|
|
|
|
|
|
|
// // Future<bool> checkPermissionStorage() async {
|
|
|
|
// // var status = await Permission.storage.status;
|
|
|
|
// // if (!status.isGranted) {
|
|
|
|
// // status = await Permission.storage.request();
|
|
|
|
// // if (status.isGranted) {
|
|
|
|
// // return true;
|
|
|
|
// // }
|
|
|
|
// // } else {
|
|
|
|
// // return true;
|
|
|
|
// // }
|
|
|
|
// // return false;
|
|
|
|
// // }
|
|
|
|
|
|
|
|
// _init() async {
|
|
|
|
// // WidgetsFlutterBinding.ensureInitialized();
|
|
|
|
// // await FlutterDownloader.initialize(debug: true);
|
|
|
|
// if (Platform.isAndroid) {
|
|
|
|
// await AndroidInAppWebViewController.setWebContentsDebuggingEnabled(true);
|
|
|
|
|
|
|
|
// var swAvailable = await AndroidWebViewFeature.isFeatureSupported(
|
|
|
|
// AndroidWebViewFeature.SERVICE_WORKER_BASIC_USAGE);
|
|
|
|
// var swInterceptAvailable = await AndroidWebViewFeature.isFeatureSupported(
|
|
|
|
// AndroidWebViewFeature.SERVICE_WORKER_SHOULD_INTERCEPT_REQUEST);
|
|
|
|
|
|
|
|
// if (swAvailable && swInterceptAvailable) {
|
|
|
|
// AndroidServiceWorkerController serviceWorkerController =
|
|
|
|
// AndroidServiceWorkerController.instance();
|
|
|
|
|
|
|
|
// serviceWorkerController.serviceWorkerClient =
|
|
|
|
// AndroidServiceWorkerClient(
|
|
|
|
// shouldInterceptRequest: (request) async {
|
|
|
|
// print(request);
|
|
|
|
// return null;
|
|
|
|
// },
|
|
|
|
// );
|
|
|
|
// }
|
|
|
|
// }
|
|
|
|
// // await FlutterDownloader.initialize(
|
|
|
|
// // debug: true // optional: set false to disable printing logs to console
|
|
|
|
// // );
|
|
|
|
// // await Permission.storage.request();
|
|
|
|
// }
|
|
|
|
|
|
|
|
// @override
|
|
|
|
// Widget build(BuildContext context) {
|
|
|
|
// final Completer<WebViewController> _controller =
|
|
|
|
// Completer<WebViewController>();
|
|
|
|
// return Scaffold(
|
|
|
|
// // We're using a Builder here so we have a context that is below the Scaffold
|
|
|
|
// // to allow calling Scaffold.of(context) so we can show a snackbar.
|
|
|
|
// appBar: SystemAppBar(
|
|
|
|
// widget.title,
|
|
|
|
// onTap: () {
|
|
|
|
// if (widget.need) {
|
|
|
|
// appBar: SystemAppBar(title, onTap: () {
|
|
|
|
// if (need) {
|
|
|
|
// Utils.changeStatusColor(color: MyColours.color_199095);
|
|
|
|
// }
|
|
|
|
// Navigator.of(context).pop();
|
|
|
|
// }),
|
|
|
|
// body: WillPopScope(
|
|
|
|
// onWillPop: () async {
|
|
|
|
// if (need) {
|
|
|
|
// Utils.changeStatusColor(color: MyColours.color_199095);
|
|
|
|
// }
|
|
|
|
// Navigator.of(context).pop();
|
|
|
|
// return true;
|
|
|
|
// },
|
|
|
|
// ),
|
|
|
|
// body: WillPopScope(
|
|
|
|
// onWillPop: () async {
|
|
|
|
// if (widget.need) {
|
|
|
|
// Utils.changeStatusColor(color: MyColours.color_199095);
|
|
|
|
// child: WebView(
|
|
|
|
// initialUrl: "http://colorcun.com/privacy1.html",
|
|
|
|
// javascriptMode: JavascriptMode.unrestricted,
|
|
|
|
// onWebViewCreated: (WebViewController webViewController) {
|
|
|
|
// // Loading.showLoading(context);
|
|
|
|
// _controller.complete(webViewController);
|
|
|
|
// },
|
|
|
|
// javascriptChannels: <JavascriptChannel>[
|
|
|
|
// _toasterJavascriptChannel(context),
|
|
|
|
// ].toSet(),
|
|
|
|
// navigationDelegate: (NavigationRequest request) {
|
|
|
|
// if (request.url.startsWith('https://www.youtube.com/')) {
|
|
|
|
// return NavigationDecision.prevent;
|
|
|
|
// }
|
|
|
|
// return true;
|
|
|
|
// return NavigationDecision.navigate;
|
|
|
|
// },
|
|
|
|
// child: Column(
|
|
|
|
// children: [
|
|
|
|
// Expanded(
|
|
|
|
// child: InAppWebView(
|
|
|
|
// key: webViewKey,
|
|
|
|
// // contextMenu: contextMenu,
|
|
|
|
// initialUrlRequest: URLRequest(
|
|
|
|
// url: Uri.parse("http://colorcun.com/privacy1.html")),
|
|
|
|
// // initialFile: "assets/index.html",
|
|
|
|
// initialUserScripts: UnmodifiableListView<UserScript>([]),
|
|
|
|
// initialOptions: options,
|
|
|
|
// pullToRefreshController: pullToRefreshController,
|
|
|
|
// onWebViewCreated: (controller) {
|
|
|
|
// webViewController = controller;
|
|
|
|
// },
|
|
|
|
// onLoadStart: (controller, url) {
|
|
|
|
// // setState(() {
|
|
|
|
// // this.url = url.toString();
|
|
|
|
// // urlController.text = this.url;
|
|
|
|
// // });
|
|
|
|
// },
|
|
|
|
// androidOnPermissionRequest:
|
|
|
|
// (controller, origin, resources) async {
|
|
|
|
// // return PermissionRequestResponse(
|
|
|
|
// // resources: resources,
|
|
|
|
// // action: PermissionRequestResponseAction.GRANT);
|
|
|
|
// },
|
|
|
|
// shouldOverrideUrlLoading:
|
|
|
|
// (controller, navigationAction) async {
|
|
|
|
// var uri = navigationAction.request.url;
|
|
|
|
|
|
|
|
// if (![
|
|
|
|
// "http",
|
|
|
|
// "https",
|
|
|
|
// "file",
|
|
|
|
// "chrome",
|
|
|
|
// "data",
|
|
|
|
// "javascript",
|
|
|
|
// "about"
|
|
|
|
// ].contains(uri.scheme)) {
|
|
|
|
// if (await canLaunch(url)) {
|
|
|
|
// // Launch the App
|
|
|
|
// await launch(
|
|
|
|
// url,
|
|
|
|
// );
|
|
|
|
// // and cancel the request
|
|
|
|
// return NavigationActionPolicy.CANCEL;
|
|
|
|
// }
|
|
|
|
// }
|
|
|
|
|
|
|
|
// return NavigationActionPolicy.ALLOW;
|
|
|
|
// },
|
|
|
|
// onLoadStop: (controller, url) async {
|
|
|
|
// // pullToRefreshController.endRefreshing();
|
|
|
|
// // setState(() {
|
|
|
|
// // this.url = url.toString();
|
|
|
|
// // urlController.text = this.url;
|
|
|
|
// // });
|
|
|
|
// },
|
|
|
|
// onLoadError: (controller, url, code, message) {
|
|
|
|
// pullToRefreshController.endRefreshing();
|
|
|
|
// },
|
|
|
|
// onProgressChanged: (controller, progress) {
|
|
|
|
// // if (progress == 100) {
|
|
|
|
// pullToRefreshController.endRefreshing();
|
|
|
|
// // }
|
|
|
|
// // setState(() {
|
|
|
|
// // this.progress = progress / 100;
|
|
|
|
// // urlController.text = this.url;
|
|
|
|
// // });
|
|
|
|
// },
|
|
|
|
// onUpdateVisitedHistory: (controller, url, androidIsReload) {
|
|
|
|
// // setState(() {
|
|
|
|
// // this.url = url.toString();
|
|
|
|
// // urlController.text = this.url;
|
|
|
|
// // });
|
|
|
|
// },
|
|
|
|
// onConsoleMessage: (controller, consoleMessage) {
|
|
|
|
// print(consoleMessage);
|
|
|
|
// },
|
|
|
|
// onDownloadStart: (controller, url) async {
|
|
|
|
// // print("onDownloadStart $url");
|
|
|
|
// // Utils.showToast('开始下载...');
|
|
|
|
// // // final taskId = await FlutterDownloader.enqueue(
|
|
|
|
// // // url: url.toString(),
|
|
|
|
// // // savedDir: (await getExternalStorageDirectory()).path,
|
|
|
|
// // // showNotification:
|
|
|
|
// // // true, // show download progress in status bar (for Android)
|
|
|
|
// // // openFileFromNotification:
|
|
|
|
// // // true, // click on notification to open downloaded file (for Android)
|
|
|
|
// // // );
|
|
|
|
// // final dir = await getExternalStorageDirectory();
|
|
|
|
// // String savePath = dir.path + "/one_key";
|
|
|
|
|
|
|
|
// // Directory directory = new Directory(savePath);
|
|
|
|
// // if (directory.existsSync()) {
|
|
|
|
// // //删除历史apk
|
|
|
|
// // final List<FileSystemEntity> files = directory.listSync();
|
|
|
|
// // for (FileSystemEntity file in files) {
|
|
|
|
// // file.deleteSync();
|
|
|
|
// // }
|
|
|
|
// // }
|
|
|
|
// // DioUtil().download(url.toString(), savePath,
|
|
|
|
// // onProgress: (received, total) {
|
|
|
|
// // // if (_progress != 100) {
|
|
|
|
// // // int progress = ((received / total) * 100).round();
|
|
|
|
// // // _progress = progress;
|
|
|
|
// // // _setState(() {});
|
|
|
|
// // // }
|
|
|
|
// // if (received == total) {
|
|
|
|
// // print('object');
|
|
|
|
// // Utils.showToast('文件已保存在${savePath}');
|
|
|
|
// // // openFile(savePath);
|
|
|
|
// // //下载完成
|
|
|
|
// // // InstallPlugin.installApk(savePath, "com.naji.one_key");
|
|
|
|
// // }
|
|
|
|
// // });
|
|
|
|
// // // },
|
|
|
|
// // // DioUtil().download(url.toString(),
|
|
|
|
// // // (await getExternalStorageDirectory()).path,
|
|
|
|
// // // onProgress: (received, total) {
|
|
|
|
// // // // if (_progress != 100) {
|
|
|
|
// // // // int progress = ((received / total) * 100).round();
|
|
|
|
// // // // _progress = progress;
|
|
|
|
// // // // _setState(() {});
|
|
|
|
// // // // }
|
|
|
|
// // // if (received == total) {
|
|
|
|
// // // //下载完成
|
|
|
|
// // // // InstallPlugin.installApk(savePath, "com.naji.one_key");
|
|
|
|
// // // }
|
|
|
|
// // // });
|
|
|
|
// },
|
|
|
|
// ))
|
|
|
|
// ],
|
|
|
|
// )
|
|
|
|
// // Builder(builder: (BuildContext context) {
|
|
|
|
// // return WebView(
|
|
|
|
// // initialUrl: "http://colorcun.com/privacy1.html",
|
|
|
|
// // javascriptMode: JavascriptMode.unrestricted,
|
|
|
|
// // onWebViewCreated: (WebViewController webViewController) {
|
|
|
|
// // _controller.complete(webViewController);
|
|
|
|
// // },
|
|
|
|
// // // TODO(iskakaushik): Remove this when collection literals makes it to stable.
|
|
|
|
// // // ignore: prefer_collection_literals
|
|
|
|
// // javascriptChannels: <JavascriptChannel>[
|
|
|
|
// // _toasterJavascriptChannel(context),
|
|
|
|
// // ].toSet(),
|
|
|
|
// // navigationDelegate: (NavigationRequest request) {
|
|
|
|
// // if (request.url.startsWith('https://www.youtube.com/')) {
|
|
|
|
// // print('blocking navigation to $request}');
|
|
|
|
// // return NavigationDecision.prevent;
|
|
|
|
// // }
|
|
|
|
// // print('allowing navigation to $request');
|
|
|
|
// // return NavigationDecision.navigate;
|
|
|
|
// // },
|
|
|
|
// // onPageStarted: (String url) {
|
|
|
|
// // print('Page started loading: $url');
|
|
|
|
// // },
|
|
|
|
// // onPageFinished: (String url) async {
|
|
|
|
// // print('Page finished loading: $url');
|
|
|
|
// // // showDownloadProgress(context);
|
|
|
|
// // final dir = await getExternalStorageDirectory();
|
|
|
|
// // String savePath = dir.path + "/one_key";
|
|
|
|
// // DioUtil().download(url, savePath,
|
|
|
|
// // onProgress: (received, total) {
|
|
|
|
// // // if (_progress != 100) {
|
|
|
|
// // // int progress = ((received / total) * 100).round();
|
|
|
|
// // // _progress = progress;
|
|
|
|
// // // _setState(() {});
|
|
|
|
// // // }
|
|
|
|
// // if (received == total) {
|
|
|
|
// // print('object');
|
|
|
|
// // //下载完成
|
|
|
|
// // // InstallPlugin.installApk(savePath, "com.naji.one_key");
|
|
|
|
// // }
|
|
|
|
// // });
|
|
|
|
// // },
|
|
|
|
// // gestureNavigationEnabled: true,
|
|
|
|
// // );
|
|
|
|
// // }),
|
|
|
|
// ));
|
|
|
|
// onPageStarted: (String url) {},
|
|
|
|
// onPageFinished: (String url) {
|
|
|
|
// // Loading.hideLoading(context);
|
|
|
|
// },
|
|
|
|
// gestureNavigationEnabled: true,
|
|
|
|
// )),
|
|
|
|
// );
|
|
|
|
// }
|
|
|
|
|
|
|
|
// // JavascriptChannel _toasterJavascriptChannel(BuildContext context) {
|
|
|
|
// // return JavascriptChannel(
|
|
|
|
// // name: 'Toaster',
|
|
|
|
// // onMessageReceived: (JavascriptMessage message) {
|
|
|
|
// // Scaffold.of(context).showSnackBar(
|
|
|
|
// // SnackBar(content: Text(message.message)),
|
|
|
|
// // );
|
|
|
|
// // });
|
|
|
|
// // }
|
|
|
|
// JavascriptChannel _toasterJavascriptChannel(BuildContext context) {
|
|
|
|
// return JavascriptChannel(
|
|
|
|
// name: 'Toaster',
|
|
|
|
// onMessageReceived: (JavascriptMessage message) {
|
|
|
|
// Scaffold.of(context).showSnackBar(
|
|
|
|
// SnackBar(content: Text(message.message)),
|
|
|
|
// );
|
|
|
|
// });
|
|
|
|
// }
|
|
|
|
// } |
|
|
|
|
|
|
|
// // class WebPage extends StatelessWidget {
|
|
|
|
// // final String title;
|
|
|
|
// // final String url;
|
|
|
|
// // final bool need;
|
|
|
|
|
|
|
|
// // const WebPage({Key key, this.title, this.url, this.need = false})
|
|
|
|
// // : super(key: key);
|
|
|
|
// // @override
|
|
|
|
// // Widget build(BuildContext context) {
|
|
|
|
// // final Completer<WebViewController> _controller =
|
|
|
|
// // Completer<WebViewController>();
|
|
|
|
// // return Scaffold(
|
|
|
|
// // appBar: SystemAppBar(title, onTap: () {
|
|
|
|
// // if (need) {
|
|
|
|
// // Utils.changeStatusColor(color: MyColours.color_199095);
|
|
|
|
// // }
|
|
|
|
// // Navigator.of(context).pop();
|
|
|
|
// // }),
|
|
|
|
// // body: WillPopScope(
|
|
|
|
// // onWillPop: () async {
|
|
|
|
// // if (need) {
|
|
|
|
// // Utils.changeStatusColor(color: MyColours.color_199095);
|
|
|
|
// // }
|
|
|
|
// // return true;
|
|
|
|
// // },
|
|
|
|
// // child: WebView(
|
|
|
|
// // initialUrl: "http://colorcun.com/privacy1.html",
|
|
|
|
// // javascriptMode: JavascriptMode.unrestricted,
|
|
|
|
// // onWebViewCreated: (WebViewController webViewController) {
|
|
|
|
// // // Loading.showLoading(context);
|
|
|
|
// // _controller.complete(webViewController);
|
|
|
|
// // },
|
|
|
|
// // javascriptChannels: <JavascriptChannel>[
|
|
|
|
// // _toasterJavascriptChannel(context),
|
|
|
|
// // ].toSet(),
|
|
|
|
// // navigationDelegate: (NavigationRequest request) {
|
|
|
|
// // if (request.url.startsWith('https://www.youtube.com/')) {
|
|
|
|
// // return NavigationDecision.prevent;
|
|
|
|
// // }
|
|
|
|
// // return NavigationDecision.navigate;
|
|
|
|
// // },
|
|
|
|
// // onPageStarted: (String url) {},
|
|
|
|
// // onPageFinished: (String url) {
|
|
|
|
// // // Loading.hideLoading(context);
|
|
|
|
// // },
|
|
|
|
// // gestureNavigationEnabled: true,
|
|
|
|
// // )),
|
|
|
|
// // );
|
|
|
|
// // }
|
|
|
|
|
|
|
|
// // JavascriptChannel _toasterJavascriptChannel(BuildContext context) {
|
|
|
|
// // return JavascriptChannel(
|
|
|
|
// // name: 'Toaster',
|
|
|
|
// // onMessageReceived: (JavascriptMessage message) {
|
|
|
|
// // Scaffold.of(context).showSnackBar(
|
|
|
|
// // SnackBar(content: Text(message.message)),
|
|
|
|
// // );
|
|
|
|
// // });
|
|
|
|
// // }
|
|
|
|
// // } |
...
|
...
|
|