1use crossbow_android::{error::*, jni::JavaVM, plugin::*};
2use std::sync::Arc;
3
4pub struct PlayBillingPlugin {
5 singleton: Arc<JniSingleton>,
6 vm: Arc<JavaVM>,
7}
8
9impl CrossbowPlugin for PlayBillingPlugin {
10 fn from_java_vm(vm: Arc<JavaVM>) -> Result<Self>
11 where
12 Self: Sized,
13 {
14 let singleton = get_jni_singleton(Self::get_plugin_name()).ok_or_else(|| {
15 AndroidError::SingletonNotRegistered(Self::get_plugin_name().to_owned())
16 })?;
17 Ok(Self { singleton, vm })
18 }
19
20 fn get_plugin_name() -> &'static str {
21 "CrossbowPlayBilling"
22 }
23
24 fn get_receiver(&self) -> &Receiver<Signal> {
25 self.singleton.get_receiver()
26 }
27}
28
29impl PlayBillingPlugin {
30 pub fn start_connection(&self) -> Result<()> {
31 let jnienv = self.vm.attach_current_thread_as_daemon()?;
32 self.singleton
33 .call_method(&jnienv, "startConnection", &[])?;
34 jnienv.exception_check()?;
35 Ok(())
36 }
37
38 pub fn end_connection(&self) -> Result<()> {
39 let jnienv = self.vm.attach_current_thread_as_daemon()?;
40 self.singleton.call_method(&jnienv, "endConnection", &[])?;
41 jnienv.exception_check()?;
42 Ok(())
43 }
44
45 pub fn is_ready(&self) -> Result<bool> {
46 let jnienv = self.vm.attach_current_thread_as_daemon()?;
47 let res = self.singleton.call_method(&jnienv, "isReady", &[])?;
48 jnienv.exception_check()?;
49 Ok(res.z()?)
50 }
51
52 pub fn get_connection_state(&self) -> Result<i32> {
53 let jnienv = self.vm.attach_current_thread_as_daemon()?;
54 let res = self
55 .singleton
56 .call_method(&jnienv, "getConnectionState", &[])?;
57 jnienv.exception_check()?;
58 Ok(res.i()?)
59 }
60
61 pub fn query_purchases<S>(&self, purchase_type: S) -> Result<()>
62 where
63 S: AsRef<str>,
64 {
65 let jnienv = self.vm.attach_current_thread_as_daemon()?;
66 let purchase_type_str = jnienv.new_string(purchase_type)?;
67 self.singleton
68 .call_method(&jnienv, "queryPurchases", &[purchase_type_str.into()])?;
69 jnienv.exception_check()?;
70 Ok(())
71 }
72
73 pub fn query_sku_details<S>(&self, sku_list: &[S], sku_type: S) -> Result<()>
74 where
75 S: AsRef<str>,
76 {
77 let jnienv = self.vm.attach_current_thread_as_daemon()?;
78 let empty_str = jnienv.new_string("")?;
79 let string_array =
80 jnienv.new_object_array(sku_list.len() as i32, "java/lang/String", empty_str)?;
81 for (index, id) in sku_list.iter().enumerate() {
82 let id_str = jnienv.new_string(id)?;
83 jnienv.set_object_array_element(string_array, index as i32, id_str)?;
84 }
85 let sku_type_str = jnienv.new_string(sku_type)?;
86 self.singleton.call_method(
87 &jnienv,
88 "querySkuDetails",
89 &[string_array.into(), sku_type_str.into()],
90 )?;
91 jnienv.exception_check()?;
92 Ok(())
93 }
94
95 pub fn acknowledge_purchase<S>(&self, purchase_token: S) -> Result<()>
96 where
97 S: AsRef<str>,
98 {
99 let jnienv = self.vm.attach_current_thread_as_daemon()?;
100 let purchase_token_str = jnienv.new_string(purchase_token)?;
101 self.singleton
102 .call_method(&jnienv, "acknowledgePurchase", &[purchase_token_str.into()])?;
103 jnienv.exception_check()?;
104 Ok(())
105 }
106
107 pub fn consume_purchase<S>(&self, purchase_token: S) -> Result<()>
108 where
109 S: AsRef<str>,
110 {
111 let jnienv = self.vm.attach_current_thread_as_daemon()?;
112 let purchase_token_str = jnienv.new_string(purchase_token)?;
113 self.singleton
114 .call_method(&jnienv, "consumePurchase", &[purchase_token_str.into()])?;
115 jnienv.exception_check()?;
116 Ok(())
117 }
118
119 pub fn confirm_price_change<S>(&self, sku: S) -> Result<JniRustType>
120 where
121 S: AsRef<str>,
122 {
123 let jnienv = self.vm.attach_current_thread_as_daemon()?;
124 let sku_str = jnienv.new_string(sku)?;
125 let res = self
126 .singleton
127 .call_method(&jnienv, "confirmPriceChange", &[sku_str.into()])?;
128 jnienv.exception_check()?;
129 Ok(JniRustType::from_jobject(&jnienv, res.l()?)?)
130 }
131
132 pub fn purchase<S>(&self, sku: S) -> Result<JniRustType>
133 where
134 S: AsRef<str>,
135 {
136 let jnienv = self.vm.attach_current_thread_as_daemon()?;
137 let sku_str = jnienv.new_string(sku)?;
138 let res = self
139 .singleton
140 .call_method(&jnienv, "purchase", &[sku_str.into()])?;
141 jnienv.exception_check()?;
142 Ok(JniRustType::from_jobject(&jnienv, res.l()?)?)
143 }
144
145 pub fn update_subscription<S>(
146 &self,
147 old_token: S,
148 sku: S,
149 proration_mode: i32,
150 ) -> Result<JniRustType>
151 where
152 S: AsRef<str>,
153 {
154 let jnienv = self.vm.attach_current_thread_as_daemon()?;
155 let old_token_str = jnienv.new_string(old_token)?;
156 let sku_str = jnienv.new_string(sku)?;
157 let res = self.singleton.call_method(
158 &jnienv,
159 "updateSubscription",
160 &[old_token_str.into(), sku_str.into(), proration_mode.into()],
161 )?;
162 jnienv.exception_check()?;
163 Ok(JniRustType::from_jobject(&jnienv, res.l()?)?)
164 }
165
166 pub fn set_obfuscated_account_id<S>(&self, account_id: S) -> Result<()>
167 where
168 S: AsRef<str>,
169 {
170 let jnienv = self.vm.attach_current_thread_as_daemon()?;
171 let account_id_str = jnienv.new_string(account_id)?;
172 self.singleton
173 .call_method(&jnienv, "setObfuscatedAccountId", &[account_id_str.into()])?;
174 jnienv.exception_check()?;
175 Ok(())
176 }
177
178 pub fn set_obfuscated_profile_id<S>(&self, profile_id: S) -> Result<()>
179 where
180 S: AsRef<str>,
181 {
182 let jnienv = self.vm.attach_current_thread_as_daemon()?;
183 let profile_id_str = jnienv.new_string(profile_id)?;
184 self.singleton
185 .call_method(&jnienv, "setObfuscatedProfileId", &[profile_id_str.into()])?;
186 jnienv.exception_check()?;
187 Ok(())
188 }
189}