| // Copyright 2022 The Fuchsia Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| import { randomUUID } from 'crypto'; |
| import * as vscode from 'vscode'; |
| |
| import * as metric_properties from './metric_properties'; |
| |
| const ENABLED_PROPERTY = 'analytics-status'; |
| const UUID_PROPERTY = 'uuid'; |
| const LAUNCHED_PROPERTY = 'vscode-fuchsia-launched'; |
| const INTERNAL_PROPERTY = 'analytics-status-internal'; |
| |
| export namespace external { |
| |
| /** |
| * Enable analytics in persistent state for external user. |
| */ |
| export async function enable(): Promise<void> { |
| await metric_properties.setBoolean(ENABLED_PROPERTY, true); |
| await metric_properties.set(UUID_PROPERTY, randomUUID()); |
| } |
| |
| /** |
| * Disable analytics in persistent state for external user. |
| */ |
| export async function disable(): Promise<void> { |
| await metric_properties.setBoolean(ENABLED_PROPERTY, false); |
| await metric_properties.deleteProperty(UUID_PROPERTY); |
| } |
| |
| /** |
| * Returns true if analytics' persistent state is enabled for external user. |
| */ |
| export async function isEnabled(): Promise<boolean> { |
| // Treat undefined as false |
| return (await metric_properties.getBoolean(ENABLED_PROPERTY)) === true; |
| } |
| |
| /** |
| * Returns true if the extension the first launch of the first Fuchsia tool, |
| * i.e. the extension is launched before other Fuchsia tools such as fx, ffx. |
| */ |
| export async function isFirstLaunchOfFirstTool(): Promise<boolean> { |
| return !(await metric_properties.exists(ENABLED_PROPERTY)); |
| } |
| |
| /** |
| * Mark that the extension has been haunched |
| */ |
| export async function markAsDirectlyLaunched() { |
| await metric_properties.set(LAUNCHED_PROPERTY, ''); |
| } |
| /** |
| * Returns true when the extension is lauched for the first time |
| */ |
| export async function isFirstDirectLaunch(): Promise<boolean> { |
| return !(await metric_properties.exists(LAUNCHED_PROPERTY)); |
| } |
| } |
| |
| export namespace internal { |
| /** |
| * Returns the opt-in level of enhanced analytics. |
| */ |
| export async function getOptInLevel(): Promise<number> { |
| const levelStr = await metric_properties.get(INTERNAL_PROPERTY); |
| if (levelStr === undefined) { |
| if (await external.isEnabled()) { |
| return 1; |
| } else { |
| return 0; |
| } |
| } |
| |
| const level = parseInt(levelStr); |
| if (level >= 0 && level <= 2) { |
| return level; |
| } |
| |
| return 0; |
| } |
| |
| /** |
| * Returns true if the user is new |
| */ |
| export async function isNewUser(): Promise<boolean> { |
| return (! await metric_properties.exists(ENABLED_PROPERTY)) && |
| (! await metric_properties.exists(INTERNAL_PROPERTY)); |
| } |
| |
| /** |
| * Returns true if the user has enabled external analytics but haven't opt-in/out for internal analytics |
| */ |
| export async function isExistingUnmigratedOptedInUser(): Promise<boolean> { |
| return (! await metric_properties.exists(INTERNAL_PROPERTY)) && await external.isEnabled(); |
| } |
| |
| /** |
| * Returns true if the user has disabled external analytics but haven't opt-in/out for internal analytics |
| */ |
| export async function isExistingUnmigratedOptedOutUser(): Promise<boolean> { |
| return (! await metric_properties.exists(INTERNAL_PROPERTY)) && (! await external.isEnabled()); |
| } |
| |
| /** |
| * Opt out internal analytics |
| */ |
| export async function optOut(): Promise<void> { |
| await metric_properties.set(INTERNAL_PROPERTY, '0'); |
| } |
| } |
| |
| /** |
| * Returns the user's UUID if it exists. Otherwise returns undefined. |
| */ |
| export async function getUuid(): Promise<string | undefined> { |
| return await metric_properties.get(UUID_PROPERTY); |
| } |
| |
| export class EnabledPropertyWatcher { |
| private readonly watcher: vscode.FileSystemWatcher; |
| |
| constructor() { |
| const pattern = |
| new vscode.RelativePattern(metric_properties.getMetricDirectory(), ENABLED_PROPERTY); |
| this.watcher = vscode.workspace.createFileSystemWatcher(pattern); |
| } |
| |
| onDidChange(listener: (e: vscode.Uri) => any) { |
| return this.watcher.onDidChange(listener); |
| } |
| |
| dispose() { |
| this.watcher.dispose(); |
| } |
| } |
| |
| export class InternalPropertyWatcher { |
| private readonly watcher: vscode.FileSystemWatcher; |
| |
| constructor() { |
| const pattern = |
| new vscode.RelativePattern(metric_properties.getMetricDirectory(), INTERNAL_PROPERTY); |
| this.watcher = vscode.workspace.createFileSystemWatcher(pattern); |
| } |
| |
| onDidChange(listener: (e: vscode.Uri) => any) { |
| return this.watcher.onDidChange(listener); |
| } |
| |
| dispose() { |
| this.watcher.dispose(); |
| } |
| } |