├── .gitignore ├── .idea ├── .gitignore ├── compiler.xml ├── gradle.xml ├── misc.xml └── vcs.xml ├── README.md ├── app ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src │ └── main │ ├── AndroidManifest.xml │ ├── java │ └── com │ │ └── guisei │ │ └── bluearchiveuncensored │ │ ├── MainActivity.java │ │ ├── config │ │ └── PackEnum.java │ │ ├── dialog │ │ └── ChannelDialog.java │ │ ├── util │ │ └── PackUtil.java │ │ └── view │ │ └── GifView.java │ └── res │ ├── drawable-xxhdpi │ ├── pic1.png │ └── pic2.png │ ├── drawable │ ├── bg_dialog.xml │ └── btn_main.xml │ ├── layout │ ├── activity_main.xml │ ├── dialog_channel.xml │ └── item_channel.xml │ ├── mipmap-hdpi │ ├── ic_launcher.png │ └── ic_launcher_round.png │ ├── mipmap-mdpi │ ├── ic_launcher.png │ └── ic_launcher_round.png │ ├── mipmap-xhdpi │ ├── ic_launcher.png │ └── ic_launcher_round.png │ ├── mipmap-xxhdpi │ ├── ic_launcher.png │ └── ic_launcher_round.png │ ├── mipmap-xxxhdpi │ ├── ic_launcher.png │ └── ic_launcher_round.png │ └── values │ └── themes.xml ├── build.gradle ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat └── settings.gradle /.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .gradle 3 | /local.properties 4 | /.idea/caches 5 | /.idea/libraries 6 | /.idea/modules.xml 7 | /.idea/workspace.xml 8 | /.idea/navEditor.xml 9 | /.idea/assetWizardSettings.xml 10 | .DS_Store 11 | /build 12 | /captures 13 | .externalNativeBuild 14 | .cxx 15 | local.properties 16 | /keystore.jks 17 | /app/release -------------------------------------------------------------------------------- /.idea/.gitignore: -------------------------------------------------------------------------------- 1 | # Default ignored files 2 | /shelf/ 3 | /workspace.xml 4 | -------------------------------------------------------------------------------- /.idea/compiler.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /.idea/gradle.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 19 | 20 | -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 26 | 45 | 46 | 47 | 48 | 49 | 50 | 52 | -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # 蔚蓝档案反和谐 2 | 3 | Android 14 某次更新之后应该是不行了,改不了 Android/data 下的东西,不然就要用 shizuku 4 | 5 | shizuku 都用了,自然也就不需要这个工具了,下个 MT 管理器之类的自己改就好了 6 | 7 | --- 8 | 9 | 作用仅仅是帮你替换 LocalizeConfig.txt 而已,和你手动在文件管理器替换没有任何区别 10 | 11 | 之所以做这个APP,是为了方便懒人 12 | 13 | ~~不是所有手机都有能修改 /sdcard/Android/data/ 目录的文件管理器,而且可能会忘记包名和需要修改的地方~~ 14 | 15 | 用这个 APP 点一下按钮就行,反和谐完了之后就可以卸载掉了,并不会影响游戏运行 16 | 17 | 替换完成之后,重启游戏,应该要下载 130MB 左右的数据包。可能会卡在初始化资源,多等一会儿就好 18 | 19 | 支持安卓版本 5.0 ~ 14,自己试过 5.0 和 7.1 和 14,其他的应该都没问题 20 | -------------------------------------------------------------------------------- /app/.gitignore: -------------------------------------------------------------------------------- 1 | /build -------------------------------------------------------------------------------- /app/build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id 'com.android.application' 3 | } 4 | 5 | android { 6 | namespace 'com.guisei.bluearchiveuncensored' 7 | compileSdk 33 8 | 9 | defaultConfig { 10 | applicationId "com.guisei.bluearchiveuncensored" 11 | minSdk 21 12 | targetSdk 33 13 | versionCode 1 14 | versionName "1.0" 15 | } 16 | 17 | buildTypes { 18 | release { 19 | minifyEnabled false 20 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' 21 | } 22 | } 23 | compileOptions { 24 | sourceCompatibility JavaVersion.VERSION_1_8 25 | targetCompatibility JavaVersion.VERSION_1_8 26 | } 27 | } 28 | 29 | dependencies { 30 | implementation "androidx.documentfile:documentfile:1.0.1" 31 | } -------------------------------------------------------------------------------- /app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # You can control the set of applied configuration files using the 3 | # proguardFiles setting in build.gradle. 4 | # 5 | # For more details, see 6 | # http://developer.android.com/guide/developing/tools/proguard.html 7 | 8 | # If your project uses WebView with JS, uncomment the following 9 | # and specify the fully qualified class name to the JavaScript interface 10 | # class: 11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 12 | # public *; 13 | #} 14 | 15 | # Uncomment this to preserve the line number information for 16 | # debugging stack traces. 17 | #-keepattributes SourceFile,LineNumberTable 18 | 19 | # If you keep the line number information, uncomment this to 20 | # hide the original source file name. 21 | #-renamesourcefileattribute SourceFile -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 17 | 18 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /app/src/main/java/com/guisei/bluearchiveuncensored/MainActivity.java: -------------------------------------------------------------------------------- 1 | package com.guisei.bluearchiveuncensored; 2 | 3 | import android.Manifest; 4 | import android.app.Activity; 5 | import android.app.AlertDialog; 6 | import android.content.Intent; 7 | import android.content.UriPermission; 8 | import android.content.pm.PackageInfo; 9 | import android.content.pm.PackageManager; 10 | import android.net.Uri; 11 | import android.os.Build; 12 | import android.os.Bundle; 13 | import android.provider.DocumentsContract; 14 | import android.widget.ImageView; 15 | import android.widget.Toast; 16 | 17 | import androidx.documentfile.provider.DocumentFile; 18 | 19 | import com.guisei.bluearchiveuncensored.config.PackEnum; 20 | import com.guisei.bluearchiveuncensored.dialog.ChannelDialog; 21 | import com.guisei.bluearchiveuncensored.util.PackUtil; 22 | 23 | import java.io.File; 24 | import java.io.OutputStream; 25 | import java.util.ArrayList; 26 | import java.util.List; 27 | 28 | public class MainActivity extends Activity { 29 | 30 | private static final int REQUEST_ANDROID_DATA = 101; 31 | private static final int REQUEST_STORAGE_PERMISSION = 102; 32 | private static final String[] PERMISSIONS_STORAGE = { 33 | Manifest.permission.READ_EXTERNAL_STORAGE, 34 | Manifest.permission.WRITE_EXTERNAL_STORAGE 35 | }; 36 | 37 | /** 38 | * 选中的包名临时保存 39 | */ 40 | private String mPackName; 41 | 42 | /** 43 | * Android/data/包名 文件夹 44 | */ 45 | private DocumentFile mDocumentFile; 46 | 47 | @Override 48 | protected void onCreate(Bundle savedInstanceState) { 49 | super.onCreate(savedInstanceState); 50 | setContentView(R.layout.activity_main); 51 | 52 | findViewById(R.id.btn_main).setOnClickListener(v -> showAppDialog()); 53 | refreshImage(R.drawable.pic1); 54 | } 55 | 56 | @Override 57 | protected void onActivityResult(int requestCode, int resultCode, Intent data) { 58 | super.onActivityResult(requestCode, resultCode, data); 59 | switch (requestCode) { 60 | case REQUEST_ANDROID_DATA: 61 | if (!hasFolderPermission()) { 62 | if (resultCode == Activity.RESULT_OK) { 63 | if (data != null && data.getData() != null) { 64 | mDocumentFile = DocumentFile.fromTreeUri(this, data.getData()); 65 | try { 66 | getContentResolver().takePersistableUriPermission(data.getData(), Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION); 67 | } catch (Exception e) { 68 | Toast.makeText(this, e.getMessage(), Toast.LENGTH_SHORT).show(); 69 | } 70 | } 71 | } 72 | } 73 | if (hasFolderPermission()) { 74 | uncensored(); 75 | } 76 | break; 77 | } 78 | } 79 | 80 | @Override 81 | public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) { 82 | super.onRequestPermissionsResult(requestCode, permissions, grantResults); 83 | switch (requestCode) { 84 | case REQUEST_STORAGE_PERMISSION: 85 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && checkSelfPermission(Manifest.permission.READ_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED) { 86 | uncensored(); 87 | return; 88 | } 89 | break; 90 | } 91 | } 92 | 93 | /** 94 | * 弹出所有渠道服选择的弹窗 95 | */ 96 | private void showAppDialog() { 97 | mPackName = null; 98 | mDocumentFile = null; 99 | 100 | List packList = PackUtil.getPacksBySystem(this); 101 | List filterList = new ArrayList<>(); 102 | for (PackageInfo packInfo : packList) { 103 | for (PackEnum packEnum : PackEnum.values()) { 104 | if (packInfo.packageName != null && packEnum.getPackName() != null && packInfo.packageName.equals(packEnum.getPackName())) { 105 | filterList.add(packInfo); 106 | break; 107 | } 108 | } 109 | } 110 | if (filterList.size() > 0) { 111 | new ChannelDialog(this, filterList, packName -> { 112 | mPackName = packName; 113 | uncensored(); 114 | }).show(); 115 | } else { 116 | Toast.makeText(this, "没有安装蔚蓝档案", Toast.LENGTH_SHORT).show(); 117 | } 118 | } 119 | 120 | /** 121 | * 反和谐 122 | */ 123 | private void uncensored() { 124 | // 权限判断 125 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { 126 | // >= Android 11,获取 Android/data 目录的权限 127 | if (!hasFolderPermission()) { 128 | new AlertDialog.Builder(this) 129 | .setTitle("温馨提示") 130 | .setMessage("点击“确认”后将会打开系统文件管理器\n需要手动点击最底部的“使用此文件夹”\n然后点击“允许”即可") 131 | .setPositiveButton("确认", (dialogInterface, i) -> { 132 | Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT_TREE); 133 | intent.putExtra("android.provider.extra.SHOW_ADVANCED", true); 134 | intent.putExtra("android.content.extra.SHOW_ADVANCED", true); 135 | intent.putExtra(DocumentsContract.EXTRA_INITIAL_URI, getFolderUri(true)); 136 | intent.addFlags(Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION | Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION); 137 | startActivityForResult(intent, REQUEST_ANDROID_DATA); 138 | }).show(); 139 | return; 140 | } else if (mDocumentFile == null) { 141 | Uri folderUri = getFolderUri(false); 142 | if (folderUri != null) { 143 | mDocumentFile = DocumentFile.fromTreeUri(this, folderUri); 144 | } 145 | } 146 | } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { 147 | // >= Android 6.0,动态申请权限 148 | if (checkSelfPermission(Manifest.permission.READ_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) { 149 | requestPermissions(PERMISSIONS_STORAGE, REQUEST_STORAGE_PERMISSION); 150 | return; 151 | } 152 | } 153 | // < Android 6.0,不需要权限 154 | if (mDocumentFile == null) { 155 | File channelFile = getChannelFile(); 156 | if (channelFile.exists()) { 157 | mDocumentFile = DocumentFile.fromFile(channelFile); 158 | } 159 | } 160 | if (mDocumentFile != null) { 161 | // 反和谐 162 | new Thread(() -> { 163 | DocumentFile filesFolder = mDocumentFile.findFile("files"); 164 | if (filesFolder == null) { 165 | filesFolder = mDocumentFile.createDirectory("files"); 166 | } 167 | if (filesFolder != null && filesFolder.isDirectory()) { 168 | DocumentFile localization = filesFolder.findFile("LocalizeConfig.txt"); 169 | try { 170 | if (localization != null) { 171 | // 删除已存在的文件 172 | localization.delete(); 173 | } 174 | localization = filesFolder.createFile("application/txt", "LocalizeConfig.txt"); 175 | OutputStream outputStream = getContentResolver().openOutputStream(localization.getUri()); 176 | String content = "Env=dev\nIsLocalize=false\nResUrls=http://mx.jvav.net.cn/asdf;http://mx.jvav.net.cn/asdf;http://mx.jvav.net.cn/asdf"; 177 | outputStream.write(content.getBytes()); 178 | outputStream.flush(); 179 | outputStream.close(); 180 | runOnUiThread(() -> { 181 | refreshImage(R.drawable.pic2); 182 | new AlertDialog.Builder(this) 183 | .setTitle("恭喜") 184 | .setMessage("反和谐成功,重启游戏即可生效\n这个APP可以卸载掉了") 185 | .setPositiveButton("知道了", null) 186 | .show(); 187 | }); 188 | } catch (Exception e) { 189 | runOnUiThread(() -> { 190 | Toast.makeText(this, e.getMessage(), Toast.LENGTH_SHORT).show(); 191 | }); 192 | } 193 | } 194 | }).start(); 195 | } 196 | } 197 | 198 | /** 199 | * 获取 Android/data 文件夹 Uri 200 | * 201 | * @param isRequestPermission 是否是申请权限 202 | * @return Uri 203 | */ 204 | private Uri getFolderUri(boolean isRequestPermission) { 205 | if (mPackName != null) { 206 | if (isRequestPermission) { 207 | return DocumentsContract.buildDocumentUri("com.android.externalstorage.documents", "primary:Android/data/" + mPackName); 208 | } else { 209 | return DocumentsContract.buildTreeDocumentUri("com.android.externalstorage.documents", "primary:Android/data/" + mPackName); 210 | } 211 | } 212 | return null; 213 | } 214 | 215 | /** 216 | * 是否有 Android/data 文件夹权限 217 | * 218 | * @return bool 219 | */ 220 | private boolean hasFolderPermission() { 221 | Uri folderUri = getFolderUri(false); 222 | if (folderUri != null) { 223 | List permissionList = getContentResolver().getPersistedUriPermissions(); 224 | for (UriPermission uriPermission : permissionList) { 225 | if (uriPermission.getUri().equals(folderUri) && (uriPermission.isReadPermission() || uriPermission.isWritePermission())) { 226 | return true; 227 | } 228 | } 229 | } 230 | return false; 231 | } 232 | 233 | /** 234 | * 获取 Android/data/包名 文件夹路径 235 | * 236 | * @return 只有 Android 11 以下才需要这个 237 | */ 238 | private File getChannelFile() { 239 | return new File("/sdcard/Android/data/" + mPackName); 240 | } 241 | 242 | /** 243 | * 刷新图片 244 | * 245 | * @param drawableId 资源 id 246 | */ 247 | private void refreshImage(int drawableId) { 248 | ImageView img = findViewById(R.id.img); 249 | img.setImageResource(drawableId); 250 | } 251 | } -------------------------------------------------------------------------------- /app/src/main/java/com/guisei/bluearchiveuncensored/config/PackEnum.java: -------------------------------------------------------------------------------- 1 | package com.guisei.bluearchiveuncensored.config; 2 | 3 | /** 4 | * 蔚蓝档案包名管理 5 | */ 6 | public enum PackEnum { 7 | 8 | SCHALE("沙勒", "com.RoamingStar.BlueArchive"), 9 | 10 | ;// ================================================================================= 11 | 12 | /** 13 | * 渠道名 14 | */ 15 | private String channel; 16 | /** 17 | * 包名 18 | */ 19 | private String packName; 20 | 21 | PackEnum(String channel, String packName) { 22 | this.channel = channel; 23 | this.packName = packName; 24 | } 25 | 26 | public String getChannel() { 27 | return channel; 28 | } 29 | 30 | public void setChannel(String channel) { 31 | this.channel = channel; 32 | } 33 | 34 | public String getPackName() { 35 | return packName; 36 | } 37 | 38 | public void setPackName(String packName) { 39 | this.packName = packName; 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /app/src/main/java/com/guisei/bluearchiveuncensored/dialog/ChannelDialog.java: -------------------------------------------------------------------------------- 1 | package com.guisei.bluearchiveuncensored.dialog; 2 | 3 | import android.app.Dialog; 4 | import android.content.Context; 5 | import android.content.pm.PackageInfo; 6 | import android.graphics.Color; 7 | import android.graphics.drawable.ColorDrawable; 8 | import android.view.LayoutInflater; 9 | import android.view.View; 10 | import android.view.ViewGroup; 11 | import android.widget.ArrayAdapter; 12 | import android.widget.ImageView; 13 | import android.widget.ListView; 14 | import android.widget.TextView; 15 | 16 | import com.guisei.bluearchiveuncensored.R; 17 | import com.guisei.bluearchiveuncensored.config.PackEnum; 18 | 19 | import java.util.List; 20 | 21 | /** 22 | * 渠道选择弹窗 23 | */ 24 | public class ChannelDialog { 25 | 26 | private Context mContext; 27 | private List mList; 28 | 29 | public interface OnListener { 30 | void onSelect(String packName); 31 | } 32 | 33 | private OnListener mOnListener; 34 | 35 | public ChannelDialog(Context context, List list, OnListener listener) { 36 | mContext = context; 37 | mList = list; 38 | mOnListener = listener; 39 | } 40 | 41 | public void show() { 42 | final Dialog dialog = new Dialog(mContext); 43 | dialog.setContentView(R.layout.dialog_channel); 44 | if (dialog.getWindow() != null) { 45 | dialog.getWindow().setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT)); 46 | } 47 | ListView listView = dialog.findViewById(R.id.list_view); 48 | ArrayAdapter arrayAdapter = new ChannelAdapter(mContext, mList); 49 | listView.setAdapter(arrayAdapter); 50 | listView.setOnItemClickListener((adapterView, view, which, l) -> { 51 | if (mOnListener != null) { 52 | mOnListener.onSelect(mList.get(which).packageName); 53 | } 54 | dialog.dismiss(); 55 | }); 56 | dialog.show(); 57 | } 58 | 59 | public class ChannelAdapter extends ArrayAdapter { 60 | public ChannelAdapter(Context context, List list) { 61 | super(context, 0, list); 62 | } 63 | 64 | @Override 65 | public View getView(int position, View convertView, ViewGroup parent) { 66 | View view = LayoutInflater.from(getContext()).inflate(R.layout.item_channel, parent, false); 67 | 68 | ImageView iv_app = view.findViewById(R.id.iv_app); 69 | TextView tv_app = view.findViewById(R.id.tv_app); 70 | TextView tv_pack_name = view.findViewById(R.id.tv_pack_name); 71 | 72 | PackageInfo packInfo = (PackageInfo) getItem(position); 73 | if (packInfo != null) { 74 | // 图标 75 | iv_app.setImageDrawable(packInfo.applicationInfo.loadIcon(getContext().getPackageManager())); 76 | // 渠道名称 77 | for (PackEnum packEnum : PackEnum.values()) { 78 | if (packEnum.getPackName().equals(packInfo.packageName)) { 79 | tv_app.setText(packEnum.getChannel()); 80 | } 81 | } 82 | // 包名 83 | tv_pack_name.setText(packInfo.packageName); 84 | } 85 | 86 | return view; 87 | } 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /app/src/main/java/com/guisei/bluearchiveuncensored/util/PackUtil.java: -------------------------------------------------------------------------------- 1 | package com.guisei.bluearchiveuncensored.util; 2 | 3 | import android.content.Context; 4 | import android.content.Intent; 5 | import android.content.pm.PackageInfo; 6 | import android.content.pm.ResolveInfo; 7 | 8 | import java.util.ArrayList; 9 | import java.util.List; 10 | 11 | /** 12 | * 包管理工具类 13 | */ 14 | public class PackUtil { 15 | 16 | /** 17 | * 使用系统方法获取包列表 18 | * 19 | * @param context context 20 | * @return list 21 | */ 22 | public static List getPacksBySystem(Context context) { 23 | return context.getPackageManager().getInstalledPackages(0); 24 | } 25 | 26 | /** 27 | * 使用queryIntentActivities获取包名列表 28 | * 这种方法获取到的并没有另外两种多,除非是国产手机只回复了部分列表,这个方法能获取全部 29 | * 而且通过包名获取包信息,又浪费了一次性能 30 | * 31 | * @param context context 32 | * @return list 33 | */ 34 | public static List getPacksByQueryIntent(Context context) { 35 | List packNameList = new ArrayList<>(); 36 | List resolveInfoList = context.getPackageManager().queryIntentActivities(new Intent(Intent.ACTION_MAIN), 0); 37 | for (ResolveInfo resolveInfo : resolveInfoList) { 38 | String packName = resolveInfo.activityInfo.applicationInfo.packageName; 39 | if (!packNameList.contains(packName)) { 40 | packNameList.add(packName); 41 | } 42 | } 43 | List packageInfoList = new ArrayList<>(); 44 | for (String packName : packNameList) { 45 | packageInfoList.add(getPackInfo(context, packName)); 46 | } 47 | return packageInfoList; 48 | } 49 | 50 | /** 51 | * 根据包名获取包信息 52 | * 53 | * @param context context 54 | * @param packName 包名 55 | * @return packInfo 56 | */ 57 | public static PackageInfo getPackInfo(Context context, String packName) { 58 | PackageInfo packInfo = null; 59 | try { 60 | packInfo = context.getPackageManager().getPackageInfo(packName, 0); 61 | } catch (Exception ignored) { 62 | } 63 | return packInfo; 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /app/src/main/java/com/guisei/bluearchiveuncensored/view/GifView.java: -------------------------------------------------------------------------------- 1 | package com.guisei.bluearchiveuncensored.view; 2 | 3 | import android.content.Context; 4 | import android.graphics.Canvas; 5 | import android.graphics.Movie; 6 | import android.util.AttributeSet; 7 | import android.view.View; 8 | 9 | import androidx.annotation.Nullable; 10 | 11 | import java.io.InputStream; 12 | 13 | /** 14 | * https://stackoverflow.com/questions/6533942/adding-gif-image-in-an-imageview-in-android 15 | */ 16 | public class GifView extends View { 17 | 18 | private InputStream gifInputStream; 19 | private Movie gifMovie; 20 | private int movieWidth, movieHeight; 21 | private long movieDuration; 22 | private long mMovieStart; 23 | 24 | public GifView(Context context) { 25 | super(context); 26 | } 27 | 28 | public GifView(Context context, @Nullable AttributeSet attrs) { 29 | super(context, attrs); 30 | } 31 | 32 | public void init(Context context, int drawableId) { 33 | setFocusable(true); 34 | gifInputStream = context.getResources().openRawResource(drawableId); 35 | 36 | gifMovie = Movie.decodeStream(gifInputStream); 37 | movieWidth = gifMovie.width(); 38 | movieHeight = gifMovie.height(); 39 | movieDuration = gifMovie.duration(); 40 | } 41 | 42 | @Override 43 | protected void onMeasure(int widthMeasureSpec, 44 | int heightMeasureSpec) { 45 | setMeasuredDimension(movieWidth, movieHeight); 46 | } 47 | 48 | public int getMovieWidth() { 49 | return movieWidth; 50 | } 51 | 52 | public int getMovieHeight() { 53 | return movieHeight; 54 | } 55 | 56 | public long getMovieDuration() { 57 | return movieDuration; 58 | } 59 | 60 | @Override 61 | protected void onDraw(Canvas canvas) { 62 | 63 | long now = android.os.SystemClock.uptimeMillis(); 64 | if (mMovieStart == 0) { // first time 65 | mMovieStart = now; 66 | } 67 | 68 | if (gifMovie != null) { 69 | 70 | int dur = gifMovie.duration(); 71 | if (dur == 0) { 72 | dur = 1000; 73 | } 74 | 75 | int relTime = (int) ((now - mMovieStart) % dur); 76 | 77 | gifMovie.setTime(relTime); 78 | 79 | gifMovie.draw(canvas, 0, 0); 80 | invalidate(); 81 | 82 | } 83 | 84 | } 85 | 86 | } -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi/pic1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yxzwym/BlueArchiveUncensored/1bd3d8a483aea551ec31aaaff5600d4704551cfc/app/src/main/res/drawable-xxhdpi/pic1.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi/pic2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yxzwym/BlueArchiveUncensored/1bd3d8a483aea551ec31aaaff5600d4704551cfc/app/src/main/res/drawable-xxhdpi/pic2.png -------------------------------------------------------------------------------- /app/src/main/res/drawable/bg_dialog.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/btn_main.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_main.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 16 | 17 |