Update #227
@@ -7,25 +7,25 @@ Script to add GApps and other stuff to Waydroid!
|
||||
## Interactive terminal interface
|
||||
|
||||
```
|
||||
git clone https://github.com/casualsnek/waydroid_script
|
||||
git clone --depth 1 --single-branch https://github.com/huakim/waydroid_script
|
||||
cd waydroid_script
|
||||
python3 -m venv venv
|
||||
venv/bin/pip install -r requirements.txt
|
||||
sudo venv/bin/python3 main.py
|
||||
```
|
||||
|
||||

|
||||

|
||||
|
||||

|
||||

|
||||
|
||||

|
||||

|
||||
|
||||
|
||||
|
||||
## Command Line
|
||||
|
||||
```bash
|
||||
git clone https://github.com/casualsnek/waydroid_script
|
||||
git clone --depth 1 --single-branch https://github.com/huakim/waydroid_script
|
||||
cd waydroid_script
|
||||
python3 -m venv venv
|
||||
venv/bin/pip install -r requirements.txt
|
||||
@@ -42,18 +42,18 @@ sudo venv/bin/python3 main.py hack {nodataperm, hidestatusbar}
|
||||
## Dependencies
|
||||
|
||||
"lzip" is required for this script to work, install it using your distribution's package manager:
|
||||
### openSUSE, Gecko based distributions:
|
||||
sudo zypper install lzip
|
||||
### RHEL, Fedora and Rocky based distributions:
|
||||
sudo dnf install lzip
|
||||
### Arch, Manjaro and EndeavourOS based distributions:
|
||||
sudo pacman -S lzip
|
||||
### Debian and Ubuntu based distributions:
|
||||
sudo apt install lzip
|
||||
### RHEL, Fedora and Rocky based distributions:
|
||||
sudo dnf install lzip
|
||||
### openSUSE based distributions:
|
||||
sudo zypper install lzip
|
||||
|
||||
## Install OpenGapps
|
||||
|
||||

|
||||

|
||||
|
||||
Open terminal and switch to the directory where "main.py" is located then run:
|
||||
|
||||
@@ -65,13 +65,13 @@ Then launch waydroid with:
|
||||
|
||||
After waydroid has finished booting, open terminal and switch to directory where "main.py" is located then run:
|
||||
|
||||
sudo python3 main.py google
|
||||
sudo venv/bin/python3 main.py certified
|
||||
Copy the returned numeric ID, then open ["https://google.com/android/uncertified/?pli=1"](https://google.com/android/uncertified/?pli=1). Enter the ID and register it. Wait 10-20 minutes for device to get registered. Then clear Google Play Service's cache and try logging in!
|
||||
|
||||
|
||||
## Install Magisk
|
||||
|
||||

|
||||

|
||||
|
||||
Open terminal and switch to directory where "main.py" is located then run:
|
||||
|
||||
@@ -81,7 +81,7 @@ Magisk will be installed on next boot!
|
||||
|
||||
Zygisk and modules like LSPosed should work now.
|
||||
|
||||
If you want to update Magisk, Please use `Direct Install into system partition` or run this sript again.
|
||||
If you want to update Magisk, Please use `Direct Install into system partition` or run this script again.
|
||||
|
||||
This script only focuses on Magisk installation, if you need more management, please check https://github.com/nitanmarcel/waydroid-magisk
|
||||
|
||||
@@ -109,7 +109,7 @@ Open terminal and switch to directory where "main.py" is located then run:
|
||||
|
||||
## Integrate Widevine DRM (L3)
|
||||
|
||||

|
||||

|
||||
|
||||
Open terminal and switch to directory where "main.py" is located then run:
|
||||
|
||||
@@ -117,8 +117,8 @@ Open terminal and switch to directory where "main.py" is located then run:
|
||||
|
||||
## Install Smart Dock
|
||||
|
||||

|
||||

|
||||

|
||||

|
||||
|
||||
Open terminal and switch to directory where "main.py" is located then run:
|
||||
|
||||
@@ -137,7 +137,7 @@ This is a temporary hack to combat against the apps permission issue on Android
|
||||
|
||||
Arknights, PUNISHING: GRAY RAVEN and other games won't freeze on the black screen.
|
||||
|
||||

|
||||

|
||||
|
||||
Open terminal and switch to directory where "main.py" is located then run:
|
||||
|
||||
@@ -163,7 +163,7 @@ chmod 777 -R /mnt/*/*/*/*/Android/obb
|
||||
|
||||
## Install microG, Aurora Store and Aurora Droid
|
||||
|
||||

|
||||

|
||||
|
||||
```
|
||||
sudo venv/bin/python3 main.py install microg
|
||||
@@ -171,10 +171,10 @@ sudo venv/bin/python3 main.py install microg
|
||||
|
||||
## Hide Status Bar
|
||||
Before
|
||||

