Hello Everyone
I'm not that good in buliding extensions , can anyone build such extension because it's useful for me + name his/her price
Hello Everyone
I'm not that good in buliding extensions , can anyone build such extension because it's useful for me + name his/her price
I have been working on that but problem is work manager. I am unable to make it work because of that. Also clarity sdks have js assets and during build process they are not included in the final apk despite test server having aar support. Even if they were included they will be under extension package name folder but most libraries require root folder path. Below is my not working code and best of luck. Help from senior fellows will be appreciated.
/**
* Copyright (C) 2024 Husnain
*
* 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.
*/
package pk.analyticsmdcat;
import android.app.Activity;
import android.util.Log;
import com.google.appinventor.components.annotations.DesignerComponent;
import com.google.appinventor.components.annotations.DesignerProperty;
import com.google.appinventor.components.annotations.SimpleEvent;
import com.google.appinventor.components.annotations.SimpleFunction;
import com.google.appinventor.components.annotations.SimpleProperty;
import com.google.appinventor.components.common.ComponentCategory;
import com.google.appinventor.components.runtime.AndroidNonvisibleComponent;
import com.google.appinventor.components.runtime.ComponentContainer;
import com.google.appinventor.components.runtime.EventDispatcher;
import com.microsoft.clarity.Clarity;
import com.microsoft.clarity.ClarityConfig;
import com.microsoft.clarity.models.ApplicationFramework;
import com.microsoft.clarity.models.LogLevel;
import java.util.Collections;
import kotlin.Unit;
/**
* An extension to integrate Microsoft Clarity analytics into MIT App Inventor.
*
* <p>This extension allows developers to track user behavior, replay sessions,
* and gain insights into app usage.
*/
@DesignerComponent(
version = 52,
versionName = "6.0",
description = "Microsoft Clarity Analytics - Developed by Husnain",
iconName = "icon.png",
category = ComponentCategory.EXTENSION
)
public class Analyticsmdcat extends AndroidNonvisibleComponent {
private static final String TAG = "ClarityExtension";
private final Activity activity;
private boolean isInitialized = false;
// --- Member variables for Designer Properties ---
private boolean allowMeteredNetwork = false;
private boolean enableWebViewCapture = true;
private boolean disableOnLowEndDevices = false;
/**
* Creates a new Analyticsmdcat component.
*
* @param container the container that this component will be placed in
*/
public Analyticsmdcat(ComponentContainer container) {
super(container.$form());
this.activity = container.$context();
Log.i(TAG, "ClarityIntegration component created.");
}
/**
* Event raised when a new Clarity session starts.
*
* @param sessionId the unique identifier for the new session
*/
@SimpleEvent(description = "Fires when a new Clarity session starts")
public void SessionStarted(String sessionId) {
EventDispatcher.dispatchEvent(this, "SessionStarted", sessionId);
}
/**
* Event raised when Clarity initialization completes successfully.
*/
@SimpleEvent(description = "Fires when Clarity initialization completes successfully")
public void InitializationComplete() {
EventDispatcher.dispatchEvent(this, "InitializationComplete");
}
/**
* Event raised when Clarity initialization fails.
*
* @param error a message describing the reason for failure
*/
@SimpleEvent(description = "Fires when Clarity initialization fails")
public void InitializationFailed(String error) {
EventDispatcher.dispatchEvent(this, "InitializationFailed", error);
}
/**
* Initializes the Clarity SDK.
*
* <p>This method should be called once on app startup, typically in the
* {@code Screen1.Initialize} event.
*
* @param projectId your Clarity project ID
* @param logLevel the desired level of logging for the Clarity SDK
*/
@SimpleFunction(description = "Initializes the Clarity SDK. Call this once on app startup (e.g., in Screen1.Initialize).")
public void Initialize(String projectId, String logLevel) {
if (isInitialized) {
Log.w(TAG, "Initialize called but SDK is already initialized. Skipping.");
return;
}
if (projectId == null || projectId.trim().isEmpty()) {
String error = "Clarity Project ID cannot be empty.";
Log.e(TAG, "Initialization failed: " + error);
InitializationFailed(error);
return;
}
try {
Log.i(TAG, "Initializing Clarity SDK...");
LogLevel level;
switch (logLevel.toLowerCase()) {
case "verbose": level = LogLevel.Verbose; break;
case "debug": level = LogLevel.Debug; break;
case "info": level = LogLevel.Info; break;
case "warning": level = LogLevel.Warning; break;
case "error": level = LogLevel.Error; break;
default: level = LogLevel.None; break;
}
ClarityConfig config = new ClarityConfig(
projectId,
null, // userId is not set in this version
level,
this.allowMeteredNetwork,
this.enableWebViewCapture,
Collections.singletonList("*"), // Allow all domains for WebView capture
ApplicationFramework.Native,
Collections.emptyList(), // No blocked domains
Collections.emptyList(), // No blocked classes
this.disableOnLowEndDevices,
null // No initial custom tags
);
// Initialize Clarity. This approach avoids WorkManager dependencies.
Clarity.initialize(activity.getApplicationContext(), config);
this.isInitialized = true;
Log.i(TAG, "Clarity SDK initialized successfully.");
// Set up a callback to be notified of new sessions.
Clarity.setOnNewSessionStartedCallback(sessionId -> {
Log.i(TAG, "Clarity session started with ID: " + sessionId);
// Fire the SessionStarted event on the UI thread.
activity.runOnUiThread(() -> SessionStarted(sessionId));
return Unit.INSTANCE;
});
// Notify the user that initialization was successful.
activity.runOnUiThread(() -> InitializationComplete());
} catch (Exception e) {
String error = "Failed to initialize Clarity: " + e.getMessage();
Log.e(TAG, error, e);
InitializationFailed(error);
}
}
/**
* Pauses the collection of analytics data.
*/
@SimpleFunction(description = "Pauses Clarity data collection")
public void Pause() {
if (!isInitialized) {
Log.w(TAG, "Clarity not initialized. Call Initialize() first.");
return;
}
try {
Clarity.pause();
Log.i(TAG, "Clarity paused.");
} catch (Exception e) {
Log.e(TAG, "Error pausing Clarity: " + e.getMessage(), e);
}
}
/**
* Resumes the collection of analytics data.
*/
@SimpleFunction(description = "Resumes Clarity data collection")
public void Resume() {
if (!isInitialized) {
Log.w(TAG, "Clarity not initialized. Call Initialize() first.");
return;
}
try {
Clarity.resume();
Log.i(TAG, "Clarity resumed.");
} catch (Exception e) {
Log.e(TAG, "Error resuming Clarity: " + e.getMessage(), e);
}
}
/**
* Sets a custom tag for the current session.
*
* @param key the name of the custom tag
* @param value the value of the custom tag
*/
@SimpleFunction(description = "Adds a custom tag to the current Clarity session")
public void SetCustomTag(String key, String value) {
if (!isInitialized) {
Log.w(TAG, "Clarity not initialized. Call Initialize() first.");
return;
}
try {
Clarity.setCustomTag(key, value);
Log.i(TAG, "Custom tag set: " + key + " = " + value);
} catch (Exception e) {
Log.e(TAG, "Error setting custom tag: " + e.getMessage(), e);
}
}
/**
* Returns the ID of the current Clarity session.
*
* @return the current session ID, or an empty string if not available
*/
@SimpleFunction(description = "Gets the current Clarity session ID")
public String GetSessionId() {
if (!isInitialized) {
Log.w(TAG, "Clarity not initialized. Call Initialize() first.");
return "";
}
try {
String sessionId = Clarity.getCurrentSessionId();
Log.i(TAG, "Current session ID: " + sessionId);
return sessionId != null ? sessionId : "";
} catch (Exception e) {
Log.e(TAG, "Error getting session ID: " + e.getMessage(), e);
return "";
}
}
// --- Designer Properties ---
/**
* Specifies whether to allow data collection over metered networks.
*
* @param allow {@code true} to allow, {@code false} to disable
*/
@DesignerProperty(editorType = "boolean", defaultValue = "false")
@SimpleProperty(description = "Allow data collection over metered networks")
public void AllowMeteredNetwork(boolean allow) {
this.allowMeteredNetwork = allow;
}
@SimpleProperty
public boolean AllowMeteredNetwork() {
return this.allowMeteredNetwork;
}
/**
* Specifies whether to enable WebView capture.
*
* @param enable {@code true} to enable, {@code false} to disable
*/
@DesignerProperty(editorType = "boolean", defaultValue = "true")
@SimpleProperty(description = "Enable WebView capture")
public void EnableWebViewCapture(boolean enable) {
this.enableWebViewCapture = enable;
}
@SimpleProperty
public boolean EnableWebViewCapture() {
return this.enableWebViewCapture;
}
/**
* Specifies whether to disable Clarity on low-end devices.
*
* @param disable {@code true} to disable, {@code false} to enable
*/
@DesignerProperty(editorType = "boolean", defaultValue = "false")
@SimpleProperty(description = "Disable Clarity on low-end devices")
public void DisableOnLowEndDevices(boolean disable) {
this.disableOnLowEndDevices = disable;
}
@SimpleProperty
public boolean DisableOnLowEndDevices() {
return this.disableOnLowEndDevices;
}
}
Project fast.yml
# The name of the extension developer
author: Husnain
# Define the minimum Android SDK API level your extension supports.
min_sdk: 14
# Define the compile Android SDK API level.
compile_sdk: 34
# If enabled, extension will be optimized using ProGuard.
proguard: true
# If enabled, extension will be optimized using R8.
R8: false
# Enable to collect ProGuard rules from runtime AARs
collect_rules: true
# If enabled, Kotlin Standard Libraries will be included with the extension.
# kotlin: false
# Kotlin Compiler version.
# kotlin_version: 1.9.24
# Enable to desugar extension's source code.
desugar_sources: false
# Enable to desugar runtime dependencies.
desugar_deps: false
# If enabled, the D8 tool will generate desugared jar (classes.dex)
desugar_dex: true
# Select resolver tool between `gradle/maven` to fetch transitive dependencies.
resolver: gradle
# Default repositories are Maven Central, Google Maven & Maven Local.
# If the library you want to use is not available in these repositories, add here by specifying their URLs.
# repositories:
# - https://jitpack.io/
# Runtime dependencies [Remote and Local]
dependencies:
- com.microsoft.clarity:clarity:2.5.2
# Compile-time dependencies [Remote and Local]
# compile_time:
# - groupId:artifactId:version
# - mylibrary.jar
# Define dependencies to skip during resolve. [Remote only]
# excludes:
# - groupId:artifactId:version
# Extension assets. [Should be present in assets directory]
# assets:
# - my-awesome-asset.anything
# Attach custom XML to bundle it with APK. [Should be present in assets directory within relative child directory]
# xmls:
# - targetPath/myfile.xml
# Define AAR libraries to attach with extension. [Remote and Local]
aars:
- com.microsoft.clarity:clarity:2.5.2
# - mylibrary.aar
# Enable to write docs in Markdown during build.
gen_docs: true
# Enable to increment the version number of each component during build.
auto_version: true
# Enable to remove @annotations from the extension.
deannonate: true
# Enable to skip matching classes provided by MIT.
filter_mit_classes: false
Mention your budget.
Are you working on this?
Depends on his budget.
I'm not the developer who will make the extension and as I said before