From 59881254166638e662cd1a3257f22de6ae3d5fad Mon Sep 17 00:00:00 2001 From: birdswu Date: Wed, 21 May 2025 15:43:34 +0800 Subject: [PATCH] =?UTF-8?q?NetworkKit=20CameraKit=20ImageKit=E6=A0=87?= =?UTF-8?q?=E8=AF=86=E6=B7=BB=E5=8A=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/main/ets/pages/AvoidDistortion.ets | 96 ++++++++++ .../entry/src/main/ets/pages/CloseSession.txt | 24 +++ .../entry/src/main/ets/pages/GetSurface.ets | 71 ++++++++ .../entry/src/main/ets/pages/GetSurface4.ets | 71 ++++++++ .../entry/src/main/ets/pages/GetSurface5.ets | 44 +++++ .../entry/src/main/ets/pages/SetFlash.ets | 31 ++++ .../src/main/ets/pages/CompressedImage.ets | 172 ++++++++++++++++++ .../main/ets/pages/CreatePixelMapError.ets | 27 +++ .../main/ets/pages/CreatePixelMapError2.ets | 39 ++++ .../entry/src/main/ets/pages/CropCommon.ets | 73 ++++++++ .../src/main/ets/pages/DisplayAspectRatio.ets | 42 +++++ .../entry/src/main/ets/pages/GetPhoto.ets | 73 ++++++++ .../entry/src/main/ets/pages/PixelFormat.ets | 67 +++++++ .../src/main/resources/base/media/example.png | Bin 0 -> 20093 bytes NetworkKit/entry/oh-package.json5 | 4 +- .../src/main/ets/pages/AddressesByName.ets | 28 +++ .../entry/src/main/ets/pages/AxiosPost.ets | 40 ++++ .../entry/src/main/ets/pages/ContentType.ets | 34 ++++ .../entry/src/main/ets/pages/ExtraData.txt | 11 ++ .../entry/src/main/ets/pages/GetAxiosUTF.ets | 54 ++++++ .../entry/src/main/ets/pages/GetDns.ets | 52 ++++++ .../entry/src/main/ets/pages/GetNetErr.ets | 52 ++++++ .../entry/src/main/ets/pages/HasNet.ets | 108 +++++++++++ .../entry/src/main/ets/pages/NetModule.json5 | 23 +++ .../entry/src/main/ets/pages/OnConnection.ets | 54 ++++++ .../entry/src/main/ets/pages/OnNetVpn.ets | 50 +++++ .../entry/src/main/ets/pages/RequestJson.ets | 37 ++++ .../entry/src/main/ets/pages/SetCookie.ets | 79 ++++++++ .../entry/src/main/ets/pages/SetForm.ets | 59 ++++++ .../src/main/ets/pages/SetLongConnect.ets | 33 ++++ .../src/main/ets/pages/SetNetSpecifier.ets | 115 ++++++++++++ .../entry/src/main/ets/pages/SetNetType.ets | 35 ++++ .../src/main/ets/pages/SetSocketServer.ets | 66 +++++++ .../entry/src/main/ets/pages/SetTcpSocket.ets | 67 +++++++ 34 files changed, 1830 insertions(+), 1 deletion(-) create mode 100644 CameraKit/entry/src/main/ets/pages/AvoidDistortion.ets create mode 100644 CameraKit/entry/src/main/ets/pages/CloseSession.txt create mode 100644 CameraKit/entry/src/main/ets/pages/GetSurface.ets create mode 100644 CameraKit/entry/src/main/ets/pages/GetSurface4.ets create mode 100644 CameraKit/entry/src/main/ets/pages/GetSurface5.ets create mode 100644 CameraKit/entry/src/main/ets/pages/SetFlash.ets create mode 100644 ImageKit/entry/src/main/ets/pages/CompressedImage.ets create mode 100644 ImageKit/entry/src/main/ets/pages/CreatePixelMapError.ets create mode 100644 ImageKit/entry/src/main/ets/pages/CreatePixelMapError2.ets create mode 100644 ImageKit/entry/src/main/ets/pages/CropCommon.ets create mode 100644 ImageKit/entry/src/main/ets/pages/DisplayAspectRatio.ets create mode 100644 ImageKit/entry/src/main/ets/pages/GetPhoto.ets create mode 100644 ImageKit/entry/src/main/ets/pages/PixelFormat.ets create mode 100644 ImageKit/entry/src/main/resources/base/media/example.png create mode 100644 NetworkKit/entry/src/main/ets/pages/AddressesByName.ets create mode 100644 NetworkKit/entry/src/main/ets/pages/AxiosPost.ets create mode 100644 NetworkKit/entry/src/main/ets/pages/ContentType.ets create mode 100644 NetworkKit/entry/src/main/ets/pages/ExtraData.txt create mode 100644 NetworkKit/entry/src/main/ets/pages/GetAxiosUTF.ets create mode 100644 NetworkKit/entry/src/main/ets/pages/GetDns.ets create mode 100644 NetworkKit/entry/src/main/ets/pages/GetNetErr.ets create mode 100644 NetworkKit/entry/src/main/ets/pages/HasNet.ets create mode 100644 NetworkKit/entry/src/main/ets/pages/NetModule.json5 create mode 100644 NetworkKit/entry/src/main/ets/pages/OnConnection.ets create mode 100644 NetworkKit/entry/src/main/ets/pages/OnNetVpn.ets create mode 100644 NetworkKit/entry/src/main/ets/pages/RequestJson.ets create mode 100644 NetworkKit/entry/src/main/ets/pages/SetCookie.ets create mode 100644 NetworkKit/entry/src/main/ets/pages/SetForm.ets create mode 100644 NetworkKit/entry/src/main/ets/pages/SetLongConnect.ets create mode 100644 NetworkKit/entry/src/main/ets/pages/SetNetSpecifier.ets create mode 100644 NetworkKit/entry/src/main/ets/pages/SetNetType.ets create mode 100644 NetworkKit/entry/src/main/ets/pages/SetSocketServer.ets create mode 100644 NetworkKit/entry/src/main/ets/pages/SetTcpSocket.ets diff --git a/CameraKit/entry/src/main/ets/pages/AvoidDistortion.ets b/CameraKit/entry/src/main/ets/pages/AvoidDistortion.ets new file mode 100644 index 0000000..1cffe04 --- /dev/null +++ b/CameraKit/entry/src/main/ets/pages/AvoidDistortion.ets @@ -0,0 +1,96 @@ +/* +* Copyright (c) 2024 Huawei Device Co., Ltd. +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +/* +* FAQ:如何避免预览流产生畸变 +*/ +import { camera } from '@kit.CameraKit'; +import { BusinessError } from '@kit.BasicServicesKit'; +import { image } from '@kit.ImageKit'; + +const TAG = 'Tag'; + +class AvoidDistortion { + xComponentWidth: number = 0; + xComponentHeight: number = 0; + mXComponentController: XComponentController = new XComponentController(); + + get() { + let previewOutput: camera.PreviewOutput|undefined; + let surfaceId: string ='surface'; + let context = null; + let cameraManager: camera.CameraManager = camera.getCameraManager(context); + let cameraArray: camera.CameraDevice[] = cameraManager.getSupportedCameras(); + let cameraPosition = 1; + let cameraOutputCap: camera.CameraOutputCapability = + cameraManager.getSupportedOutputCapability(cameraArray[cameraPosition],camera.SceneMode.NORMAL_PHOTO); + + // [Start AvoidDistortion] + //The aspect ratio of the preview stream and the video output stream resolution should be consistent + let previewProfilesArray: Array = cameraOutputCap.previewProfiles; + + let position: number = 0; + if (previewProfilesArray != null) { + previewProfilesArray.forEach((value: camera.Profile,index: number) => { + // View supported preview sizes + console.info(TAG, + `支持的预览尺寸: [${value.size.width},${value.size.height},${value.size.width / value.size.height}]`); + if (value.size.width === 2592 && value.size.height === 1200) { + position = index; + } + }) + } else { + console.error(TAG,"createOutput photoProfilesArray == null || undefined"); + } + + let photoProfilesArray: Array = cameraOutputCap.photoProfiles; + if (!photoProfilesArray) { + console.error(TAG,"createOutput photoProfilesArray == null || undefined"); + } + + this.xComponentWidth = previewProfilesArray[position].size.width; + this.xComponentHeight = previewProfilesArray[position].size.height; + + this.mXComponentController.setXComponentSurfaceSize({ + surfaceWidth: this.xComponentWidth, + surfaceHeight: this.xComponentHeight + }); + // Create a preview output stream, where the parameter surfaceId refers to the XCompoonent component mentioned earlier, + // and the preview stream is the surface provided by the XCompoonent component + try { + previewOutput = cameraManager.createPreviewOutput(previewProfilesArray[position],surfaceId); + } catch (error) { + let err = error as BusinessError; + console.error(TAG,`Failed to create the PreviewOutput instance. error code: ${err.code}`); + } + if (previewOutput === undefined) { + return; + } + + // Monitor preview output error message + previewOutput.on('error',(error: BusinessError) => { + console.error(TAG,`Preview output error code: ${error.code}`); + }); + + // Create an ImageReceiver object and set photo parameters: + // The resolution size is set based on the current device's supported photo resolution size obtained from the previous photoProfilesArray + let size: image.Size = { + height: 1200, + width: 2592 + } + let imageReceiver: image.ImageReceiver = image.createImageReceiver(size,4,8); + // [End AvoidDistortion] + } +} \ No newline at end of file diff --git a/CameraKit/entry/src/main/ets/pages/CloseSession.txt b/CameraKit/entry/src/main/ets/pages/CloseSession.txt new file mode 100644 index 0000000..5ca9942 --- /dev/null +++ b/CameraKit/entry/src/main/ets/pages/CloseSession.txt @@ -0,0 +1,24 @@ + +/* +* FAQ:如何实现相机关闭 +*/ + +// [Start CloseSession] +// Stop the current session + photoSession.stop(); + +// Release camera input stream + cameraInput.close(); + +// Release preview output stream + previewOutput.release(); + +// Release the photo output stream + photoOutput.release(); + +// Release session + photoSession.release(); + +// Session left blank + photoSession = undefined; +// [End CloseSession] \ No newline at end of file diff --git a/CameraKit/entry/src/main/ets/pages/GetSurface.ets b/CameraKit/entry/src/main/ets/pages/GetSurface.ets new file mode 100644 index 0000000..a343a9b --- /dev/null +++ b/CameraKit/entry/src/main/ets/pages/GetSurface.ets @@ -0,0 +1,71 @@ +/* +* Copyright (c) 2024 Huawei Device Co., Ltd. +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +/* +* FAQ:如何读取相机的预览图 +*/ + +// [Start GetSurface] +import { image } from '@kit.ImageKit'; +// [End GetSurface] + +// [Start GetSurface2] +async function getImageReceiverSurfaceId(): Promise { + let receiver: image.ImageReceiver = image.createImageReceiver({ width: 640,height: 480 },4,8); + console.info('before ImageReceiver check'); + let ImageReceiverSurfaceId: string | undefined = undefined; + if (receiver !== undefined) { + console.info('ImageReceiver is ok'); + let ImageReceiverSurfaceId: string = await receiver.getReceivingSurfaceId(); + console.info(`ImageReceived id: ${ImageReceiverSurfaceId}`); + } else { + console.error('ImageReceiver is not ok'); + } + return ImageReceiverSurfaceId; +} + +// [End GetSurface2] + +// [Start GetSurface3] +//xxx.ets +// 创建XComponentController +@Entry +@Component +struct XComponentPage { + // 创建XComponentController + mXComponentController: XComponentController = new XComponentController; + + build() { + Flex() { + // Create XCompoonent + XComponent({ + id: '', + type: 'surface', + libraryname: '', + controller: this.mXComponentController + }) + .onLoad(() => { + // Set the width and height of the Surface (1920 * 1080), and set the preview size based on the preview resolution size supported + // by the current device obtained from previewProfilesArray earlier + this.mXComponentController.setXComponentSurfaceRect({surfaceWidth:1920,surfaceHeight:1080}); + // Get Surface ID + let surfaceId: string = this.mXComponentController.getXComponentSurfaceId(); + }) + .width('1920px') + .height('1080px') + } + } +} +// [End GetSurface3] \ No newline at end of file diff --git a/CameraKit/entry/src/main/ets/pages/GetSurface4.ets b/CameraKit/entry/src/main/ets/pages/GetSurface4.ets new file mode 100644 index 0000000..e5a419a --- /dev/null +++ b/CameraKit/entry/src/main/ets/pages/GetSurface4.ets @@ -0,0 +1,71 @@ +/* +* Copyright (c) 2024 Huawei Device Co., Ltd. +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +/* +* FAQ:如何读取相机的预览图 +*/ + +// [Start GetSurface4] +import { camera } from '@kit.CameraKit'; +import { image } from '@kit.ImageKit'; + +async function createDualChannelPreview(cameraManager: camera.CameraManager, XComponentSurfaceId: string, receiver: image.ImageReceiver): Promise { + let camerasDevices: Array = cameraManager.getSupportedCameras(); // 获取支持的相机设备对象 + + // Get profile object + let profiles: camera.CameraOutputCapability = cameraManager.getSupportedOutputCapability(camerasDevices[0], camera.SceneMode.NORMAL_PHOTO); // 获取对应相机设备profiles + let previewProfiles: Array = profiles.previewProfiles; + + // Preview Stream 1 + let previewProfilesObj: camera.Profile = previewProfiles[0]; + + // Preview Stream 2 + let previewProfilesObj2: camera.Profile = previewProfiles[0]; + + // Create preview stream 1 output object + let previewOutput: camera.PreviewOutput = cameraManager.createPreviewOutput(previewProfilesObj, XComponentSurfaceId); + + // Create preview stream 2 output object + let imageReceiverSurfaceId: string = await receiver.getReceivingSurfaceId(); + let previewOutput2: camera.PreviewOutput = cameraManager.createPreviewOutput(previewProfilesObj2, imageReceiverSurfaceId); + + // Create cameraInput object + let cameraInput: camera.CameraInput = cameraManager.createCameraInput(camerasDevices[0]); + + // Turn on the camera + await cameraInput.open(); + + // session flow + let captureSession = cameraManager.createSession(camera.SceneMode.NORMAL_PHOTO); + + // Start configuring session + captureSession.beginConfig(); + + // Add CameraInput to the conversation + captureSession.addInput(cameraInput); + + // Add preview stream 1 to the session + captureSession.addOutput(previewOutput); + + // Add preview stream 2 to the session + captureSession.addOutput(previewOutput2); + + // Submit configuration information + await captureSession.commitConfig(); + + // session start + await captureSession.start(); +} +// [End GetSurface4] \ No newline at end of file diff --git a/CameraKit/entry/src/main/ets/pages/GetSurface5.ets b/CameraKit/entry/src/main/ets/pages/GetSurface5.ets new file mode 100644 index 0000000..d405f32 --- /dev/null +++ b/CameraKit/entry/src/main/ets/pages/GetSurface5.ets @@ -0,0 +1,44 @@ +/* +* Copyright (c) 2024 Huawei Device Co., Ltd. +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +/* +* FAQ:如何读取相机的预览图 +*/ + +// [Start GetSurface5] +import { BusinessError } from '@kit.BasicServicesKit'; +import image from '@ohos.multimedia.image'; + +function onImageArrival(receiver: image.ImageReceiver): void { + receiver.on('imageArrival', () => { + receiver.readNextImage((err: BusinessError, nextImage: image.Image) => { + if (err || nextImage === undefined) { + return; + } + // The current ImageReceiver does not support obtaining YUV data. You can use the JPEG data channel to receive YUV data, but using this method carries risks + nextImage.getComponent(image.ComponentType.JPEG, (err: BusinessError, imgComponent: image.Component) => { + if (err || imgComponent === undefined) { + return; + } + if (imgComponent.byteBuffer as ArrayBuffer) { + // Handle according to business demands + } else { + return; + } + }) + }) + }) +} +// [End GetSurface5] \ No newline at end of file diff --git a/CameraKit/entry/src/main/ets/pages/SetFlash.ets b/CameraKit/entry/src/main/ets/pages/SetFlash.ets new file mode 100644 index 0000000..947c9ec --- /dev/null +++ b/CameraKit/entry/src/main/ets/pages/SetFlash.ets @@ -0,0 +1,31 @@ +/* +* Copyright (c) 2024 Huawei Device Co., Ltd. +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ +import { camera } from '@kit.CameraKit'; +const TAG = 'Tag'; +/* +* FAQ:如何开关闪光灯 +*/ +class FlashSet{ + // [Start setFlash] + setFlash(captureSession: camera.PhotoSession,flashMode: camera.FlashMode) { + if (captureSession != null) { + let focusModeStatus: boolean = captureSession?.isFlashModeSupported(flashMode); + if (focusModeStatus) { + captureSession.setFlashMode(flashMode); + } + } + } + // [End setFlash] +} diff --git a/ImageKit/entry/src/main/ets/pages/CompressedImage.ets b/ImageKit/entry/src/main/ets/pages/CompressedImage.ets new file mode 100644 index 0000000..96686c1 --- /dev/null +++ b/ImageKit/entry/src/main/ets/pages/CompressedImage.ets @@ -0,0 +1,172 @@ +/* +* Copyright (c) 2024 Huawei Device Co., Ltd. +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +/* +* FAQ:如何将图片压缩到指定大小以下 +*/ + +// [Start CompressedImage] +import { image } from '@kit.ImageKit'; +import { resourceManager } from '@kit.LocalizationKit'; +import { fileUri } from '@kit.CoreFileKit'; +import { BusinessError } from '@kit.BasicServicesKit'; +import { fileIo} from '@kit.CoreFileKit'; + +class CompressedImageInfo { + imageUri: string = ""; // URI of compressed image storage location + imageByteLength: number = 0; // Compressed image byte length +} + +/** + * Image compression, saving + * @param sourcePixelMap:PixelMap object of the original image to be compressed + * @param maxCompressedImageSize:Specify the compression target size for the image, in kb + * @returns compressedImageInfo:Return the final compressed image information + */ +async function compressedImage(sourcePixelMap: image.PixelMap, maxCompressedImageSize: number): Promise { + // Create an ImagePacker object for image encoding + const imagePackerApi = image.createImagePacker(); + const IMAGE_QUALITY = 80; + const packOpts: image.PackingOption = { format: "image/jpeg", quality: IMAGE_QUALITY }; + // Encode through PixelMap. Compressed ImageData is the image file stream obtained by packaging. + let compressedImageData: ArrayBuffer = await imagePackerApi.packing(sourcePixelMap, packOpts); + // 压缩目标图像字节长度 + const maxCompressedImageByte = maxCompressedImageSize * 1024; + // Image compression. First, determine whether the minimum byte size of the image that can be compressed by packing meets the specified image compression size when setting the image quality parameter quality to 80. + // If satisfied, use the packing method to binary search for the quality closest to the specified image compression target size to compress the image. + // If it is not satisfied, use scale to scale the image first, use a while loop to scale the image by 0.4 times each time, + // then use packing (with the image quality parameter quality set to 80) to obtain the compressed image size, + // and finally find the compressed image data with the closest scaling factor to the specified image compression target size. + if (maxCompressedImageByte > compressedImageData.byteLength) { + // Using packing binary compression to obtain image file streams + compressedImageData = await packingImage(compressedImageData, sourcePixelMap, IMAGE_QUALITY, maxCompressedImageByte); + } else { + // Use scale to scale the image first, use a while loop to decrease the scale by 0.4 times each time, then use packing (with the image quality parameter quality set to 80) to obtain the compressed image size, + // and finally find the compressed image data with the closest scaling factor to the specified image compression target size + let imageScale = 1; + const REDUCE_SCALE = 0.4; + // Determine whether the compressed image size is greater than the specified compression target size. If it is, continue to reduce the scaling factor for compression. + while (compressedImageData.byteLength > maxCompressedImageByte) { + if (imageScale > 0) { + // Performance knowledge point: As scale directly modifies the PixelMap data of the image, binary search for scale scaling factor is not applicable. Here, we use a cyclic decrease of 0.4 times to scale the image, + // in order to find and determine the most suitable scaling factor. If you do not have high requirements for image compression quality, + // it is recommended to increase the scaling factor reduceScale each time to reduce loops and improve scale compression performance. + imageScale = imageScale - REDUCE_SCALE; + await sourcePixelMap.scale(imageScale, imageScale); + compressedImageData = await packing(sourcePixelMap, IMAGE_QUALITY); + } else { + // When the imageScale scaling is less than or equal to 0, it is meaningless and the compression ends. We do not consider the case where the image scaling factor is less than reduceScale here. + break; + } + } + } + // Save the image and return the compressed image information. + const compressedImageInfo: CompressedImageInfo = await saveImage(compressedImageData); + return compressedImageInfo; +} + +/** + * Packing compression + * @param sourcePixelMap:PixelMap of the original image to be compressed + * @param imageQuality:Image quality parameters + * @returns data:Return compressed image data + */ +async function packing(sourcePixelMap: image.PixelMap, imageQuality: number): Promise { + const imagePackerApi = image.createImagePacker(); + const packOpts: image.PackingOption = { format: "image/jpeg", quality: imageQuality }; + const data: ArrayBuffer = await imagePackerApi.packing(sourcePixelMap, packOpts); + return data; +} + +/** + * Packing binary method cyclic compression + * @param compressedImageData:Arrays Buffer for image compression + * @param sourcePixelMap:PixelMap of the original image to be compressed + * @param imageQuality:Image quality parameters + * @param maxCompressedImageByte:Compress the byte length of the target image + * @returns compressedImageData:Return the compressed image data after binary packing + */ +async function packingImage(compressedImageData: ArrayBuffer, sourcePixelMap: image.PixelMap, imageQuality: number, maxCompressedImageByte: number): Promise { + // The range of image quality parameters is 80-100. Here, an array for packing binary image quality parameters is created with 5 as the minimum binary unit. + const packingArray: number[] = []; + const DICHOTOMY_ACCURACY = 5; + // Performance knowledge point: If the requirements for image compression quality are not high, it is recommended to increase the minimum binary unit dichotomyAccuracy, + // reduce loops, and improve packing compression performance. + for (let i = 80; i <= 100; i += DICHOTOMY_ACCURACY) { + packingArray.push(i); + } + let left = 0; + let right = packingArray.length - 1; + // Binary compressed image + while (left <= right) { + const mid = Math.floor((left + right) / 2); + imageQuality = packingArray[mid]; + // 根据传入的图片质量参数进行packing压缩,返回压缩后的图片文件流数据。 + compressedImageData = await packing(sourcePixelMap, imageQuality); + // Perform packing compression based on the input image quality parameters and return the compressed image file stream data. + if (compressedImageData.byteLength <= maxCompressedImageByte) { + left = mid + 1; + if (mid === packingArray.length - 1) { + break; + } + // Obtain the compressed image file stream data with the next binary image quality parameter (mid+1) + compressedImageData = await packing(sourcePixelMap, packingArray[mid + 1]); + // Determine whether the size of the image compressed with the next image quality parameter (mid+1) is greater than the specified compression target size of the image. + // If it is greater than, it indicates that the current image quality parameter (mid) compresses the image size closest to the compression target size of the specified image. + // Pass in the current image quality parameter mid to obtain the final compressed data of the target image. + if (compressedImageData.byteLength > maxCompressedImageByte) { + compressedImageData = await packing(sourcePixelMap, packingArray[mid]); + break; + } + } else { + // The target value is not in the right half of the current range. Move the right boundary of the search range to the left to narrow down the search range + // and continue searching for the left half in the next iteration. + right = mid - 1; + } + } + return compressedImageData; +} + +/** + * pictures saving + * @param compressedImageData:Compressed image data + * @returns compressedImageInfo:Return compressed image information + */ +async function saveImage(compressedImageData: ArrayBuffer): Promise { + const context: Context = getContext(); + // Define the compressed image URI to be saved. AfterCompression.jpeg represents the compressed image. + const compressedImageUri: string = context.filesDir + '/' + 'afterCompression.jpeg'; + try { + const res = fileIo.accessSync(compressedImageUri); + if (res) { + // If the image afterCommisiona.jpeg already exists, delete it + fileIo.unlinkSync(compressedImageUri); + } + } catch (err) { + console.error(`AccessSync failed with error message: ${err.message}, error code: ${err.code}`); + } + // Knowledge point: Save images. Obtain the final compressed image data compressed ImageData and save the image. + // Compress image data and write it to a file + const file: fileIo.File = fileIo.openSync(compressedImageUri, fileIo.OpenMode.READ_WRITE | fileIo.OpenMode.CREATE); + fileIo.writeSync(file.fd, compressedImageData); + fileIo.closeSync(file); + // Obtain compressed image information + let compressedImageInfo: CompressedImageInfo = new CompressedImageInfo(); + compressedImageInfo.imageUri = compressedImageUri; + compressedImageInfo.imageByteLength = compressedImageData.byteLength; + return compressedImageInfo; +} +// [End CompressedImage] + diff --git a/ImageKit/entry/src/main/ets/pages/CreatePixelMapError.ets b/ImageKit/entry/src/main/ets/pages/CreatePixelMapError.ets new file mode 100644 index 0000000..49f054a --- /dev/null +++ b/ImageKit/entry/src/main/ets/pages/CreatePixelMapError.ets @@ -0,0 +1,27 @@ +/* +* Copyright (c) 2024 Huawei Device Co., Ltd. +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ +import { image } from '@kit.ImageKit'; +import { fileIo as fs } from '@kit.CoreFileKit'; +/* +* FAQ:调用imageSource.createPixelMap()报错“Create PixelMap error” +*/ +async function set(){ + let uri: string = '' + // [Start CreatePixelMapError1] + const file = fs.openSync(uri, fs.OpenMode.READ_ONLY); + const imageSource = image.createImageSource(file.fd); + const pixelMap = await imageSource.createPixelMap({sampleSize: 2, rotate: 0}); + // [End CreatePixelMapError1] +} diff --git a/ImageKit/entry/src/main/ets/pages/CreatePixelMapError2.ets b/ImageKit/entry/src/main/ets/pages/CreatePixelMapError2.ets new file mode 100644 index 0000000..7775368 --- /dev/null +++ b/ImageKit/entry/src/main/ets/pages/CreatePixelMapError2.ets @@ -0,0 +1,39 @@ +/* +* Copyright (c) 2024 Huawei Device Co., Ltd. +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +/* +* FAQ:调用imageSource.createPixelMap()报错“Create PixelMap error” +*/ + +// [Start CreatePixelMapError2] +import { BusinessError } from '@kit.BasicServicesKit'; +import { image } from '@kit.ImageKit'; + +let decodingOptions: image.DecodingOptions = { + editable: true, + desiredPixelFormat: 3, + desiredSize: { width: 4,height: 6 } +} +const context: Context = getContext(this); +const filePath: string = context.cacheDir + '/test.jpg'; +const imageSource: image.ImageSource = image.createImageSource(filePath); +// Create PixelMap and perform simple rotation and scaling +imageSource.createPixelMap(decodingOptions).then((pixelMap: image.PixelMap) => { + console.log("Succeeded in creating PixelMap") +}).catch((err: BusinessError) => { + console.error("Failed to create PixelMap") +}); +// [End CreatePixelMapError2] + diff --git a/ImageKit/entry/src/main/ets/pages/CropCommon.ets b/ImageKit/entry/src/main/ets/pages/CropCommon.ets new file mode 100644 index 0000000..51424ab --- /dev/null +++ b/ImageKit/entry/src/main/ets/pages/CropCommon.ets @@ -0,0 +1,73 @@ +/* +* Copyright (c) 2024 Huawei Device Co., Ltd. +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +/* +* FAQ:如何对相册图片进行编辑裁剪 +*/ + +// [Start CropCommon] +// Crop 4:3 +class RegionItem { + /** + * width coordinate. + */ + x: number; + + /** + * height coordinate. + */ + y: number; + + constructor(x: number, y: number) { + this.x = x; + this.y = y; + } +} + +export async function cropCommon(pixelMap: PixelMap, cropWidth: number, cropHeight: number, cropPosition: RegionItem) { + pixelMap.crop({ + size: { + width: cropWidth, + height: cropHeight + }, + x: cropPosition.x, + y: cropPosition.y + }); +} + +// Pass in three parameters: image. PixelMap, image width, and image height. After obtaining the cropped image width and height, +// pass the parameters into the cropCommon method +export async function banner(pixelMap: PixelMap, width: number, height: number) { + if (width <= height) { + const cropWidth = width; + const cropHeight = Math.floor(width * 0.75); + const cropPosition = new RegionItem(0, Math.floor((height - cropHeight) / 2)); + cropCommon(pixelMap, cropWidth, cropHeight, cropPosition); + return; + } + if (width * 0.75 >= height) { + const cropWidth = Math.floor(height / 0.75); + const cropHeight = height; + const cropPosition = new RegionItem(Math.floor((width - cropWidth) / 2), 0); + cropCommon(pixelMap, cropWidth, cropHeight, cropPosition); + return; + } + const cropWidth = width; + const cropHeight = Math.floor(width * 0.75); + const cropPosition = new RegionItem(0, Math.floor((height - cropHeight) / 2)); + cropCommon(pixelMap, cropWidth, cropHeight, cropPosition); +} +// [End CropCommon] + diff --git a/ImageKit/entry/src/main/ets/pages/DisplayAspectRatio.ets b/ImageKit/entry/src/main/ets/pages/DisplayAspectRatio.ets new file mode 100644 index 0000000..2d2d091 --- /dev/null +++ b/ImageKit/entry/src/main/ets/pages/DisplayAspectRatio.ets @@ -0,0 +1,42 @@ +/* +* Copyright (c) 2024 Huawei Device Co., Ltd. +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +/* +* FAQ:如何设置图片显示的分辨率 +*/ + +// [Start DisplayAspectRatio] +@Entry +@Component +struct Index { + build() { + Column() { + Row({ space: 50 }) { + Image($r('app.media.example')) + .sourceSize({ + width: 40, + height: 40 + }) + .objectFit(ImageFit.ScaleDown) + .aspectRatio(1) + .width('25%') + .border({ width: 1 }) + .overlay('width:40 height:40', { align: Alignment.Bottom, offset: { x: 0, y: 40 } }) + } + } + } +} +// [End DisplayAspectRatio] + diff --git a/ImageKit/entry/src/main/ets/pages/GetPhoto.ets b/ImageKit/entry/src/main/ets/pages/GetPhoto.ets new file mode 100644 index 0000000..ccf282d --- /dev/null +++ b/ImageKit/entry/src/main/ets/pages/GetPhoto.ets @@ -0,0 +1,73 @@ +/* +* Copyright (c) 2024 Huawei Device Co., Ltd. +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +/* +* FAQ:如何读取相册中的图片 +*/ + +// [Start GetPhoto] +import { photoAccessHelper } from '@kit.MediaLibraryKit'; +import { image } from '@kit.ImageKit'; +import { fileIo as fs } from '@kit.CoreFileKit'; + +@Entry +@Component +struct Index { + @State getAlbum: string = '显示相册中的图片'; + @State pixel: image.PixelMap | undefined = undefined; + @State albumPath: string = ''; + @State photoSize: number = 0; + + async getPictureFromAlbum() { + // Pull up the album and select the pictures + let PhotoSelectOptions = new photoAccessHelper.PhotoSelectOptions(); + PhotoSelectOptions.MIMEType = photoAccessHelper.PhotoViewMIMETypes.IMAGE_TYPE; + PhotoSelectOptions.maxSelectNumber = 1; + let photoPicker = new photoAccessHelper.PhotoViewPicker(); + let photoSelectResult: photoAccessHelper.PhotoSelectResult = await photoPicker.select(PhotoSelectOptions); + this.albumPath = photoSelectResult.photoUris[0]; + + // Read the image as a buffer + const file = fs.openSync(this.albumPath, fs.OpenMode.READ_ONLY); + this.photoSize = fs.statSync(file.fd).size; + console.info('Photo Size: ' + this.photoSize); + let buffer = new ArrayBuffer(this.photoSize); + fs.readSync(file.fd, buffer); + fs.closeSync(file); + + // Decoding into PixelMap + const imageSource = image.createImageSource(buffer); + console.log('imageSource: ' + JSON.stringify(imageSource)); + this.pixel = await imageSource.createPixelMap({}); + } + + build() { + Row() { + Column() { + Image(this.pixel) + .width('100%') + .aspectRatio(1) + Button('显示照片') + .onClick(() => { + this.getPictureFromAlbum(); + }) + } + .width('100%') + } + .height('100%') + } +} +// [End GetPhoto] + diff --git a/ImageKit/entry/src/main/ets/pages/PixelFormat.ets b/ImageKit/entry/src/main/ets/pages/PixelFormat.ets new file mode 100644 index 0000000..c7a95e2 --- /dev/null +++ b/ImageKit/entry/src/main/ets/pages/PixelFormat.ets @@ -0,0 +1,67 @@ +/* +* Copyright (c) 2024 Huawei Device Co., Ltd. +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +/* +* FAQ:将pixelFormat设置为nv21或者nv12时抛出错误码62980096怎么处理 +*/ + +// [Start PixelFormat] +import { image } from '@kit.ImageKit'; + +@Entry +@Component +struct Index { + @State message: string = 'NV21AndNV12ToPixelMap'; + @State private pixelMap: PixelMap | null = null; + @State private pixelMap2: PixelMap | null = null; + + build() { + Row() { + Column() { + Image(this.pixelMap) + .width(200).height(200).margin(15) + Text(this.message) + .fontSize(50) + .fontWeight(FontWeight.Bold) + .onClick(async () => { + let resourceManager = getContext(this).resourceManager + let imageArray = await resourceManager.getMediaContent($r("app.media.sample14_NV21_fromJPG_510X510")); + let pixelBuffer = new Uint8Array(imageArray).buffer as Object as ArrayBuffer; + // The value 8 of the sourcePixelFormat parameter corresponds to NV21 format, and 9 corresponds to NV12 format; The sourceSize parameter needs to set the width and height (the width and height data of the original yuv image), and the width value cannot be odd. + let sourceOptions: image.SourceOptions = + { sourceDensity: 120, sourcePixelFormat: 8, sourceSize: { width: 510, height: 510 } }; + let imageResource = image.createImageSource(pixelBuffer, sourceOptions); + let opts: image.DecodingOptions = { editable: true } + this.pixelMap = await imageResource.createPixelMap(opts); + + let imageArray2 = await resourceManager.getMediaContent($r('app.media.sample10_NV12_fromJPG_510X510')); + let pixelBuffer2 = new Uint8Array(imageArray2).buffer as Object as ArrayBuffer; + // The value 8 of the sourcePixelFormat parameter corresponds to NV21 format, and 9 corresponds to NV12 format; The sourceSize parameter needs to set the width and height (the width and height data of the original yuv image), and the width value cannot be odd. + let sourceOptions2: image.SourceOptions = + { sourceDensity: 120, sourcePixelFormat: 9, sourceSize: { width: 510, height: 510 } }; + let imageResource2 = image.createImageSource(pixelBuffer2, sourceOptions2); + let opts2: image.DecodingOptions = { editable: true } + this.pixelMap2 = await imageResource2.createPixelMap(opts2); + }) + Image(this.pixelMap2) + .width(200).height(200).margin(15) + } + .width('100%') + } + .height('100%') + } +} +// [End PixelFormat] + diff --git a/ImageKit/entry/src/main/resources/base/media/example.png b/ImageKit/entry/src/main/resources/base/media/example.png new file mode 100644 index 0000000000000000000000000000000000000000..205ad8b5a8a42e8762fbe4899b8e5e31ce822b8b GIT binary patch literal 20093 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

c)ijgxDm}8EsRXFt3h@+Ufr7@DN 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 literal 0 HcmV?d00001 diff --git a/NetworkKit/entry/oh-package.json5 b/NetworkKit/entry/oh-package.json5 index 248c3b7..f97dd29 100644 --- a/NetworkKit/entry/oh-package.json5 +++ b/NetworkKit/entry/oh-package.json5 @@ -5,6 +5,8 @@ "main": "", "author": "", "license": "", - "dependencies": {} + "dependencies": { + "@ohos/axios": "^2.2.4" + } } diff --git a/NetworkKit/entry/src/main/ets/pages/AddressesByName.ets b/NetworkKit/entry/src/main/ets/pages/AddressesByName.ets new file mode 100644 index 0000000..c47b6fb --- /dev/null +++ b/NetworkKit/entry/src/main/ets/pages/AddressesByName.ets @@ -0,0 +1,28 @@ +/* +* Copyright (c) 2024 Huawei Device Co., Ltd. +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +/* +* FAQ:Socket接口库是否支持绑定域名 +*/ + +// [Start AddressesByName] +import { connection } from '@kit.NetworkKit' +import { BusinessError } from "@kit.BasicServicesKit" + +connection.getAddressesByName("xxxx", (error: BusinessError, data: connection.NetAddress[]) => { + console.log(JSON.stringify(error)); + console.log(JSON.stringify(data)); +}) +// [End AddressesByName] diff --git a/NetworkKit/entry/src/main/ets/pages/AxiosPost.ets b/NetworkKit/entry/src/main/ets/pages/AxiosPost.ets new file mode 100644 index 0000000..e09447f --- /dev/null +++ b/NetworkKit/entry/src/main/ets/pages/AxiosPost.ets @@ -0,0 +1,40 @@ +/* +* Copyright (c) 2024 Huawei Device Co., Ltd. +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + + +/* +* FAQ:三方件@ohos/axios中发起post请求,如何以queryParams形式传递参数 +*/ +import { url } from '@kit.ArkTS' +import axios,{ AxiosError,AxiosResponse } from '@ohos/axios' +// [Start AxiosPost] +let params: url.URLParams = new url.URLParams() +params.append('fod' ,'1') +params.append('bard','2') +axios.post('https://developer.mozilla.org/?' + params.toString()).then((res: AxiosResponse) => { + let message = "request result: " + JSON.stringify(res.data); +}).catch((err:AxiosError) => { + let message = "request error: " + err.message; +}) +// [End AxiosPost] + + +// [Start AxiosPost2] +axios({ url: 'https://developer.mozilla.org/?', method: 'post', params: { fod: '1', bard: '2', } }).then((res: AxiosResponse) => { + let message = "request result: " + JSON.stringify(res.data); +}).catch((err:AxiosError) => { + let message = "request error: " + err.message; +}) +// [End AxiosPost2] diff --git a/NetworkKit/entry/src/main/ets/pages/ContentType.ets b/NetworkKit/entry/src/main/ets/pages/ContentType.ets new file mode 100644 index 0000000..3b61ee8 --- /dev/null +++ b/NetworkKit/entry/src/main/ets/pages/ContentType.ets @@ -0,0 +1,34 @@ +/* +* Copyright (c) 2024 Huawei Device Co., Ltd. +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +/* +* FAQ:http网络连接中的通用知识 +*/ +import { http } from '@kit.NetworkKit'; + + +let httpRequest = http.createHttp(); +httpRequest.request( + "EXAMPLE_URL", + { + header: { + 'Accept': 'application/json', + // [Start ContentType] + 'Content-Type': "application/json; charset=UTF-8" + // [End ContentType] + } + }, + () => { + }); diff --git a/NetworkKit/entry/src/main/ets/pages/ExtraData.txt b/NetworkKit/entry/src/main/ets/pages/ExtraData.txt new file mode 100644 index 0000000..19a09fc --- /dev/null +++ b/NetworkKit/entry/src/main/ets/pages/ExtraData.txt @@ -0,0 +1,11 @@ + + +/* +* FAQ:http请求的官方示例代码中的extraData是什么类型 +*/ + +// [Start ExtraData] +1)extraData:"data to send"; +2)extraData:{ data: "data to send", }; +3)extraData:{ data: new ArrayBuffer(1)}; +// [End ExtraData] \ No newline at end of file diff --git a/NetworkKit/entry/src/main/ets/pages/GetAxiosUTF.ets b/NetworkKit/entry/src/main/ets/pages/GetAxiosUTF.ets new file mode 100644 index 0000000..08c5b13 --- /dev/null +++ b/NetworkKit/entry/src/main/ets/pages/GetAxiosUTF.ets @@ -0,0 +1,54 @@ +/* +* Copyright (c) 2024 Huawei Device Co., Ltd. +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +/* +* FAQ:如何将Axios获取GBK格式的网络数据转换UTF-8格式 +*/ + +// [Start GetAxiosUTF] +import { util } from '@kit.ArkTS'; +import axios, { AxiosResponse } from '@ohos/axios'; + +const URL: string = 'xxx'; + +@Entry +@Component +struct FriendsBook { + + build() { + } + + aboutToAppear() { + axios, null>({ + method: 'get', + url: URL, + // When using the til.TextDecoder method, the encoding and decoding formats must be consistent, + // so the data type needs to be set to ARRAY_SUFFER when retrieving, otherwise garbled characters will appear. + responseType: 'ARRAY_BUFFER' + }) + .then((res: AxiosResponse) => { + // First, use create to construct a TextDecoder instance and set the encoding format to gbk. + const textDecoder = util.TextDecoder.create('gbk', { ignoreBOM: true }); + // Next, use the decodeWithStream method to decode the input parameters and output the corresponding UTF-8 formatted text string. + // The parameters passed in must be in Uint8Array format, so the obtained data needs to be converted to an array type using the Uint8Array method. + const result = new Uint8Array(res.data); + const resultString = textDecoder.decodeToString(result, { stream: false }); + // Parse JSON strings. + const jsonResult = JSON.parse(resultString) as string; + }) + } +} +// [End GetAxiosUTF] + diff --git a/NetworkKit/entry/src/main/ets/pages/GetDns.ets b/NetworkKit/entry/src/main/ets/pages/GetDns.ets new file mode 100644 index 0000000..d92e957 --- /dev/null +++ b/NetworkKit/entry/src/main/ets/pages/GetDns.ets @@ -0,0 +1,52 @@ +/* +* Copyright (c) 2024 Huawei Device Co., Ltd. +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +/* +* FAQ:connection如何获取网卡链路地址、DNS地址等信息 +*/ + +// [Start GetDns] +import { connection } from '@kit.NetworkKit'; +import { BusinessError } from '@kit.BasicServicesKit'; + +@Entry +@Component +struct GetConnectionProperties { + getConnectionProperties() { + connection.getDefaultNet().then((netHandle: connection.NetHandle) => { + connection.getConnectionProperties(netHandle, (error: BusinessError, data: connection.ConnectionProperties) => { + if (error) { + console.error(`Failed to get connection properties. Code:${error.code}, message:${error.message}`); + return; + } + console.info('Succeeded to get data: ' + JSON.stringify(data)); + }) + }); + } + + build() { + Column({ space: 10 }) { + Button('获取对应的网络连接信息') + .onClick(() => { + this.getConnectionProperties(); + }) + } + .alignItems(HorizontalAlign.Center) + .height('100%') + .width('100%') + } +} +// [End GetDns] + diff --git a/NetworkKit/entry/src/main/ets/pages/GetNetErr.ets b/NetworkKit/entry/src/main/ets/pages/GetNetErr.ets new file mode 100644 index 0000000..f93e527 --- /dev/null +++ b/NetworkKit/entry/src/main/ets/pages/GetNetErr.ets @@ -0,0 +1,52 @@ +/* +* Copyright (c) 2024 Huawei Device Co., Ltd. +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +/* +* FAQ:无网络环境下使用同步方法获取网络状态报错 +*/ + +// [Start GetNetErr] +import { connection } from '@kit.NetworkKit' +import { BusinessError } from '@kit.BasicServicesKit'; + +@Entry +@Component +struct GetErrInfo { + getErrInfo() { + try { + let netHandle = connection.getDefaultNetSync(); + let connectionproperties = connection.getConnectionPropertiesSync(netHandle); + } catch (err) { + let error: BusinessError = err as BusinessError; + console.log('error: ' + JSON.stringify(error)); + } + } + + build() { + Row() { + Column() { + Button('获取网络类型') + .onClick(() => { + this.getErrInfo(); + + }) + } + .width('100%') + } + .height('100%') + } +} +// [End GetNetErr] + diff --git a/NetworkKit/entry/src/main/ets/pages/HasNet.ets b/NetworkKit/entry/src/main/ets/pages/HasNet.ets new file mode 100644 index 0000000..cc887cb --- /dev/null +++ b/NetworkKit/entry/src/main/ets/pages/HasNet.ets @@ -0,0 +1,108 @@ +/* +* Copyright (c) 2024 Huawei Device Co., Ltd. +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +/* +* FAQ:如何判断当前网络能否上网 +*/ + +// [Start HasNet] +import { connection } from '@kit.NetworkKit'; +import { BusinessError } from '@kit.BasicServicesKit'; + + +@Entry +@Component +export struct NetJudge { + public static netConnection: connection.NetConnection | undefined = undefined; + private static JUDGE_NET_TAG: string = 'NetJudge.currNet.isUseful'; + + init() { + NetJudge.netConnection = connection.createNetConnection(); + NetJudge.netConnection.register(() => { + console.info('connection register success'); + }); + + NetJudge.netConnection.on('netAvailable', (data) => { + console.info('NetJudge netAvailable '); + AppStorage.setOrCreate(NetJudge.JUDGE_NET_TAG, this.judgeHasNet()); + }); + + NetJudge.netConnection.on('netUnavailable', () => { + console.info('NetJudge netUnavailable '); + AppStorage.setOrCreate(NetJudge.JUDGE_NET_TAG, this.judgeHasNet()); + }); + + NetJudge.netConnection.on('netCapabilitiesChange', (data: connection.NetCapabilityInfo) => { + AppStorage.setOrCreate(NetJudge.JUDGE_NET_TAG, this.judgeHasNet()); + }); + + // Subscribe to network connection information change events. You can only receive this event notification after calling register + NetJudge.netConnection.on('netConnectionPropertiesChange', (data: connection.NetConnectionPropertyInfo) => { + AppStorage.setOrCreate(NetJudge.JUDGE_NET_TAG, this.judgeHasNet()); + }); + + NetJudge.netConnection.on('netLost', () => { + AppStorage.setOrCreate(NetJudge.JUDGE_NET_TAG, this.judgeHasNet()); + }); + } + + regist() { + if (NetJudge.netConnection === undefined) { + this.init(); + } + } + + judgeHasNet(): boolean { + try { // Get current network connection + let netHandle = connection.getDefaultNetSync(); + + // 0-100 Reserved connections for the system + if (!netHandle || netHandle.netId < 100) { + return false; + } + + // Get the properties of the connection + let netCapability = connection.getNetCapabilitiesSync(netHandle); + let cap = netCapability.networkCap; + if (!cap) { + return false; + } + + for (let em of cap) { + if (connection.NetCap.NET_CAPABILITY_VALIDATED === em) { + return true; + } + } + } catch (e) { + let err = e as BusinessError; + console.info('get netInfo error :' + JSON.stringify(err)); + } + return false; + } + + build() { + Column({ space: 10 }) { + Button('如何判断当前网络能否上网') + .onClick(() => { + this.regist(); + }) + } + .alignItems(HorizontalAlign.Center) + .height('100%') + .width('100%') + } +} +// [End HasNet] + diff --git a/NetworkKit/entry/src/main/ets/pages/NetModule.json5 b/NetworkKit/entry/src/main/ets/pages/NetModule.json5 new file mode 100644 index 0000000..44703bb --- /dev/null +++ b/NetworkKit/entry/src/main/ets/pages/NetModule.json5 @@ -0,0 +1,23 @@ +/* +* FAQ:手机网络正常,但是调用connection.hasDefaultNet()接口失败 +*/ + +// [Start NetModule] +{ + "module": { + // ... + "requestPermissions": [ + { + "name": "ohos.permission.GET_NETWORK_INFO", + "reason": "$string:reason", + "usedScene": { + "abilities": [ + "FormAbility" + ], + "when": "inuse" + } + } + ] + } +} +// [End NetModule] \ No newline at end of file diff --git a/NetworkKit/entry/src/main/ets/pages/OnConnection.ets b/NetworkKit/entry/src/main/ets/pages/OnConnection.ets new file mode 100644 index 0000000..09af398 --- /dev/null +++ b/NetworkKit/entry/src/main/ets/pages/OnConnection.ets @@ -0,0 +1,54 @@ +/* +* Copyright (c) 2024 Huawei Device Co., Ltd. +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +/* +* FAQ:connection如何监听网络事件 +*/ + +// [Start OnConnection] +import { connection } from '@kit.NetworkKit'; +import { BusinessError } from '@kit.BasicServicesKit'; + +@Entry +@Component +struct ListenNetworkEvent { + listenNetworkEvent() { + // Create NetConnection object + let netConnection: connection.NetConnection = connection.createNetConnection(); + + // Register subscription events using the register interface first + netConnection.register((error: BusinessError) => { + console.info(JSON.stringify(error)); + }); + + // Subscribe to network available events. You can only receive this event notification after calling register + netConnection.on('netAvailable', (data: connection.NetHandle) => { + console.info('Succeeded to get data: ' + JSON.stringify(data)); + }); + } + + build() { + Column({ space: 10 }) { + Button('监听网络事件').onClick(() => { + this.listenNetworkEvent() + }) + } + .alignItems(HorizontalAlign.Center) + .height('100%') + .width('100%') + } +} +// [End OnConnection] + diff --git a/NetworkKit/entry/src/main/ets/pages/OnNetVpn.ets b/NetworkKit/entry/src/main/ets/pages/OnNetVpn.ets new file mode 100644 index 0000000..45c681c --- /dev/null +++ b/NetworkKit/entry/src/main/ets/pages/OnNetVpn.ets @@ -0,0 +1,50 @@ +/* +* Copyright (c) 2024 Huawei Device Co., Ltd. +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +/* +* FAQ:如何监听判断VPN类型网络 +*/ + +// [Start OnNetVpn] +import { connection } from '@kit.NetworkKit'; + +@Entry +@Component +export struct JudeNetType { + getNetType() { + connection.getDefaultNet().then((netHandle: connection.NetHandle) => { + connection.getNetCapabilities(netHandle).then((data: connection.NetCapabilities) => { + const type: Number = data.bearerTypes[0]; + console.log('Acquire NetType Success:' + type) + if (type === 4) { + // 当type的值为4,则网络类型为VPN + console.log('is vpn'); + } + }) + }); + } + + build() { + Column({ space: 10 }) { + Button('获取网络连接类型').onClick(() => { + this.getNetType() + }) + }.alignItems(HorizontalAlign.Center) + .height('100%') + .width('100%') + } +} +// [End OnNetVpn] + diff --git a/NetworkKit/entry/src/main/ets/pages/RequestJson.ets b/NetworkKit/entry/src/main/ets/pages/RequestJson.ets new file mode 100644 index 0000000..2b277db --- /dev/null +++ b/NetworkKit/entry/src/main/ets/pages/RequestJson.ets @@ -0,0 +1,37 @@ +/* +* Copyright (c) 2024 Huawei Device Co., Ltd. +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +/* +* FAQ:ArkTS中HTTP请求如何以JSON形式进行传输 +*/ + +// [Start RequestJson] +import { http } from '@kit.NetworkKit'; + +class Header { + public contentType: string; + constructor(contentType: string) { + this.contentType = contentType; + } +} +let httpRequest = http.createHttp(); +let promise = httpRequest.request("EXAMPLE_URL", { + method: http.RequestMethod.GET, + connectTimeout: 60000, + readTimeout: 60000, + header: new Header('application/json') +}); +// [End RequestJson] + diff --git a/NetworkKit/entry/src/main/ets/pages/SetCookie.ets b/NetworkKit/entry/src/main/ets/pages/SetCookie.ets new file mode 100644 index 0000000..86a13e6 --- /dev/null +++ b/NetworkKit/entry/src/main/ets/pages/SetCookie.ets @@ -0,0 +1,79 @@ +/* +* Copyright (c) 2024 Huawei Device Co., Ltd. +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +/* +* FAQ:HTTP接口如何设置Cookie +*/ + +// [Start SetCookie] +import { http } from '@kit.NetworkKit'; + +class Header { + public contentType: string; + public cookie: Record; + + constructor(contentType: string, cookie: Record) { + this.contentType = contentType; + this.cookie = cookie; + } +} + +@Entry +@Component +struct HttpRequest { + @State message: string = '发起请求'; + + request() { + let httpRequest = http.createHttp(); + let options: http.HttpRequestOptions = { + method: http.RequestMethod.POST, + extraData: 'data to send', + expectDataType: http.HttpDataType.STRING, + priority: 1, + // Developers add header fields according to their own business needs, with the first parameter being contentType + // and the second parameter being cookie + header: new Header('application/json', { 'name': 'value', 'name2': 'value2' }) + }; + console.info('Header:' + JSON.stringify(new Header('application/json', { 'name': 'value', 'name2': 'value2' }))); + httpRequest.request("EXAMPLE_URL", options, (err: Error, data: http.HttpResponse) => { + if (!err) { + console.info('Result:' + data.result); + console.info('code:' + data.responseCode); + console.info('type:' + JSON.stringify(data.resultType)); + console.info('header:' + JSON.stringify(data.header)); + console.info('cookies:' + data.cookies); // Starting from API version 8, cookies are supported + } else { + console.info('error:' + JSON.stringify(err)); + } + }); + } + + build() { + Row() { + Column() { + Button(this.message) + .fontSize(30) + .fontWeight(FontWeight.Bold) + .onClick(() => { + this.request(); + }) + } + .width('100%') + } + .height('100%') + } +} +// [End SetCookie] + diff --git a/NetworkKit/entry/src/main/ets/pages/SetForm.ets b/NetworkKit/entry/src/main/ets/pages/SetForm.ets new file mode 100644 index 0000000..9c62886 --- /dev/null +++ b/NetworkKit/entry/src/main/ets/pages/SetForm.ets @@ -0,0 +1,59 @@ +/* +* Copyright (c) 2024 Huawei Device Co., Ltd. +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +/* +* FAQ:http请求如何以表单形式进行传输 +*/ + +// [Start SetForm] +import { http } from '@kit.NetworkKit'; + +let options: http.HttpRequestOptions = { + method: http.RequestMethod.GET, + extraData: 'send message', + header: { 'Content-Type': 'application/x-www-form-urlencoded' }, + readTimeout: 50000, + connectTimeout: 50000 +} +// [End SetForm] + +// [Start SetForm2] +let httpRequest = http.createHttp(); +let data = "user=Query&password=Admin123"; +httpRequest.request( + 'https:xxx', + { + method: http.RequestMethod.POST, + // Optional, default is http.RequestMethod.gET//Developers can add header fields according to their own business needs + header: { 'Content-Type': 'application/x-www-form-urlencoded' }, // This field is used to pass content when using POST requests + extraData: data, + connectTimeout: 60000, // Optional, default is 60000ms + readTimeout: 60000, // Optional, default is 60000ms + }, (err, data) => { + if (!err) { + // Data.read is the HTTP response content, which can be parsed according to business needs + console.info('Result:' + JSON.stringify(data.result)); + console.info('code:' + + JSON.stringify(data.responseCode)); // Data.reader is an HTTP response header that can be parsed according to business needs + console.info('header:' + JSON.stringify(data.header)); + console.info('cookies:' + + JSON.stringify(data.cookies)); // Starting from API8 + } else { + console.info('error:' + JSON.stringify(err)); // Unsubscribe from HTTP response header events + httpRequest.off('headersReceive'); // When the request is exhausted, call the destroy method to actively destroy it. + httpRequest.destroy(); + } +}) +// [End SetForm2] diff --git a/NetworkKit/entry/src/main/ets/pages/SetLongConnect.ets b/NetworkKit/entry/src/main/ets/pages/SetLongConnect.ets new file mode 100644 index 0000000..24b7c2d --- /dev/null +++ b/NetworkKit/entry/src/main/ets/pages/SetLongConnect.ets @@ -0,0 +1,33 @@ +/* +* Copyright (c) 2024 Huawei Device Co., Ltd. +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +/* +* FAQ:如何实现http长连接 +*/ + +// [Start SetLongConnect] +import { http } from '@kit.NetworkKit'; +import { BusinessError } from '@kit.BasicServicesKit'; + +let httpRequest = http.createHttp(); +httpRequest.requestInStream("EXAMPLE_URL", (err: BusinessError, data: number) => { + if (!err) { + console.info("requestInStream OK! ResponseCode is " + JSON.stringify(data)); + } else { + console.error("requestInStream ERROR : err = " + JSON.stringify(err)); + } +}) +// [End SetLongConnect] + diff --git a/NetworkKit/entry/src/main/ets/pages/SetNetSpecifier.ets b/NetworkKit/entry/src/main/ets/pages/SetNetSpecifier.ets new file mode 100644 index 0000000..0b13434 --- /dev/null +++ b/NetworkKit/entry/src/main/ets/pages/SetNetSpecifier.ets @@ -0,0 +1,115 @@ +/* +* Copyright (c) 2024 Huawei Device Co., Ltd. +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +/* +* FAQ:如何解决网络连接状态变化的公共事件返回内容为"NetType":1的问题 +*/ + +// [Start SetNetSpecifier] +import { connection } from '@kit.NetworkKit'; +import { BusinessError, commonEventManager } from '@kit.BasicServicesKit'; + +function listen_network() { + let netSpecifier: connection.NetSpecifier = { + netCapabilities: { + //Assuming the current default network is WiFi, a cellular network connection needs to be created, and the network type can be specified as cellular network + bearerTypes: [connection.NetBearType.BEARER_CELLULAR, connection.NetBearType.BEARER_WIFI], + }, + }; + let conn = connection.createNetConnection(netSpecifier); + + + conn.register((err: BusinessError, data: void) => { + console.warn('register 网络 ' + JSON.stringify(err)) + }); + + // Subscription event, network available + conn.on('netAvailable', ((data: connection.NetHandle) => { + console.warn('网络可用, netId is ' + data.netId); + })); + + // Subscription event, network available + conn.on('netCapabilitiesChange', ((data: connection.NetCapabilityInfo) => { + console.warn('网络 netCapabilitiesChange bearerTypes ' + data.netCap.bearerTypes); + console.warn('网络 netCapabilitiesChange networkCap ' + data.netCap.networkCap); + })); + + // Subscription event, network unavailable + conn.on('netUnavailable', ((data: void) => { + console.warn('网络不可用, data is ' + JSON.stringify(data)); + })); + + // Subscription event, network disconnection + conn.on('netLost', ((data: connection.NetHandle) => { + console.warn('网络lost, netId is ' + data.netId); + })); +} + +// After monitoring an event, it is necessary to obtain the network status through a network interface +function sub_network() { + console.warn('into sub_network') + // Public event monitoring code: + let subscribeInfo: commonEventManager.CommonEventSubscribeInfo = { + // Subscription message exception public event + events: ['usual.event.CONNECTIVITY_CHANGE'] + } + + // Create subscriber callback + commonEventManager.createSubscriber(subscribeInfo, (err: BusinessError, subscriber: commonEventManager.CommonEventSubscriber) => { + if (err) { + console.warn(`Failed to create netWorkSubscribeInfo. Code is ${err.code}, message is ${err.message}`); + return; + } + if (subscribeInfo && subscribeInfo != null) { + // Subscribe to public event callbacks + commonEventManager.subscribe(subscriber, (err: BusinessError, data: commonEventManager.CommonEventData) => { + if (err) { + console.warn(`Failed to netWorkSubscribe common event. Code is ${err.code}, message is ${err.message}`); + return; + } + console.warn('NET_CONNECTIVITY_CHANGE:' + JSON.stringify(data.parameters)); + + setTimeout(async () => { + connection.getDefaultNet((error, data) => { + console.log(JSON.stringify(error)) + console.log(JSON.stringify(data)) + }) }, 500); + // The log printed here is{'NetType':1,'moduleName':''} + }) + } + }) +} + +@Entry +@Component +struct NetWork { + + build() { + Row() { + Column() { + Button('监听网络').onClick(()=>{ + listen_network() + }) + Button('获取网络状态').onClick(()=>{ + sub_network() + }) + } + .width('100%') + } + .height('100%') + } +} +// [End SetNetSpecifier] + diff --git a/NetworkKit/entry/src/main/ets/pages/SetNetType.ets b/NetworkKit/entry/src/main/ets/pages/SetNetType.ets new file mode 100644 index 0000000..b6fea12 --- /dev/null +++ b/NetworkKit/entry/src/main/ets/pages/SetNetType.ets @@ -0,0 +1,35 @@ +/* +* Copyright (c) 2024 Huawei Device Co., Ltd. +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +/* +* FAQ:如何判断使用的是移动蜂窝网络 +*/ + +// [Start SetNetType] +import { connection } from '@kit.NetworkKit'; + +// Check if the network is connected +connection.hasDefaultNet((error, data) => { + console.log('data: ' + data); +}) +// Obtain network capability information +connection.getDefaultNet().then((netHandle) => { + connection.getNetCapabilities(netHandle, (error, data) => { + console.log(JSON.stringify(error)); + console.log(JSON.stringify(data)); + }) +}) +// [End SetNetType] + diff --git a/NetworkKit/entry/src/main/ets/pages/SetSocketServer.ets b/NetworkKit/entry/src/main/ets/pages/SetSocketServer.ets new file mode 100644 index 0000000..fe0834b --- /dev/null +++ b/NetworkKit/entry/src/main/ets/pages/SetSocketServer.ets @@ -0,0 +1,66 @@ +/* +* Copyright (c) 2024 Huawei Device Co., Ltd. +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +/* +* FAQ:使用SocketServer时,如何解决较高概率接收不到 client.on("message", (value: SocketInfo) 中的回调问题 +*/ + +// [Start SetSocketServer] +import { socket } from '@kit.NetworkKit'; + +let tcpServer: socket.TCPSocketServer = socket.constructTCPSocketServerInstance(); +// 定义存放客户端连接的数组 +let tcpConnectArray: socket.TCPSocketConnection[] = []; + +class SocketInfo { + message: ArrayBuffer = new ArrayBuffer(1); + remoteInfo: socket.SocketRemoteInfo = {} as socket.SocketRemoteInfo; +} + +@Entry +@Component +struct CreateSocket { + build() { + Column() { + Button('创建socket').onClick(async () => { + tcpServer.on('connect', (client: socket.TCPSocketConnection) => { + // 保存客户端的socket + tcpConnectArray.push(client); + // Subscribe to events of the TCPSocketConnection object. + client.on('close', () => { + console.log("on close success"); + }); + client.on('message', (value: SocketInfo) => { + // 此处高概率收不到message + let buffer = value.message; + let dataView = new DataView(buffer); + let str = ''; + for (let i = 0; i < dataView.byteLength; ++i) { + str += String.fromCharCode(dataView.getUint8(i)); + } + console.log('received message--:' + str); + }); + }) + console.log('create socket Succeeded '); + }) + + } + .height('100%') + .width('100%') + .justifyContent(FlexAlign.Center) + } +} +// [End SetSocketServer] + diff --git a/NetworkKit/entry/src/main/ets/pages/SetTcpSocket.ets b/NetworkKit/entry/src/main/ets/pages/SetTcpSocket.ets new file mode 100644 index 0000000..5999468 --- /dev/null +++ b/NetworkKit/entry/src/main/ets/pages/SetTcpSocket.ets @@ -0,0 +1,67 @@ +/* +* Copyright (c) 2024 Huawei Device Co., Ltd. +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +/* +* FAQ:在建立好TCPSocket之后,如何将复合类型结构转换为ArrayBuffer +*/ + +// [Start SetTcpSocket] +interface ObjData { + A1: string, + B1: number, + C1: ObjData2 +} +interface ObjData2 { + key1: string, + key2: string +} + +@Entry +@Component +export struct ObjectToArrayBuffer { + @State message: string = 'Object转ArrayBuffe'; + + strToArrayBuffer(str: string) { + let buf = new ArrayBuffer(str.length * 2); + let bufView = new Uint16Array(buf); + for (let i = 0, strLen = str.length; i < strLen; i++) { + bufView[i] = str.charCodeAt(i); + } + return bufView; + } + + build() { + Row() { + Column() { + Text(this.message) + .fontSize(50) + .fontWeight(FontWeight.Bold) + .onClick(() => { + let objData: ObjData = { + A1:'字符串', + B1: 1, + C1:{'key1':'FF','key2':'GG'} + } + let buf1 = this.strToArrayBuffer(JSON.stringify(objData)); + console.info(`buf1: ${JSON.stringify(buf1 )}`); + }) + } + .width('100%') + } + .height('100%') + } +} +// [End SetTcpSocket] + -- Gitee