|
||||

|
||||
|
||||
After
|
||||

|
||||

|
||||
|
||||
```
|
||||
sudo venv/bin/python3 main.py hack hidestatusbar
|
||||
|
||||
|
Before Width: | Height: | Size: 1.0 MiB |
|
Before Width: | Height: | Size: 344 KiB |
|
Before Width: | Height: | Size: 122 KiB |
|
Before Width: | Height: | Size: 321 KiB |
|
Before Width: | Height: | Size: 348 KiB |
|
Before Width: | Height: | Size: 1.0 MiB |
|
Before Width: | Height: | Size: 112 KiB |
|
Before Width: | Height: | Size: 590 KiB |
|
Before Width: | Height: | Size: 665 KiB |
|
Before Width: | Height: | Size: 17 KiB |
|
Before Width: | Height: | Size: 15 KiB |
|
Before Width: | Height: | Size: 43 KiB |
@@ -1,22 +1,4 @@
|
||||
with (import <nixpkgs> {});
|
||||
|
||||
stdenv.mkDerivation {
|
||||
name = "waydroid_script";
|
||||
|
||||
buildInputs = [
|
||||
(python3.withPackages(ps: with ps; [ tqdm requests inquirerpy ]))
|
||||
];
|
||||
|
||||
src = ./.;
|
||||
|
||||
postPatch = ''
|
||||
patchShebangs main.py
|
||||
'';
|
||||
|
||||
installPhase = ''
|
||||
mkdir -p $out/libexec
|
||||
cp -r . $out/libexec/waydroid_script
|
||||
mkdir -p $out/bin
|
||||
ln -s $out/libexec/waydroid_script/main.py $out/bin/waydroid_script
|
||||
'';
|
||||
}
|
||||
let
|
||||
pkgs = import <nixpkgs> {};
|
||||
in
|
||||
pkgs.callPackage ./package.nix { }
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
{
|
||||
"nodes": {
|
||||
"nixpkgs": {
|
||||
"locked": {
|
||||
"lastModified": 1707650010,
|
||||
"narHash": "sha256-dOhphIA4MGrH4ElNCy/OlwmN24MsnEqFjRR6+RY7jZw=",
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "809cca784b9f72a5ad4b991e0e7bcf8890f9c3a6",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"id": "nixpkgs",
|
||||
"type": "indirect"
|
||||
}
|
||||
},
|
||||
"root": {
|
||||
"inputs": {
|
||||
"nixpkgs": "nixpkgs",
|
||||
"systems": "systems"
|
||||
}
|
||||
},
|
||||
"systems": {
|
||||
"locked": {
|
||||
"lastModified": 1681028828,
|
||||
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
|
||||
"owner": "nix-systems",
|
||||
"repo": "default",
|
||||
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "nix-systems",
|
||||
"repo": "default",
|
||||
"type": "github"
|
||||
}
|
||||
}
|
||||
},
|
||||
"root": "root",
|
||||
"version": 7
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
{
|
||||
description = "Waydroid Extras Script";
|
||||
inputs = {
|
||||
systems.url = "github:nix-systems/default";
|
||||
};
|
||||
outputs = { self, nixpkgs, systems }:
|
||||
let
|
||||
inherit (nixpkgs) lib;
|
||||
eachSystem = lib.genAttrs (import systems);
|
||||
mkApp = program: { type = "app"; inherit program; };
|
||||
in {
|
||||
packages = eachSystem (system: rec {
|
||||
waydroid_script = nixpkgs.legacyPackages."${system}".callPackage ./package.nix { };
|
||||
default = waydroid_script;
|
||||
});
|
||||
apps = eachSystem (system: rec {
|
||||
waydroid_script = mkApp "${self.outputs.packages.${system}.waydroid_script}/bin/waydroid_script";
|
||||
default = waydroid_script;
|
||||
});
|
||||
};
|
||||
}
|
||||
@@ -1,7 +1,10 @@
|
||||
#!/usr/bin/env python3
|
||||
from InquirerPy import inquirer
|
||||
from InquirerPy.base.control import Choice
|
||||
from InquirerPy.separator import Separator
|
||||
try:
|
||||
from InquirerPy import inquirer
|
||||
from InquirerPy.base.control import Choice
|
||||
from InquirerPy.separator import Separator
|
||||
except ModuleNotFoundError:
|
||||
inquirer = None
|
||||
import argparse
|
||||
import os
|
||||
from typing import List
|
||||
@@ -21,45 +24,29 @@ from stuff.fdroidpriv import FDroidPriv
|
||||
import tools.helper as helper
|
||||
from tools import container
|
||||
from tools import images
|
||||
|
||||
import argparse
|
||||
|
||||
from tools.logger import Logger
|
||||
|
||||
|
||||
def get_certified(args):
|
||||
def get_certified():
|
||||
AndroidId().get_id()
|
||||
|
||||
|
||||
def mount(partition, copy_dir):
|
||||
img = os.path.join(images.get_image_dir(), partition+".img")
|
||||
mount_point = ""
|
||||
if partition == "system":
|
||||
mount_point = os.path.join(copy_dir)
|
||||
else:
|
||||
mount_point = os.path.join(copy_dir, partition)
|
||||
Logger.info("Mounting {} to {}".format(img, mount_point))
|
||||
img = os.path.join(images.get_image_dir(), f"{partition}.img")
|
||||
mount_point = os.path.join(copy_dir) if partition == "system" else os.path.join(copy_dir, partition)
|
||||
Logger.info(f"Mounting {img} to {mount_point}")
|
||||
images.mount(img, mount_point)
|
||||
|
||||
|
||||
def resize(partition):
|
||||
img = os.path.join(images.get_image_dir(), partition+".img")
|
||||
img_size = int(os.path.getsize(img)/(1024*1024))
|
||||
new_size = "{}M".format(img_size+500)
|
||||
Logger.info("Resizing {} to {}".format(img, new_size))
|
||||
img = os.path.join(images.get_image_dir(), f"{partition}.img")
|
||||
img_size = int(os.path.getsize(img) / (1024 * 1024))
|
||||
new_size = f"{img_size + 500}M"
|
||||
Logger.info(f"Resizing {img} to {new_size}")
|
||||
images.resize(img, new_size)
|
||||
|
||||
|
||||
def umount(partition, copy_dir):
|
||||
mount_point = ""
|
||||
if partition == "system":
|
||||
mount_point = os.path.join(copy_dir)
|
||||
else:
|
||||
mount_point = os.path.join(copy_dir, partition)
|
||||
Logger.info("Umounting {}".format(mount_point))
|
||||
mount_point = os.path.join(copy_dir) if partition == "system" else os.path.join(copy_dir, partition)
|
||||
Logger.info(f"Unmounting {mount_point}")
|
||||
images.umount(mount_point)
|
||||
|
||||
|
||||
def install_app(args):
|
||||
install_list: List[General] = []
|
||||
app = args.app
|
||||
@@ -118,7 +105,6 @@ def install_app(args):
|
||||
|
||||
container.upgrade()
|
||||
|
||||
|
||||
def remove_app(args):
|
||||
remove_list: List[General] = []
|
||||
app = args.app
|
||||
@@ -158,10 +144,8 @@ def remove_app(args):
|
||||
|
||||
container.upgrade()
|
||||
|
||||
|
||||
def hack_option(args):
|
||||
Logger.warning(
|
||||
"If these hacks cause any problems, run `sudo python main.py remove <hack_option>` to remove")
|
||||
Logger.warning("If these hacks cause any problems, run `sudo python main.py remove <hack_option>` to remove")
|
||||
|
||||
hack_list: List[General] = []
|
||||
options = args.option_name
|
||||
@@ -198,10 +182,11 @@ def hack_option(args):
|
||||
|
||||
container.upgrade()
|
||||
|
||||
|
||||
def interact():
|
||||
if inquirer is None:
|
||||
print('Please, install InquirerPy module first')
|
||||
return
|
||||
os.system("clear")
|
||||
args = argparse.Namespace()
|
||||
android_version = inquirer.select(
|
||||
message="Select Android version",
|
||||
instruction="(\u2191\u2193 Select Item)",
|
||||
@@ -214,15 +199,12 @@ def interact():
|
||||
).execute()
|
||||
if not android_version:
|
||||
exit()
|
||||
args.android_version = android_version
|
||||
|
||||
args = argparse.Namespace(android_version=android_version, microg_variant="Standard")
|
||||
|
||||
action = inquirer.select(
|
||||
message="Please select an action",
|
||||
choices=[
|
||||
"Install",
|
||||
"Remove",
|
||||
"Hack",
|
||||
"Get Google Device ID to Get Certified"
|
||||
],
|
||||
choices=["Install", "Remove", "Hack", "Get Google Device ID to Get Certified"],
|
||||
instruction="([↑↓]: Select Item)",
|
||||
default=None,
|
||||
).execute()
|
||||
@@ -231,7 +213,7 @@ def interact():
|
||||
|
||||
install_choices = ["gapps", "microg", "libndk", "libhoudini", "magisk", "smartdock", "fdroidpriv",]
|
||||
hack_choices = []
|
||||
if android_version=="11":
|
||||
if android_version == "11":
|
||||
install_choices.extend(["widevine"])
|
||||
hack_choices.extend(["nodataperm", "hidestatusbar"])
|
||||
|
||||
@@ -243,8 +225,7 @@ def interact():
|
||||
invalid_message="should be at least 1 selection",
|
||||
choices=install_choices
|
||||
).execute()
|
||||
microg_variants = ["Standard", "NoGoolag",
|
||||
"UNLP", "Minimal", "MinimalIAP"]
|
||||
microg_variants = ["Standard", "NoGoolag", "UNLP", "Minimal", "MinimalIAP"]
|
||||
if "microg" in apps:
|
||||
microg_variant = inquirer.select(
|
||||
message="Select MicroG variant",
|
||||
@@ -263,7 +244,7 @@ def interact():
|
||||
choices=[*install_choices, *hack_choices]
|
||||
).execute()
|
||||
args.app = apps
|
||||
args.microg_variant="Standard"
|
||||
args.microg_variant = "Standard"
|
||||
remove_app(args)
|
||||
elif action == "Hack":
|
||||
apps = inquirer.checkbox(
|
||||
@@ -276,15 +257,14 @@ def interact():
|
||||
args.option_name = apps
|
||||
hack_option(args)
|
||||
elif action == "Get Google Device ID to Get Certified":
|
||||
AndroidId().get_id()
|
||||
|
||||
get_certified()
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(description='''
|
||||
Does stuff like installing Gapps, installing Magisk, installing NDK Translation and getting Android ID for device registration.
|
||||
Use -h flag for help!''')
|
||||
|
||||
subparsers = parser.add_subparsers(title="coomand", dest='command')
|
||||
subparsers = parser.add_subparsers(title="command", dest='command')
|
||||
parser.add_argument('-a', '--android-version',
|
||||
dest='android_version',
|
||||
help='Specify the Android version',
|
||||
@@ -296,8 +276,7 @@ def main():
|
||||
'certified', help='Get device ID to obtain Play Store certification')
|
||||
certified.set_defaults(func=get_certified)
|
||||
|
||||
install_choices = ["gapps", "microg", "libndk", "libhoudini",
|
||||
"magisk", "mitm", "smartdock", "widevine"]
|
||||
install_choices = ["gapps", "microg", "libndk", "libhoudini", "magisk", "mitm", "smartdock", "widevine"]
|
||||
hack_choices = ["nodataperm", "hidestatusbar"]
|
||||
micrg_variants = ["Standard", "NoGoolag", "UNLP", "Minimal", "MinimalIAP"]
|
||||
remove_choices = install_choices
|
||||
@@ -306,7 +285,6 @@ def main():
|
||||
"dest": "app",
|
||||
"type": str,
|
||||
"nargs": '+',
|
||||
# "metavar":"",
|
||||
}
|
||||
|
||||
install_help = """
|
||||
@@ -344,15 +322,12 @@ widevine: Add support for widevine DRM L3
|
||||
hack_parser.set_defaults(func=hack_option)
|
||||
|
||||
args = parser.parse_args()
|
||||
args.microg_variant = "Standard"
|
||||
if hasattr(args, 'func'):
|
||||
args_dict = vars(args)
|
||||
helper.check_root()
|
||||
args.func(args)
|
||||
else:
|
||||
helper.check_root()
|
||||
interact()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
{ lib,
|
||||
stdenvNoCC,
|
||||
lzip,
|
||||
python3,
|
||||
makeWrapper }:
|
||||
let
|
||||
wrappedPath = lib.makeBinPath [ lzip ];
|
||||
in stdenvNoCC.mkDerivation {
|
||||
name = "waydroid_script";
|
||||
|
||||
buildInputs = [
|
||||
(python3.withPackages(ps: with ps; [ tqdm requests inquirerpy ]))
|
||||
];
|
||||
|
||||
nativeBuildInputs = [
|
||||
makeWrapper
|
||||
];
|
||||
|
||||
src = ./.;
|
||||
|
||||
postPatch = ''
|
||||
patchShebangs main.py
|
||||
'';
|
||||
|
||||
installPhase = ''
|
||||
mkdir -p $out/libexec
|
||||
cp -r . $out/libexec/waydroid_script
|
||||
mkdir -p $out/bin
|
||||
makeShellWrapper $out/libexec/waydroid_script/main.py $out/bin/waydroid_script \
|
||||
--prefix PATH : "${wrappedPath}"
|
||||
'';
|
||||
}
|
||||
@@ -1,3 +1,2 @@
|
||||
tqdm
|
||||
requests
|
||||
InquirerPy
|
||||
|
||||
@@ -36,11 +36,11 @@ class MicroG(General):
|
||||
"6136b383153c2a6797d14fb4d7ca3f97"
|
||||
],
|
||||
"Minimal": [
|
||||
"https://github.com/ayasa520/MinMicroG/releases/download/latest/MinMicroG-Minimal-2.11.1-20230429100521.zip"
|
||||
"https://github.com/ayasa520/MinMicroG/releases/download/latest/MinMicroG-Minimal-2.11.1-20230429100521.zip",
|
||||
"afb87eb64e7749cfd72c4760d85849da"
|
||||
],
|
||||
"MinimalIAP": [
|
||||
"https://github.com/ayasa520/MinMicroG/releases/download/latest/MinMicroG-MinimalIAP-2.11.1-20230429100556.zip"
|
||||
"https://github.com/ayasa520/MinMicroG/releases/download/latest/MinMicroG-MinimalIAP-2.11.1-20230429100556.zip",
|
||||
"cc071f4f776cbc16c4c1f707aff1f7fa"
|
||||
]
|
||||
}
|
||||
|
||||
@@ -49,4 +49,4 @@ class Ndk(General):
|
||||
Logger.info("Copying libndk library files ...")
|
||||
name = re.findall("([a-zA-Z0-9]+)\.zip", self.dl_link)[0]
|
||||
shutil.copytree(os.path.join(self.extract_to, "vendor_google_proprietary_ndk_translation-prebuilt-" + name,
|
||||
"prebuilts"), os.path.join(self.copy_dir, self.partition), dirs_exist_ok=True)
|
||||
"prebuilts"), os.path.join(self.copy_dir, self.partition), dirs_exist_ok=True)
|
||||
|
||||
@@ -40,7 +40,7 @@ class Nodataperm(General):
|
||||
self.act_md5 = self.dl_links[android_version][arch][1]
|
||||
|
||||
def copy(self):
|
||||
name = re.findall("([a-zA-Z0-9]+)\.zip", self.dl_link)[0]
|
||||
name = re.findall("([a-zA-Z0-9]+)\\.zip", self.dl_link)[0]
|
||||
extract_path = os.path.join(
|
||||
self.extract_to, f"hack_full_data_permission-{name}")
|
||||
if not container.use_overlayfs():
|
||||
|
||||
@@ -4,10 +4,10 @@ from stuff.general import General
|
||||
|
||||
class Smartdock(General):
|
||||
id = "smartdock"
|
||||
dl_link = "https://f-droid.org/repo/cu.axel.smartdock_1100.apk"
|
||||
dl_link = "https://f-droid.org/repo/cu.axel.smartdock_1130.apk"
|
||||
partition = "system"
|
||||
dl_file_name = "smartdock.apk"
|
||||
act_md5 = "f4087d34218eac902a5cca98ee03d215"
|
||||
act_md5 = "6bfedb959ef5855c3782e8001cb67f86"
|
||||
apply_props = { "qemu.hw.mainkeys" : "1" }
|
||||
skip_extract = True
|
||||
permissions = """<?xml version="1.0" encoding="utf-8"?>
|
||||
@@ -30,6 +30,7 @@ class Smartdock(General):
|
||||
<permission name="android.permission.ACCESS_SUPERUSER"/>
|
||||
<permission name="android.permission.PACKAGE_USAGE_STATS" />
|
||||
<permission name="android.permission.QUERY_ALL_PACKAGES" />
|
||||
<permission name="android.permission.WRITE_SECURE_SETTINGS" />
|
||||
</privapp-permissions>
|
||||
</permissions>
|
||||
"""
|
||||
|
||||
@@ -40,7 +40,7 @@ class Widevine(General):
|
||||
self.act_md5 = self.dl_links[self.arch[0]][android_version][1]
|
||||
|
||||
def copy(self):
|
||||
name = re.findall("([a-zA-Z0-9]+)\.zip", self.dl_link)[0]
|
||||
name = re.findall("([a-zA-Z0-9]+)\\.zip", self.dl_link)[0]
|
||||
Logger.info("Copying widevine library files ...")
|
||||
shutil.copytree(os.path.join(self.extract_to, "vendor_google_proprietary_widevine-prebuilt-"+name,
|
||||
"prebuilts"), os.path.join(self.copy_dir, self.partition), dirs_exist_ok=True)
|
||||
|
||||
@@ -22,7 +22,7 @@ def use_overlayfs():
|
||||
cfg = configparser.ConfigParser()
|
||||
cfg_file = os.environ.get("WAYDROID_CONFIG", "/var/lib/waydroid/waydroid.cfg")
|
||||
if not os.path.isfile(cfg_file):
|
||||
Logger.error("Cannot locate waydroid config file, reinit wayland and try again!")
|
||||
Logger.error("Cannot locate waydroid config file, reinit waydroid and try again!")
|
||||
sys.exit(1)
|
||||
cfg.read(cfg_file)
|
||||
if "waydroid" not in cfg:
|
||||
@@ -50,4 +50,4 @@ def is_running():
|
||||
return "Session:\tRUNNING" in run(["waydroid", "status"]).stdout.decode()
|
||||
|
||||
def upgrade():
|
||||
run(["waydroid", "upgrade", "-o"], ignore=r"\[.*\] Stopping container\n\[.*\] Starting container")
|
||||
run(["waydroid", "upgrade", "-o"], ignore=r"\[.*\] Stopping container\n\[.*\] Starting container")
|
||||
|
||||
@@ -32,7 +32,7 @@ def get_image_dir():
|
||||
cfg = configparser.ConfigParser()
|
||||
cfg_file = os.environ.get("WAYDROID_CONFIG", "/var/lib/waydroid/waydroid.cfg")
|
||||
if not os.path.isfile(cfg_file):
|
||||
Logger.error("Cannot locate waydroid config file, reinit wayland and try again!")
|
||||
Logger.error("Cannot locate waydroid config file, reinit waydroid and try again!")
|
||||
sys.exit(1)
|
||||
cfg.read(cfg_file)
|
||||
if "waydroid" not in cfg:
|
||||
|
||||