2021年10月28日 星期四

Android Studio 聊天室下拉加載歷史訊息

 一開始需求是每次有新的訊息要在最下面

然後就很突然的,將 RecyclerView 設置為倒序

LinearLayoutManager linearLayoutManager = new LinearLayoutManager(getActivity(), 
LinearLayoutManager.VERTICAL, true); // 列表翻轉
linearLayoutManager.setStackFromEnd(true); // 列表在底部開始展示, 反轉後由上面開始展示
rvMessagesChat.setLayoutManager(linearLayoutManager);
rvMessagesChat.setHasFixedSize(true);
rvMessagesChat.setAnimation(null);


但是在加載新資料的時候,舊資料會被擠下去

int beforeSize = mList.size();
int afterSize = model.vModelRecord.size();
profileMessagesChatAdapter.addList(model.vModelRecord);
profileMessagesChatAdapter.notifyItemRangeInserted(beforeSize, afterSize);


所以需要新增這行,這樣就不會有擠壓的動畫

rvMessagesChat.setAnimation(null);

2021年9月15日 星期三

Samba - Ubunta 和 Windows 之間的橋樑

 1. 先在 Ubunta 安裝 Samba 

sudo apt-get install samba


2. 設定 Samba 工作群組,要和Windows 相同

 vim /etc/samba/smb.conf

3. 設定要分享的資料夾,格式如下

[ShareName] 分享資料夾名稱

path = /var/usr/    分享資料夾路徑

read only = no    是否唯讀

create mask = 777     檔案遮罩

directory mask = 777    資料夾遮至

writable = yes    是否可寫入

4. 將 Samba 加進防火牆白名單

sudo ufw status verbose → 確認防火牆狀態

sudo ufw allow samba → 將 Samba 加進防火牆白名單

sudo ufw reload → 重啟防火牆

5. 重啟 Samba 讓他吃修改後的設定檔

sudo service smbd restart

6. 完成

2021年8月25日 星期三

AppsFlyer 廣告追蹤

 Apps Flyer 是一個多平台整合的廣告追蹤網站


Apps Flyer 相關

Android SDK 集成

iOS SDK 集成


Facebook 相關

Facebook 廣告配置指南

Facebook SDK

Facebook Dashboard



iOS 14.5 官方將廣告追蹤透明化

必須經過用戶同意方可進行追蹤

再設定 Facebook 追蹤時,

必須將事件管理工具 > 資料來源 > 設定 > 為 SKAdNetwork 設定應用程式事件 開啟

並編輯事件,才可以收到來自 iOS 的資料


Swift 

AppEvents.logEvent(AppEvents.Name.completedRegistration)


2021年8月24日 星期二

iOS 訂閱性商品是否可取得續訂資料

 可以透過 iOS 驗證收據中,取得最新的訂閱資料中查看 訂閱截止日

作法:

  1. iOS成功購買產品後,呼叫 Fetch the Receipt Data(備註1) 去取 Apple Store 收據
  2. 因為 Apple Store 收據 本身有經過加密,也因為安全性的問題,需由後端去與 Apple 伺服器溝通
  3. 承第二點可透過 POST MAN 進行測試實際用法參考 (備註2)
  4. 將第一點取得的加密字串傳給後端API
  5. 由後端去和APPLE伺服器溝通,取得解碼後的收據詳細資料(JSONObject)
  6. 儲存至DB

備註:
1. iOS 原生

if let appStoreReceiptURL = Bundle.main.appStoreReceiptURL, FileManager,default.fileExists(atPath: appStoreReceiptURL.path){
  do{
    let receiptData = try Data(contentsOf: appStoreReceiptURL, options: .alwaysMapped)
    let receiptString = receiptData.base64EncodedString(options: [])

    // 呼叫 API

  } catch {
    print("Couldn't read receipt data with error: " + error.localizedDescription)
  }
}

2. 驗證收據是否正確
正式驗證 API 網址
https://buy.itunes.apple.com/verifyReceipt
沙箱驗證 API 網址
https://sandbox.itunes.apple.com/verifyReceipt
資料來源:https://www.jianshu.com/p/d0ac8cec794b

3. 驗證資料的格式,必須自建為 JSONObject
https://developer.apple.com/documentation/appstorereceipts/requestbody

4.回傳的狀態碼
https://developer.apple.com/documentation/appstorereceipts/status

5. 後端

