You've already forked Magisk
mirror of
https://github.com/topjohnwu/Magisk.git
synced 2025-09-06 06:36:58 +00:00
Merge Magisk install zip into Magisk Manager
Distribute Magisk directly with Magisk Manager APK. The APK will contain all required binaries and scripts for installation and uninstallation. App versions will now align with Magisk releases. Extra effort is spent to make the APK itself also a flashable zip that can be used in custom recoveries, so those still prefer to install Magisk with recoveries will not be affected with this change. As a bonus, this makes the whole installation and uninstallation process 100% offline. The existing Magisk Manager was not really functional without an Internet connection, as the installation process was highly tied to zips hosted on the server. An additional bonus: since all binaries are now shipped as "native libraries" of the APK, we can finally bump the target SDK version higher than 28. The target SDK version was stuck at 28 for a long time because newer SELinux restricts running executables from internal storage. More details can be found here: https://github.com/termux/termux-app/issues/1072 The target SDK bump will be addressed in a future commit. Co-authored with @vvb2060
This commit is contained in:
@@ -25,306 +25,217 @@
|
||||
app:fitsSystemWindowsInsets="top|bottom"
|
||||
tools:paddingTop="24dp">
|
||||
|
||||
<FrameLayout
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
android:layout_height="wrap_content"
|
||||
android:clipToPadding="false"
|
||||
android:orientation="vertical"
|
||||
android:paddingTop="@dimen/l_50">
|
||||
|
||||
<LinearLayout
|
||||
goneUnless="@{viewModel.loaded && viewModel.progress <= 0}"
|
||||
<com.google.android.material.card.MaterialCardView
|
||||
style="@style/WidgetFoundation.Card"
|
||||
gone="@{viewModel.skipOptions}"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:clipToPadding="false"
|
||||
android:orientation="vertical"
|
||||
android:paddingTop="@dimen/l_50">
|
||||
|
||||
<com.google.android.material.card.MaterialCardView
|
||||
style="@style/WidgetFoundation.Card"
|
||||
gone="@{viewModel.skipOptions}"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="@dimen/l1"
|
||||
android:layout_marginEnd="@dimen/l1"
|
||||
android:focusable="false">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<ImageView
|
||||
style="@style/WidgetFoundation.Icon"
|
||||
isSelected="@{viewModel.step > 0}"
|
||||
android:layout_marginStart="@dimen/l_25"
|
||||
app:srcCompat="@drawable/ic_check_circle_md2" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_marginStart="@dimen/l1"
|
||||
android:layout_weight="1"
|
||||
android:gravity="center_vertical"
|
||||
android:text="@string/install_options_title"
|
||||
android:textAppearance="@style/AppearanceFoundation.Body"
|
||||
android:textStyle="bold" />
|
||||
|
||||
<Button
|
||||
style="@style/WidgetFoundation.Button.Text"
|
||||
gone="@{viewModel.step != 0}"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:onClick="@{() -> viewModel.step(1)}"
|
||||
android:text="@string/install_next" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
gone="@{viewModel.step != 0}"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="@dimen/l1"
|
||||
android:layout_marginTop="@dimen/l_50"
|
||||
android:layout_marginEnd="@dimen/l1"
|
||||
android:layout_marginBottom="@dimen/l_50"
|
||||
android:orientation="vertical"
|
||||
android:paddingStart="3dp"
|
||||
android:paddingEnd="3dp"
|
||||
tools:layout_gravity="center">
|
||||
|
||||
<CheckBox
|
||||
style="@style/WidgetFoundation.Checkbox"
|
||||
gone="@{Info.isSAR}"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:checked="@={Config.keepVerity}"
|
||||
android:text="@string/keep_dm_verity"
|
||||
tools:checked="true" />
|
||||
|
||||
<CheckBox
|
||||
style="@style/WidgetFoundation.Checkbox"
|
||||
goneUnless="@{Info.isFDE}"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:checked="@={Config.keepEnc}"
|
||||
android:text="@string/keep_force_encryption"
|
||||
app:tint="?colorPrimary" />
|
||||
|
||||
<CheckBox
|
||||
style="@style/WidgetFoundation.Checkbox"
|
||||
gone="@{Info.ramdisk}"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:checked="@={Config.recovery}"
|
||||
android:text="@string/recovery_mode"
|
||||
app:tint="?colorPrimary" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</com.google.android.material.card.MaterialCardView>
|
||||
|
||||
<com.google.android.material.card.MaterialCardView
|
||||
style="@style/WidgetFoundation.Card"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="@dimen/l1"
|
||||
android:layout_marginTop="@dimen/l1"
|
||||
android:layout_marginEnd="@dimen/l1"
|
||||
android:focusable="false">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<ImageView
|
||||
style="@style/WidgetFoundation.Icon"
|
||||
isSelected="@{viewModel.step > 1}"
|
||||
android:layout_marginStart="@dimen/l_25"
|
||||
app:srcCompat="@drawable/ic_check_circle_md2" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_marginStart="@dimen/l1"
|
||||
android:layout_weight="1"
|
||||
android:gravity="center_vertical"
|
||||
android:text="@string/install_method_title"
|
||||
android:textAppearance="@style/AppearanceFoundation.Body"
|
||||
android:textStyle="bold" />
|
||||
|
||||
<Button
|
||||
style="@style/WidgetFoundation.Button.Text"
|
||||
gone="@{viewModel.step != 1}"
|
||||
isEnabled="@{viewModel.method == @id/method_patch ? viewModel.data != null : viewModel.method != -1}"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:onClick="@{() -> viewModel.install()}"
|
||||
android:text="@string/install_start"
|
||||
app:icon="@drawable/ic_forth_md2"
|
||||
app:iconGravity="textEnd" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<RadioGroup
|
||||
gone="@{viewModel.step != 1}"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="@dimen/l1"
|
||||
android:layout_marginTop="@dimen/l_50"
|
||||
android:layout_marginEnd="@dimen/l1"
|
||||
android:layout_marginBottom="@dimen/l_50"
|
||||
android:checkedButton="@={viewModel.method}"
|
||||
android:paddingStart="3dp"
|
||||
android:paddingEnd="3dp">
|
||||
|
||||
<RadioButton
|
||||
android:id="@+id/method_download"
|
||||
style="@style/WidgetFoundation.RadioButton"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/download_zip_only"
|
||||
tools:checked="true" />
|
||||
|
||||
<RadioButton
|
||||
android:id="@+id/method_patch"
|
||||
style="@style/WidgetFoundation.RadioButton"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/select_patch_file" />
|
||||
|
||||
<RadioButton
|
||||
android:id="@+id/method_direct"
|
||||
style="@style/WidgetFoundation.RadioButton"
|
||||
gone="@{!viewModel.rooted}"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/direct_install" />
|
||||
|
||||
<RadioButton
|
||||
android:id="@+id/method_inactive_slot"
|
||||
style="@style/WidgetFoundation.RadioButton"
|
||||
gone="@{viewModel.noSecondSlot}"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/install_inactive_slot" />
|
||||
|
||||
</RadioGroup>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</com.google.android.material.card.MaterialCardView>
|
||||
|
||||
<com.google.android.material.card.MaterialCardView
|
||||
style="@style/WidgetFoundation.Card"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="@dimen/l1"
|
||||
android:layout_marginStart="@dimen/l1"
|
||||
android:layout_marginEnd="@dimen/l1"
|
||||
android:focusable="false">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/release_notes"
|
||||
markdownText="@{viewModel.notes}"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_margin="15dp"
|
||||
android:textAppearance="@style/AppearanceFoundation.Caption"
|
||||
android:visibility="gone"
|
||||
tools:maxLines="5"
|
||||
tools:text="@tools:sample/lorem/random"
|
||||
tools:visibility="visible" />
|
||||
|
||||
</com.google.android.material.card.MaterialCardView>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
goneUnless="@{viewModel.loading}"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_gravity="center"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="@dimen/l1"
|
||||
android:layout_marginEnd="@dimen/l1"
|
||||
android:gravity="center"
|
||||
android:orientation="vertical">
|
||||
android:focusable="false">
|
||||
|
||||
<TextView
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="center"
|
||||
android:text="@string/loading"
|
||||
android:textAppearance="@style/AppearanceFoundation.Title" />
|
||||
android:orientation="vertical">
|
||||
|
||||
<FrameLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="@dimen/l1">
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<ProgressBar
|
||||
style="@style/WidgetFoundation.ProgressBar"
|
||||
invisible="@{viewModel.progress <= 0}"
|
||||
android:layout_width="100dp"
|
||||
android:layout_gravity="center"
|
||||
android:progress="@{viewModel.progress}" />
|
||||
<ImageView
|
||||
style="@style/WidgetFoundation.Icon"
|
||||
isSelected="@{viewModel.step > 0}"
|
||||
android:layout_marginStart="@dimen/l_25"
|
||||
app:srcCompat="@drawable/ic_check_circle_md2" />
|
||||
|
||||
<ProgressBar
|
||||
style="@style/WidgetFoundation.ProgressBar.Indeterminate"
|
||||
invisibleUnless="@{viewModel.progress <= 0}"
|
||||
android:layout_width="100dp"
|
||||
android:layout_gravity="center" />
|
||||
<TextView
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_marginStart="@dimen/l1"
|
||||
android:layout_weight="1"
|
||||
android:gravity="center_vertical"
|
||||
android:text="@string/install_options_title"
|
||||
android:textAppearance="@style/AppearanceFoundation.Body"
|
||||
android:textStyle="bold" />
|
||||
|
||||
</FrameLayout>
|
||||
<Button
|
||||
style="@style/WidgetFoundation.Button.Text"
|
||||
gone="@{viewModel.step != 0}"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:onClick="@{() -> viewModel.step(1)}"
|
||||
android:text="@string/install_next" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</LinearLayout>
|
||||
<LinearLayout
|
||||
gone="@{viewModel.step != 0}"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="@dimen/l1"
|
||||
android:layout_marginTop="@dimen/l_50"
|
||||
android:layout_marginEnd="@dimen/l1"
|
||||
android:layout_marginBottom="@dimen/l_50"
|
||||
android:orientation="vertical"
|
||||
android:paddingStart="3dp"
|
||||
android:paddingEnd="3dp"
|
||||
tools:layout_gravity="center">
|
||||
|
||||
<LinearLayout
|
||||
goneUnless="@{viewModel.loaded && viewModel.progress >= 100}"
|
||||
<CheckBox
|
||||
style="@style/WidgetFoundation.Checkbox"
|
||||
gone="@{Info.isSAR}"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:checked="@={Config.keepVerity}"
|
||||
android:text="@string/keep_dm_verity"
|
||||
tools:checked="true" />
|
||||
|
||||
<CheckBox
|
||||
style="@style/WidgetFoundation.Checkbox"
|
||||
goneUnless="@{Info.isFDE}"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:checked="@={Config.keepEnc}"
|
||||
android:text="@string/keep_force_encryption"
|
||||
app:tint="?colorPrimary" />
|
||||
|
||||
<CheckBox
|
||||
style="@style/WidgetFoundation.Checkbox"
|
||||
gone="@{Info.ramdisk}"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:checked="@={Config.recovery}"
|
||||
android:text="@string/recovery_mode"
|
||||
app:tint="?colorPrimary" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</com.google.android.material.card.MaterialCardView>
|
||||
|
||||
<com.google.android.material.card.MaterialCardView
|
||||
style="@style/WidgetFoundation.Card"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center"
|
||||
android:gravity="center"
|
||||
android:orientation="vertical">
|
||||
android:layout_marginStart="@dimen/l1"
|
||||
android:layout_marginTop="@dimen/l1"
|
||||
android:layout_marginEnd="@dimen/l1"
|
||||
android:focusable="false">
|
||||
|
||||
<TextView
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="center"
|
||||
android:text="@string/download_complete"
|
||||
android:textAppearance="@style/AppearanceFoundation.Title" />
|
||||
android:orientation="vertical">
|
||||
|
||||
</LinearLayout>
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<LinearLayout
|
||||
goneUnless="@{viewModel.loadFailed}"
|
||||
<ImageView
|
||||
style="@style/WidgetFoundation.Icon"
|
||||
isSelected="@{viewModel.step > 1}"
|
||||
android:layout_marginStart="@dimen/l_25"
|
||||
app:srcCompat="@drawable/ic_check_circle_md2" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_marginStart="@dimen/l1"
|
||||
android:layout_weight="1"
|
||||
android:gravity="center_vertical"
|
||||
android:text="@string/install_method_title"
|
||||
android:textAppearance="@style/AppearanceFoundation.Body"
|
||||
android:textStyle="bold" />
|
||||
|
||||
<Button
|
||||
style="@style/WidgetFoundation.Button.Text"
|
||||
gone="@{viewModel.step != 1}"
|
||||
isEnabled="@{viewModel.method == @id/method_patch ? viewModel.data != null : viewModel.method != -1}"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:onClick="@{() -> viewModel.install()}"
|
||||
android:text="@string/install_start"
|
||||
app:icon="@drawable/ic_forth_md2"
|
||||
app:iconGravity="textEnd" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<RadioGroup
|
||||
gone="@{viewModel.step != 1}"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="@dimen/l1"
|
||||
android:layout_marginTop="@dimen/l_50"
|
||||
android:layout_marginEnd="@dimen/l1"
|
||||
android:layout_marginBottom="@dimen/l_50"
|
||||
android:checkedButton="@={viewModel.method}"
|
||||
android:paddingStart="3dp"
|
||||
android:paddingEnd="3dp">
|
||||
|
||||
<RadioButton
|
||||
android:id="@+id/method_patch"
|
||||
style="@style/WidgetFoundation.RadioButton"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/select_patch_file" />
|
||||
|
||||
<RadioButton
|
||||
android:id="@+id/method_direct"
|
||||
style="@style/WidgetFoundation.RadioButton"
|
||||
gone="@{!viewModel.rooted}"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/direct_install" />
|
||||
|
||||
<RadioButton
|
||||
android:id="@+id/method_inactive_slot"
|
||||
style="@style/WidgetFoundation.RadioButton"
|
||||
gone="@{viewModel.noSecondSlot}"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/install_inactive_slot" />
|
||||
|
||||
</RadioGroup>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</com.google.android.material.card.MaterialCardView>
|
||||
|
||||
<com.google.android.material.card.MaterialCardView
|
||||
style="@style/WidgetFoundation.Card"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center"
|
||||
android:gravity="center"
|
||||
android:orientation="vertical">
|
||||
android:layout_marginTop="@dimen/l1"
|
||||
android:layout_marginStart="@dimen/l1"
|
||||
android:layout_marginEnd="@dimen/l1"
|
||||
android:focusable="false">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/release_notes"
|
||||
markdownText="@{viewModel.notes}"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="center"
|
||||
android:text="@string/download_file_error"
|
||||
android:textAppearance="@style/AppearanceFoundation.Title" />
|
||||
android:layout_margin="15dp"
|
||||
android:textAppearance="@style/AppearanceFoundation.Caption"
|
||||
android:visibility="gone"
|
||||
tools:maxLines="5"
|
||||
tools:text="@tools:sample/lorem/random"
|
||||
tools:visibility="visible" />
|
||||
|
||||
</LinearLayout>
|
||||
</com.google.android.material.card.MaterialCardView>
|
||||
|
||||
</FrameLayout>
|
||||
</LinearLayout>
|
||||
|
||||
</androidx.core.widget.NestedScrollView>
|
||||
|
||||
|
||||
@@ -49,10 +49,6 @@
|
||||
android:label="FlashFragment"
|
||||
tools:layout="@layout/fragment_flash_md2">
|
||||
|
||||
<argument
|
||||
android:name="installer"
|
||||
app:argType="android.net.Uri" />
|
||||
|
||||
<argument
|
||||
android:name="action"
|
||||
app:argType="string" />
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
/util_functions.sh
|
||||
@@ -13,21 +13,35 @@ env_check() {
|
||||
return 0
|
||||
}
|
||||
|
||||
fix_env() {
|
||||
cd $MAGISKBIN
|
||||
PATH=/system/bin /system/bin/sh update-binary -x
|
||||
./busybox rm -f update-binary magisk.apk
|
||||
./busybox chmod -R 755 .
|
||||
./magiskinit -x magisk magisk
|
||||
cp_readlink() {
|
||||
if [ -z $2 ]; then
|
||||
cd $1
|
||||
else
|
||||
cp -af $1/. $2
|
||||
cd $2
|
||||
fi
|
||||
for file in *; do
|
||||
if [ -L $file ]; then
|
||||
local full=$(readlink -f $file)
|
||||
rm $file
|
||||
cp -af $full $file
|
||||
fi
|
||||
done
|
||||
chmod -R 755 .
|
||||
cd /
|
||||
}
|
||||
|
||||
direct_install() {
|
||||
rm -rf $MAGISKBIN/* 2>/dev/null
|
||||
fix_env() {
|
||||
# Cleanup and make dirs
|
||||
rm -rf $MAGISKBIN/*
|
||||
mkdir -p $MAGISKBIN 2>/dev/null
|
||||
chmod 700 $NVBASE
|
||||
cp -af $1/. $MAGISKBIN
|
||||
rm -f $MAGISKBIN/new-boot.img
|
||||
rm -rf $1
|
||||
chown -R 0:0 $MAGISKBIN
|
||||
}
|
||||
|
||||
direct_install() {
|
||||
echo "- Flashing new boot image"
|
||||
flash_image $1/new-boot.img $2
|
||||
case $? in
|
||||
@@ -40,10 +54,20 @@ direct_install() {
|
||||
return 2
|
||||
;;
|
||||
esac
|
||||
rm -rf $1
|
||||
|
||||
rm -f $1/new-boot.img
|
||||
fix_env $1
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
run_uninstaller() {
|
||||
rm -rf /dev/tmp
|
||||
mkdir -p /dev/tmp/install
|
||||
unzip -o "$1" "assets/*" "lib/*" -d /dev/tmp/install
|
||||
INSTALLER=/dev/tmp/install sh /dev/tmp/install/assets/magisk_uninstaller.sh dummy 1 "$1"
|
||||
}
|
||||
|
||||
restore_imgs() {
|
||||
[ -z $SHA1 ] && return 1
|
||||
local BACKUPDIR=/data/magisk_backup_$SHA1
|
||||
|
||||
Reference in New Issue
Block a user