Skip to content

Commit

Permalink
Option to create desktop shortcut; close on launch
Browse files Browse the repository at this point in the history
  • Loading branch information
alesimula committed Nov 14, 2021
1 parent 59d74d6 commit 309225f
Show file tree
Hide file tree
Showing 5 changed files with 127 additions and 4 deletions.
21 changes: 19 additions & 2 deletions lib/apk_installer.dart
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import 'package:shared_value/shared_value.dart';
import 'package:wsa_pacman/android/permissions.dart';
import 'package:wsa_pacman/global_state.dart';
import 'package:wsa_pacman/main.dart';
import 'package:wsa_pacman/windows/win_io.dart';
import 'package:wsa_pacman/widget/adaptive_icon.dart';
import 'package:wsa_pacman/widget/flexible_info_bar.dart';
import 'package:wsa_pacman/widget/move_window_nomax.dart';
Expand All @@ -24,6 +25,7 @@ import 'package:flutter/services.dart';

import 'package:provider/provider.dart';
import 'package:bitsdojo_window/bitsdojo_window.dart';
import 'package:wsa_pacman/windows/win_path.dart';

import 'dart:developer';
import 'theme.dart';
Expand Down Expand Up @@ -372,6 +374,14 @@ class ProcessData {
class ApkInstaller extends StatefulWidget {
const ApkInstaller({Key? key}) : super(key: key);

static void createLaunchIcon(String package, String appName) {
WinIO.createShortcut(
r"%LOCALAPPDATA%\Microsoft\WindowsApps\MicrosoftCorporationII.WindowsSubsystemForAndroid_8wekyb3d8bbwe\WsaClient.exe",
"${WinPath.desktop}\\$appName",
args: "/launch wsa://$package",
icon: '%LOCALAPPDATA%\\Packages\\MicrosoftCorporationII.WindowsSubsystemForAndroid_8wekyb3d8bbwe\\LocalState\\$package.ico');
}

static void installApk(String apkFile, String ipAddress, int port, [bool downgrade = false]) async {
log("INSTALLING \"$apkFile\" on on $ipAddress:$port...");
var installation = Process.run('${Env.TOOLS_DIR}\\adb.exe', ['-s', '$ipAddress:$port', 'install', if (downgrade) '-r', if (downgrade) '-d', apkFile])
Expand Down Expand Up @@ -401,6 +411,7 @@ class ApkInstaller extends StatefulWidget {
class _ApkInstallerState extends State<ApkInstaller> {
int index = 0;
ToggleButtonThemeData? warningButtonTheme;
bool createShortcut = false;

@override
Widget build(BuildContext context) {
Expand Down Expand Up @@ -523,20 +534,26 @@ class _ApkInstallerState extends State<ApkInstaller> {
titleWidget,
const SizedBox(height: 10),
Text("The application $appTitle was successifully installed"),
if (installType == InstallType.INSTALL) const SizedBox(height: 10),
if (installType == InstallType.INSTALL) Checkbox(
checked: createShortcut,
content: const Text("Create desktop shortcut"),
onChanged: (value) => setState(() => createShortcut = value!),
),
const Spacer(),
Row(
mainAxisAlignment: MainAxisAlignment.end,
crossAxisAlignment: CrossAxisAlignment.end,
children: [
noMoveWindow(Button(
child: const Text('Dismiss'),
onPressed: (){appWindow.close();},
onPressed: (){if (createShortcut) ApkInstaller.createLaunchIcon(package, appTitle); appWindow.close();},
)),
(){return isLaunchable ? const SizedBox(width: 15) : SizedBox.shrink();}(),
(){return isLaunchable ? noMoveWindow(ToggleButton(
child: const Text('Open app'),
checked: true,
onChanged: (_){log('am start -n ${GState.package.of(context)}/${GState.activity.of(context)}'); Process.run('${Env.TOOLS_DIR}\\adb.exe', ['-s', '$ipAddress:$port', 'shell', 'am start -n ${GState.package.of(context)}/${GState.activity.of(context)}']);},
onChanged: (_){if (createShortcut) ApkInstaller.createLaunchIcon(package, appTitle); Process.run('${Env.TOOLS_DIR}\\adb.exe', ['-s', '$ipAddress:$port', 'shell', 'am start -n ${GState.package.of(context)}/${GState.activity.of(context)}']); appWindow.close();},
)) : const SizedBox.shrink();}()
]
)
Expand Down
49 changes: 49 additions & 0 deletions lib/windows/win_io.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
// ignore_for_file: non_constant_identifier_names

import 'dart:ffi';
import 'package:ffi/ffi.dart';
import 'package:win32/win32.dart';

class RegistryKeyValuePair {
final String key;
final String value;

const RegistryKeyValuePair(this.key, this.value);
}

class WinIO {
/// Creates a Windows shortcut (.lnk);
static void createShortcut(String filePath, String linkPath, {String? description, String? args, String? icon}) {
final shellLink = ShellLink.createInstance();
final lpPath = filePath.toNativeUtf16();
final lpArgs = args?.toNativeUtf16();
final lpIcon = icon?.toNativeUtf16();
final lpLinkPath = "$linkPath.lnk".toNativeUtf16();
final lpDescription = description?.toNativeUtf16() ?? nullptr;
final ptrIID_IPersistFile = convertToCLSID(IID_IPersistFile);
final ppf = calloc<COMObject>();

try {
shellLink.SetPath(lpPath);
if (lpArgs != null) shellLink.SetArguments(lpArgs);
if (description != null) shellLink.SetDescription(lpDescription);
if (lpIcon != null) shellLink.SetIconLocation(lpIcon, 0);

final hr = shellLink.QueryInterface(ptrIID_IPersistFile, ppf.cast());
if (SUCCEEDED(hr)) {
final persistFile = IPersistFile(ppf);
persistFile.Save(lpLinkPath, TRUE);
persistFile.Release();
}
shellLink.Release();
} finally {
free(lpPath);
if (lpArgs != null) free(lpArgs);
if (lpIcon != null) free(lpIcon);
free(lpLinkPath);
if (lpDescription != nullptr) free(lpDescription);
free(ptrIID_IPersistFile);
free(ppf);
}
}
}
56 changes: 56 additions & 0 deletions lib/windows/win_path.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
// Copyright (c) 2020, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.

// Demonstrates usage of various shell APIs to retrieve known folder locations

import 'dart:ffi';
import 'package:ffi/ffi.dart';
import 'package:win32/win32.dart';

class WinPath {
// Get the path of the temporary directory (typically %TEMP%)
static late String temp = (){
final buffer = wsalloc(MAX_PATH + 1);
final length = GetTempPath(MAX_PATH, buffer);

try {
if (length == 0) {
final error = GetLastError();
throw WindowsException(error);
} else {
var path = buffer.toDartString();

// GetTempPath adds a trailing backslash, but SHGetKnownFolderPath does not.
// Strip off trailing backslash for consistency with other methods here.
if (path.endsWith('\\')) {
path = path.substring(0, path.length - 1);
}
return path;
}
} finally {
free(buffer);
}
}();

/// Get the desktop path
static late String desktop = (){
final appsFolder = GUIDFromString(FOLDERID_Desktop);
final ppszPath = calloc<PWSTR>();

try {
final hr =
SHGetKnownFolderPath(appsFolder, KF_FLAG_DEFAULT, NULL, ppszPath);

if (FAILED(hr)) {
throw WindowsException(hr);
}

final path = ppszPath.value.toDartString();
return path;
} finally {
free(appsFolder);
free(ppszPath);
}
}();
}
4 changes: 2 additions & 2 deletions pubspec.lock
Original file line number Diff line number Diff line change
Expand Up @@ -500,12 +500,12 @@ packages:
source: hosted
version: "2.1.1"
win32:
dependency: transitive
dependency: "direct main"
description:
name: win32
url: "https://pub.dartlang.org"
source: hosted
version: "2.2.10"
version: "2.3.0"
xdg_directories:
dependency: transitive
description:
Expand Down
1 change: 1 addition & 0 deletions pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ dependencies:
protobuf: ^2.0.0
#path_provider: ^2.0.6
mdi: ^5.0.0-nullsafety.0
win32: ^2.3.0


dev_dependencies:
Expand Down

0 comments on commit 309225f

Please sign in to comment.