function setReceiptIos($arr){
		if(!isset($arr)){
			return 'ESY9001';
		} else if(empty($arr["receipt_data"])){
			return 'EPT0015';
		} else if(empty($arr["uid"])){
			return 'EPT0007';
		} else if(empty($arr["iap_type"])){
			return 'EPT0016';
		} else if(empty($arr["ptid"])){
			return 'EPT0004';
		}
		
		// get mydb
		$mydb = &$this->mydb;
		
		date_default_timezone_set("Asia/Taipei");
		
		function acurl($receipt_data, $sandbox=0){
			
			// SandBox , Official
			$secret = apple_connect_serect_key;
			$POSTFIELDS = array("receipt-data" => $receipt_data, 'password'=>$secret, "");
			$POSTFIELDS = json_encode($POSTFIELDS);
	 
			//正式購買地址 沙盒購買地址
			$url_buy     = "https://buy.itunes.apple.com/verifyReceipt";
			$url_sandbox = "https://sandbox.itunes.apple.com/verifyReceipt";
			$url = $sandbox ? $url_sandbox : $url_buy;
	 
			//簡單的curl
			$ch = curl_init($url);
			curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
			curl_setopt($ch, CURLOPT_POST, 1);
			curl_setopt($ch, CURLOPT_POSTFIELDS, $POSTFIELDS);
			$result = curl_exec($ch);
			curl_close($ch);
			return $result;
		}
		
		// 請求驗證
	    $html = acurl($arr["receipt_data"]);
		$data = json_decode($html, true);
		// 如果是沙盒資料 則驗證沙盒模式
	    if($data['status']=='21007'){
	        // 請求驗證
	        $html = acurl($arr["receipt_data"], 1);
	        $data = json_decode($html,true);
	        $data['sandbox'] = '1';
	    }
		
		 // 判斷是否購買成功
	    if(intval($data['status'])===0){
	        $result=array(
	            'status'  => $data['status'],
	            'message' => '購買成功'
	            );
	    }else{
			
	        $result=array(
	            'status'  => $data['status'],
	            'message' => '購買失敗'
	            );
				
			return $result;
	    }
		
		$latestPosition = 0;
		
		
		
		$purchase_date_ms = "0";
		
		$receipt = array();
		// latest_receipt_info 只會回傳可續訂的收據, 其他收據要在 IN_APP 裡面找
		if ($arr["iap_type"] == "subscription") {
			$receipt = $data["latest_receipt_info"];
		} 
		else {
			$receiptArr = $data["receipt"]["in_app"];
			$receiptArrLength = count($receiptArr);
			if ( $receiptArrLength > 0){
				for ($i=0; $i<$receiptArrLength; $i++){
					if ( (double) $receiptArr[$i]["purchase_date_ms"] > (double) $purchase_date_ms ){
						$purchase_date_ms = (double) $receiptArr[$i]["purchase_date_ms"]; 
						$latestPosition = $i;
					}
				}
			}
			
			$receipt = $receiptArr;
		}
		
		
		$transaction_id = 'NULL';
		if (isset($receipt[$latestPosition]["transaction_id"]) ){
			$transaction_id = "'".$receipt[$latestPosition]["transaction_id"]."'"; 
		}
		
		$product_id = 'NULL';
		if (isset($receipt[$latestPosition]["product_id"]) ){
			$product_id = "'".$receipt[$latestPosition]["product_id"]."'"; 
		}
		
		
		$purchase_date = 'NULL';
		if (isset($receipt[$latestPosition]["purchase_date"]) ){
			$purchase_date = "'".date("Y-m-d H:i:s",strtotime($receipt[$latestPosition]["purchase_date"]))."'"; 
		}
		
		$expires_date = 'NULL';
		if (isset($receipt[$latestPosition]["expires_date"]) ){
			$expires_date = "'".date("Y-m-d H:i:s",strtotime($receipt[$latestPosition]["expires_date"]))."'"; 
		}
		
		$cancellation_date = 'NULL';
		if (isset($receipt[$latestPosition]["cancellation_date"]) ){
			$cancellation_date = "'".date("Y-m-d H:i:s",strtotime($receipt[$latestPosition]["cancellation_date"]))."'"; 
		}
		
		$cancellation_reason = 'NULL';
		if (isset($receipt[$latestPosition]["cancellation_reason"]) ){
			$cancellation_reason = "'".$receipt[$latestPosition]["cancellation_reason"]."'"; 
		}
		
		$original_purchase_date = 'NULL';
		if (isset($receipt[$latestPosition]["original_purchase_date"]) ){
			$original_purchase_date = "'".date("Y-m-d H:i:s",strtotime($receipt[$latestPosition]["original_purchase_date"]))."'"; 
		}
		
		{ $query = "
		INSERT INTO `".PREFIX_TABLE_0."_".DB_NAME_1."`.`".PREFIX_TABLE_0."_history_user_ios_receipt` (
			`prefixid` ,
			`huirid` ,
			`uid` ,
			`transaction_id` ,
			`product_id` ,
			`purchase_date` ,
			`expires_date` ,
			`cancellation_date` ,
			`cancellation_reason` ,
			`original_purchase_date` ,
			`encode_string` ,
			`ptid` ,
			`seq` ,
			`switch` ,
			`insertt`,
			`modifyt`
		) VALUES (
			'".PREFIX_INDEX_0."',
			NULL,
			'".$arr["uid"]."',
			{$transaction_id},
			{$product_id},
			{$purchase_date},
			{$expires_date},
			{$cancellation_date},
			{$cancellation_reason},
			{$original_purchase_date},
			'".$arr["receipt_data"]."',
			'".$arr["ptid"]."',
			'10',
			'Y',
			NOW(),
			NOW()
		)";
		}
		
		
		// echo $query ; exit;
		if(!$mydb->query($query)) return 'ESY9002';
			
		return $result;
	}

Android Studio IDE 錯誤

 :app:compile xxxxx JavaWithJavac FAILED An exception has occurred in the compiler (1.8.0_312). Please file a bug against the Java compiler ...