Commit 49f3729e authored by bixing's avatar bixing

Initial commit

parents
*.iml
.gradle
/local.properties
/.idea/caches
/.idea/libraries
/.idea/modules.xml
/.idea/workspace.xml
/.idea/navEditor.xml
/.idea/assetWizardSettings.xml
.DS_Store
/build
/captures
.externalNativeBuild
.cxx
local.properties
# Default ignored files
/shelf/
/workspace.xml
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="CompilerConfiguration">
<bytecodeTargetLevel target="17" />
</component>
</project>
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="GradleMigrationSettings" migrationVersion="1" />
<component name="GradleSettings">
<option name="linkedExternalProjectsSettings">
<GradleProjectSettings>
<option name="testRunner" value="GRADLE" />
<option name="distributionType" value="DEFAULT_WRAPPED" />
<option name="externalProjectPath" value="$PROJECT_DIR$" />
<option name="gradleJvm" value="17" />
<option name="modules">
<set>
<option value="$PROJECT_DIR$" />
<option value="$PROJECT_DIR$/app" />
</set>
</option>
</GradleProjectSettings>
</option>
</component>
</project>
\ No newline at end of file
<project version="4">
<component name="ExternalStorageConfigurationManager" enabled="true" />
<component name="ProjectRootManager" version="2" languageLevel="JDK_17" project-jdk-name="17" project-jdk-type="JavaSDK">
<output url="file://$PROJECT_DIR$/build/classes" />
</component>
<component name="ProjectType">
<option name="id" value="Android" />
</component>
</project>
\ No newline at end of file
/build
\ No newline at end of file
plugins {
id 'com.android.application'
}
android {
namespace 'com.ads.cal.picturetranslate'
compileSdk 33
defaultConfig {
applicationId "com.ads.cal.picturetranslate"
minSdk 24
targetSdk 33
versionCode 1
versionName "1.0"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
ndk {
abiFilters "armeabi-v7a", "arm64-v8a", "x86", "x86_64"
}
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
}
dependencies {
implementation 'androidx.appcompat:appcompat:1.6.1'
implementation 'com.google.android.material:material:1.9.0'
implementation 'androidx.camera:camera-view:1.2.3'
testImplementation 'junit:junit:4.13.2'
androidTestImplementation 'androidx.test.ext:junit:1.1.5'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.5.1'
implementation (platform("org.jetbrains.kotlin:kotlin-bom:1.8.0"))
// To recognize Latin script
implementation 'com.google.mlkit:text-recognition:16.0.0'
// To recognize Chinese script
implementation 'com.google.mlkit:text-recognition-chinese:16.0.0'
// To recognize Devanagari script
implementation 'com.google.mlkit:text-recognition-devanagari:16.0.0'
// To recognize Japanese script
implementation 'com.google.mlkit:text-recognition-japanese:16.0.0'
// To recognize Korean script
implementation 'com.google.mlkit:text-recognition-korean:16.0.0'
implementation "androidx.camera:camera-core:1.3.0-alpha04"
implementation "androidx.camera:camera-camera2:1.3.0-alpha04"
implementation "androidx.camera:camera-lifecycle:1.3.0-alpha04"
//图像裁剪
implementation 'com.edmodo:cropper:1.0.1'
implementation 'com.github.bumptech.glide:glide:4.15.1'
annotationProcessor 'com.github.bumptech.glide:compiler:4.12.0'
implementation 'pl.droidsonroids.gif:android-gif-drawable:1.2.27'
}
\ No newline at end of file
# Add project specific ProGuard rules here.
# You can control the set of applied configuration files using the
# proguardFiles setting in build.gradle.
#
# For more details, see
# http://developer.android.com/guide/developing/tools/proguard.html
# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
# public *;
#}
# Uncomment this to preserve the line number information for
# debugging stack traces.
#-keepattributes SourceFile,LineNumberTable
# If you keep the line number information, uncomment this to
# hide the original source file name.
#-renamesourcefileattribute SourceFile
\ No newline at end of file
package com.ads.cal.picturetranslate;
import android.content.Context;
import androidx.test.platform.app.InstrumentationRegistry;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import org.junit.Test;
import org.junit.runner.RunWith;
import static org.junit.Assert.*;
/**
* Instrumented test, which will execute on an Android device.
*
* @see <a href="http://d.android.com/tools/testing">Testing documentation</a>
*/
@RunWith(AndroidJUnit4.class)
public class ExampleInstrumentedTest {
@Test
public void useAppContext() {
// Context of the app under test.
Context appContext = InstrumentationRegistry.getInstrumentation().getTargetContext();
assertEquals("com.ads.cal.picturetranslate", appContext.getPackageName());
}
}
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
<uses-permission android:name="android.permission.CAMERA" />
<uses-feature android:name="android.hardware.camera.any" />
<uses-permission android:name="android.permission.RECORD_AUDIO" />
<uses-permission
android:name="android.permission.WRITE_EXTERNAL_STORAGE"
android:maxSdkVersion="28" />
<uses-permission android:name="android.permission.READ_MEDIA_IMAGES" />
<uses-feature
android:name="android.hardware.camera"
android:required="false" />
<application
android:name=".PictureApplication"
android:allowBackup="true"
android:dataExtractionRules="@xml/data_extraction_rules"
android:fullBackupContent="@xml/backup_rules"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/Theme.PictureTranslate"
tools:targetApi="31">
<meta-data
android:name="com.google.mlkit.vision.DEPENDENCIES"
android:value="ocr" />
<activity
android:name=".activity.PictureTranslateStartActivity"
android:exported="true"
android:screenOrientation="portrait">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity android:name=".activity.AboutActivity"
android:exported="true"
android:screenOrientation="portrait"
/>
<activity
android:name=".activity.PictureTranslateListMainActivity"
android:exported="true"
android:launchMode="singleTask"
android:screenOrientation="portrait"/>
<activity
android:name=".activity.CameraActivity"
android:configChanges="orientation|screenSize|keyboardHidden"
android:exported="true"
android:hardwareAccelerated="true" />
<activity
android:name=".activity.PictureTranslateCutOutPhotoActivity"
android:configChanges="orientation|screenSize|keyboardHidden"
android:exported="true"
android:hardwareAccelerated="true" />
<activity
android:name=".activity.PictureTranslateResultActivity"
android:configChanges="orientation|screenSize|keyboardHidden"
android:exported="true" />
<activity
android:name=".activity.PictureTranslateShowPhotoActivity"
android:configChanges="orientation|screenSize|keyboardHidden"
android:screenOrientation="portrait"
android:exported="true" />
</application>
<!-- To use multiple models: android:value="ocr,ocr_chinese,ocr_devanagari,ocr_japanese,ocr_korean,..." -->
</manifest>
\ No newline at end of file
package com.ads.cal.picturetranslate;
import android.util.Log;
public class LogUtils {
private static final String TAG = "AAAAAAAA"; // 默认的日志标签
private static boolean isEnabled = true; // 是否启用日志输出,可以根据需要在应用中动态控制
// 设置是否启用日志输出
public static void setEnabled(boolean enabled) {
isEnabled = enabled;
}
// 输出普通信息日志
public static void d(String message) {
if (isEnabled) {
Log.d(TAG, message);
}
}
// 输出错误信息日志
public static void e(String message) {
if (isEnabled) {
Log.e(TAG, message);
}
}
// 输出警告信息日志
public static void w(String message) {
if (isEnabled) {
Log.w(TAG, message);
}
}
// 输出信息日志
public static void i(String message) {
if (isEnabled) {
Log.i(TAG, message);
}
}
// 输出调试信息日志
public static void v(String message) {
if (isEnabled) {
Log.v(TAG, message);
}
}
// 输出指定标签的信息日志
public static void d(String tag, String message) {
if (isEnabled) {
Log.d(tag, message);
}
}
// 输出指定标签的错误信息日志
public static void e(String tag, String message) {
if (isEnabled) {
Log.e(tag, message);
}
}
// 输出指定标签的警告信息日志
public static void w(String tag, String message) {
if (isEnabled) {
Log.w(tag, message);
}
}
// 输出指定标签的信息日志
public static void i(String tag, String message) {
if (isEnabled) {
Log.i(tag, message);
}
}
// 输出指定标签的调试信息日志
public static void v(String tag, String message) {
if (isEnabled) {
Log.v(tag, message);
}
}
}
package com.ads.cal.picturetranslate;
import android.Manifest;
import android.app.Activity;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.os.Build;
import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat;
public class PermissionUtil {
public static void checkSelfIMAGEPermission(Activity activity, int requestCode) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
if (ContextCompat.checkSelfPermission(activity, android.Manifest.permission.READ_MEDIA_IMAGES)
!= PackageManager.PERMISSION_GRANTED) {
// 如果没有权限,请求权限
ActivityCompat.requestPermissions(activity,
new String[]{android.Manifest.permission.READ_MEDIA_IMAGES},
requestCode);
} else {
// 已经有权限,可以访问媒体图像文件
toSystemPhoto(activity, requestCode);
}
} else {
// 请求外部存储权限
if (ContextCompat.checkSelfPermission(activity, android.Manifest.permission.READ_EXTERNAL_STORAGE)
!= PackageManager.PERMISSION_GRANTED) {
// 如果没有权限,请求权限
ActivityCompat.requestPermissions(activity,
new String[]{Manifest.permission.READ_EXTERNAL_STORAGE},
requestCode);
} else {
// 已经有权限,可以继续处理逻辑
toSystemPhoto(activity, requestCode);
}
}
}
public static void toSystemPhoto(Activity activity, int requestCode) {
Intent intent = new Intent(Intent.ACTION_PICK, android.provider.MediaStore.Images.Media.EXTERNAL_CONTENT_URI);
activity.startActivityForResult(intent, requestCode);
}
}
package com.ads.cal.picturetranslate;
import android.app.Application;
import com.ads.cal.picturetranslate.db.DataBaseManager;
public class PictureApplication extends Application {
private static DataBaseManager dataBaseManager;
@Override
public void onCreate() {
super.onCreate();
new Thread(() -> {
PictureTranslateUtils.init();
});
initDB();
}
private void initDB() {
dataBaseManager = new DataBaseManager(this);
dataBaseManager.open();
}
public static DataBaseManager getDataBaseManager() {
return dataBaseManager;
}
}
package com.ads.cal.picturetranslate;
import com.google.android.gms.tasks.OnFailureListener;
import com.google.android.gms.tasks.OnSuccessListener;
import com.google.android.gms.tasks.Task;
import com.google.mlkit.vision.common.InputImage;
import com.google.mlkit.vision.text.Text;
import com.google.mlkit.vision.text.TextRecognition;
import com.google.mlkit.vision.text.TextRecognizer;
import com.google.mlkit.vision.text.chinese.ChineseTextRecognizerOptions;
import com.google.mlkit.vision.text.devanagari.DevanagariTextRecognizerOptions;
import com.google.mlkit.vision.text.japanese.JapaneseTextRecognizerOptions;
import com.google.mlkit.vision.text.korean.KoreanTextRecognizerOptions;
import com.google.mlkit.vision.text.latin.TextRecognizerOptions;
public class PictureTranslateUtils {
private final TextRecognizer recognizer;
private final TextRecognizer chineseRecognizer;
private final TextRecognizer devanagariRecognizer;
private final TextRecognizer japaneseRecognizer;
private final TextRecognizer koreanRecognizer;
public PictureTranslateUtils() {
recognizer = TextRecognition.getClient(TextRecognizerOptions.DEFAULT_OPTIONS);
chineseRecognizer = TextRecognition.getClient(new ChineseTextRecognizerOptions.Builder().build());
devanagariRecognizer = TextRecognition.getClient(new DevanagariTextRecognizerOptions.Builder().build());
japaneseRecognizer = TextRecognition.getClient(new JapaneseTextRecognizerOptions.Builder().build());
koreanRecognizer = TextRecognition.getClient(new KoreanTextRecognizerOptions.Builder().build());
}
private static final class PictureTranslateUtilsHolder {
static final PictureTranslateUtils pictureTranslateUtils = new PictureTranslateUtils();
}
public static PictureTranslateUtils init() {
return PictureTranslateUtilsHolder.pictureTranslateUtils;
}
public void translateStandard(InputImage image, OnSuccessListener<Text> onSuccessListener, OnFailureListener onFailureListener) {
recognizer.process(image)
.addOnSuccessListener(onSuccessListener)
.addOnFailureListener(onFailureListener);
}
public void translateChinese(InputImage image, OnSuccessListener<Text> onSuccessListener, OnFailureListener onFailureListener) {
chineseRecognizer.process(image)
.addOnSuccessListener(onSuccessListener)
.addOnFailureListener(onFailureListener);
}
public void translateDevanagari(InputImage image, OnSuccessListener<Text> onSuccessListener, OnFailureListener onFailureListener) {
devanagariRecognizer.process(image)
.addOnSuccessListener(onSuccessListener)
.addOnFailureListener(onFailureListener);
}
public void translateJapanese(InputImage image, OnSuccessListener<Text> onSuccessListener, OnFailureListener onFailureListener) {
japaneseRecognizer.process(image)
.addOnSuccessListener(onSuccessListener)
.addOnFailureListener(onFailureListener);
}
public void translateKorean(InputImage image, OnSuccessListener<Text> onSuccessListener, OnFailureListener onFailureListener) {
koreanRecognizer.process(image)
.addOnSuccessListener(onSuccessListener)
.addOnFailureListener(onFailureListener);
}
public void close() {
if (recognizer != null) {
recognizer.close();
}
if (chineseRecognizer != null) {
chineseRecognizer.close();
}
if (devanagariRecognizer != null) {
devanagariRecognizer.close();
}
if (japaneseRecognizer != null) {
japaneseRecognizer.close();
}
if (koreanRecognizer != null) {
koreanRecognizer.close();
}
}
}
package com.ads.cal.picturetranslate;
import android.content.Context;
import android.content.SharedPreferences;
public class SharedPreferencesUtil {
private static final String PREFS_NAME = "MyAppPreferences";
public static final String KEY_FIRST_INSTALL_APP = "key_first_install_app";
public static final String KEY_FIRST_CLK_TAKE_PHOTO = "key_first_clk_take_photo";
// 保存字符串
public static void saveString(Context context, String key, String value) {
SharedPreferences sharedPreferences = context.getSharedPreferences(PREFS_NAME, Context.MODE_PRIVATE);
SharedPreferences.Editor editor = sharedPreferences.edit();
editor.putString(key, value);
editor.apply();
}
// 读取字符串
public static String getString(Context context, String key, String defaultValue) {
SharedPreferences sharedPreferences = context.getSharedPreferences(PREFS_NAME, Context.MODE_PRIVATE);
return sharedPreferences.getString(key, defaultValue);
}
}
package com.ads.cal.picturetranslate;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class ThreadUtils {
private static final ExecutorService executorService = Executors.newFixedThreadPool(5);
public static void startRunnable(Runnable runnable) {
executorService.submit(runnable);
}
}
package com.ads.cal.picturetranslate;
import android.content.ContentResolver;
import android.content.ContentUris;
import android.content.Context;
import android.database.Cursor;
import android.graphics.Point;
import android.net.Uri;
import android.provider.DocumentsContract;
import android.provider.MediaStore;
import android.view.Display;
import android.view.WindowManager;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Locale;
public class Util {
/**
* 获取真实路径
* <p>
* 支持以下
* <p>
* file://
* content://media/external/file/109009
* FileProvider适配
* content://com.tencent.mobileqq.fileprovider/external_files/storage/emulated/0/Tencent/QQfile_recv/
* content://com.tencent.mm.external.fileprovider/external/tencent/MicroMsg/Download/
* content://com.android.providers.downloads.documents"
* content://com.android.externalstorage.documents
* content://com.android.providers.media.documents
* content://com.google.android.apps.photos.content
*/
public static String getFileFromUri(Context context, Uri uri) {
if (uri == null) {
return null;
}
switch (uri.getScheme()) {
case ContentResolver.SCHEME_CONTENT:
if (isGooglePhotosUri(uri)) {
return uri.getLastPathSegment();
} else if (isMediaDocument(uri)) {
// MediaProvider
final String docId = DocumentsContract.getDocumentId(uri);
final String[] split = docId.split(":");
final String type = split[0];
Uri contentUri = null;
if ("image".equals(type)) {
contentUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
} else if ("video".equals(type)) {
contentUri = MediaStore.Video.Media.EXTERNAL_CONTENT_URI;
} else if ("audio".equals(type)) {
contentUri = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI;
}
final String selection = "_id=?";
final String[] selectionArgs = new String[]{split[1]};
return getFilePathFromContentUri(context, contentUri, selection, selectionArgs);
} else if (isDownloadsDocument(uri)) {
// DownloadsProvider
final String id = DocumentsContract.getDocumentId(uri);
final Uri contentUri = ContentUris.withAppendedId(Uri.parse("content://downloads/public_downloads"), Long.valueOf(id));
return getFilePathFromContentUri(context, contentUri, null, null);
}
return getFilePathFromContentUri(context, uri, null, null);
case ContentResolver.SCHEME_FILE:
default:
//file://
return new File(uri.getPath()).getAbsolutePath();
}
}
/**
* 从uri获取path 或 拷贝
*/
private static String getFilePathFromContentUri(Context context, Uri uri, String selection, String[] selectionArgs) {
if (null == uri) return null;
String data = null;
String[] filePathColumn = {MediaStore.MediaColumns.DATA, MediaStore.MediaColumns.DISPLAY_NAME};
Cursor cursor = context.getContentResolver().query(uri, filePathColumn, selection, selectionArgs, null);
if (null != cursor) {
if (cursor.moveToFirst()) {
int index = cursor.getColumnIndex(MediaStore.MediaColumns.DATA);
if (index > -1) {
data = cursor.getString(index);
if (data == null || !fileIsExists(data)) {
//可能拿不到真实路径 或 文件不存在 走拷贝流程
int nameIndex = cursor.getColumnIndex(MediaStore.MediaColumns.DISPLAY_NAME);
String fileName = cursor.getString(nameIndex);
data = getPathFromInputStreamUri(context, uri, fileName);
}
} else {
//拷贝一份
int nameIndex = cursor.getColumnIndex(MediaStore.MediaColumns.DISPLAY_NAME);
String fileName = cursor.getString(nameIndex);
data = getPathFromInputStreamUri(context, uri, fileName);
}
}
cursor.close();
}
return data;
}
/**
* 用流拷贝文件一份到自己APP私有目录下
*
* @param context
* @param uri
* @param fileName
*/
private static String getPathFromInputStreamUri(Context context, Uri uri, String fileName) {
InputStream inputStream = null;
String filePath = null;
if (uri.getAuthority() != null) {
try {
inputStream = context.getContentResolver().openInputStream(uri);
File file = createTemporalFileFrom(context, inputStream, fileName);
filePath = file.getPath();
} catch (Exception e) {
} finally {
try {
if (inputStream != null) {
inputStream.close();
}
} catch (Exception e) {
}
}
}
return filePath;
}
private static File createTemporalFileFrom(Context context, InputStream inputStream, String fileName)
throws IOException {
File targetFile = null;
if (inputStream != null) {
int read;
byte[] buffer = new byte[8 * 1024];
//自己定义拷贝文件路径
targetFile = new File(context.getExternalCacheDir(), fileName);
if (targetFile.exists()) {
targetFile.delete();
}
OutputStream outputStream = new FileOutputStream(targetFile);
while ((read = inputStream.read(buffer)) != -1) {
outputStream.write(buffer, 0, read);
}
outputStream.flush();
try {
outputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
return targetFile;
}
private static boolean isDownloadsDocument(Uri uri) {
return "com.android.providers.downloads.documents".equals(uri.getAuthority());
}
private static boolean isMediaDocument(Uri uri) {
return "com.android.providers.media.documents".equals(uri.getAuthority());
}
public static boolean isGooglePhotosUri(Uri uri) {
return "com.google.android.apps.photos.content".equals(uri.getAuthority());
}
//判断文件是否存在
private static boolean fileIsExists(String filePath) {
try {
File f = new File(filePath);
if (!f.exists()) {
return false;
}
} catch (Exception e) {
return false;
}
return true;
}
public static String getDataTime(long createTime) {
long timeDifference = System.currentTimeMillis() - createTime;
String timeAgo;
if (timeDifference < 60000) { // 不到1分钟
timeAgo = (timeDifference / 1000) + "秒前";
} else if (timeDifference < 3600000) { // 不到1小时
timeAgo = (timeDifference / 60000) + "分钟前";
} else if (timeDifference < 86400000) { // 不到1天
timeAgo = (timeDifference / 3600000) + "小时前";
} else { // 大于1天
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.getDefault());
timeAgo = sdf.format(new Date());
}
return timeAgo;
}
public static Uri getFirstImageUriFromGallery(Context context) {
// 定义要查询的内容URI
Uri uri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
// 定义要返回的列,这里我们只需要文件路径
String[] projection = {MediaStore.Images.Media.DATA};
// 查询并排序
Cursor cursor = context.getContentResolver().query(
uri,
projection,
null,
null,
null
);
if (cursor != null && cursor.moveToFirst()) {
// 获取第一张图片的路径
int columnIndex = cursor.getColumnIndexOrThrow(MediaStore.Images.Media.DATA);
String imagePath = cursor.getString(columnIndex);
// 关闭游标
cursor.close();
// 返回第一张图片的URI
return Uri.parse("file://" + imagePath);
}
return null; // 如果没有找到图片,返回null
}
public static int getScreenHeight(Context context) {
WindowManager windowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
Display display = windowManager.getDefaultDisplay();
Point size = new Point();
display.getSize(size);
return size.y;
}
}
package com.ads.cal.picturetranslate.activity;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.os.Bundle;
import android.widget.ImageView;
import android.widget.TextView;
import androidx.annotation.Nullable;
import com.ads.cal.picturetranslate.R;
import com.ads.cal.picturetranslate.base.BaseActivity;
import java.io.IOException;
import pl.droidsonroids.gif.GifDecoder;
import pl.droidsonroids.gif.GifDrawable;
import pl.droidsonroids.gif.GifImageView;
import pl.droidsonroids.gif.InputSource;
public class AboutActivity extends BaseActivity {
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_about);
initView();
}
private void initView() {
GifImageView imageView = findViewById(R.id.image);
try {
GifDrawable gifFromAssets = new GifDrawable( getAssets(), "app_gif.gif" );
gifFromAssets.setLoopCount(1);
imageView.setImageDrawable(gifFromAssets);
} catch (IOException e) {
e.printStackTrace();
}
TextView versionCode = findViewById(R.id.versioncode);
try {
PackageInfo packageInfo = getPackageManager().getPackageInfo(getPackageName(), 0);
versionCode.setText(String.valueOf(packageInfo.versionCode));
} catch (PackageManager.NameNotFoundException e) {
e.printStackTrace();
}
}
public static void startAboutActivity(Context context) {
Intent intent = new Intent(context, AboutActivity.class);
context.startActivity(intent);
}
}
package com.ads.cal.picturetranslate.activity;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.content.res.Configuration;
import android.net.Uri;
import android.os.Bundle;
import android.view.View;
import android.view.WindowManager;
import android.widget.ImageView;
import android.widget.Toast;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.camera.core.CameraSelector;
import androidx.camera.core.ImageAnalysis;
import androidx.camera.core.ImageCapture;
import androidx.camera.core.ImageCaptureException;
import androidx.camera.core.ImageProxy;
import androidx.camera.core.Preview;
import androidx.camera.lifecycle.ProcessCameraProvider;
import androidx.camera.view.PreviewView;
import androidx.core.content.ContextCompat;
import com.ads.cal.picturetranslate.LogUtils;
import com.ads.cal.picturetranslate.PermissionUtil;
import com.ads.cal.picturetranslate.R;
import com.ads.cal.picturetranslate.SharedPreferencesUtil;
import com.ads.cal.picturetranslate.Util;
import com.ads.cal.picturetranslate.base.BaseActivity;
import com.google.common.util.concurrent.ListenableFuture;
import java.io.File;
public class CameraActivity extends BaseActivity implements ImageAnalysis.Analyzer {
private static final int SELECT_TAKE_PHONE = 1;
private ImageCapture imageCapture;
private ImageView imageView, selectPhone;
private PreviewView mPreview;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
// 隐藏状态栏和虚拟导航栏
getWindow().setFlags(
WindowManager.LayoutParams.FLAG_FULLSCREEN,
WindowManager.LayoutParams.FLAG_FULLSCREEN
);
// 隐藏虚拟导航栏
getWindow().getDecorView().setSystemUiVisibility(
View.SYSTEM_UI_FLAG_LAYOUT_STABLE
| View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
| View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
| View.SYSTEM_UI_FLAG_FULLSCREEN
| View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY
);
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_camera);
initView();
initData();
try {
initCamera();
} catch (Exception e) {
e.printStackTrace();
LogUtils.e("AAAAAAA", e.getMessage());
}
SharedPreferencesUtil.saveString(this, SharedPreferencesUtil.KEY_FIRST_CLK_TAKE_PHOTO, "clk");
}
private void initView() {
ImageView close = findViewById(R.id.close);
close.setOnClickListener(v -> finish());
mPreview = findViewById(R.id.preview);
imageView = findViewById(R.id.take_photo);
imageView.setOnClickListener(v -> startTakePhone());
selectPhone = findViewById(R.id.select_phone);
selectPhone.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
PermissionUtil.checkSelfIMAGEPermission(CameraActivity.this, SELECT_TAKE_PHONE);
}
});
}
private void initData() {
Uri firstImageUri = Util.getFirstImageUriFromGallery(this);
if (firstImageUri == null) {
return;
}
selectPhone.setImageURI(firstImageUri);
}
private void initCamera() {
// 将Camera的生命周期和Activity绑定在一起(设定生命周期所有者),这样就不用手动控制相机的启动和关闭。
ListenableFuture<ProcessCameraProvider> cameraProviderFuture = ProcessCameraProvider.getInstance(this);
cameraProviderFuture.addListener(() -> {
ProcessCameraProvider processCameraProvider = null;
try {
processCameraProvider = cameraProviderFuture.get();
} catch (Exception e) {
e.printStackTrace();
}
Preview preview = new Preview.Builder().build();
preview.setSurfaceProvider(mPreview.getSurfaceProvider());
// 创建拍照所需的实例
imageCapture = new ImageCapture.Builder()
.setCaptureMode(ImageCapture.CAPTURE_MODE_MINIMIZE_LATENCY)
.setJpegQuality(80).build();
ImageAnalysis imageAnalysis = new ImageAnalysis.Builder().build();
// Select back camera as a default
CameraSelector cameraSelector = CameraSelector.DEFAULT_BACK_CAMERA;
try {
if (null == processCameraProvider) {
return;
}
processCameraProvider.unbindAll();
processCameraProvider.bindToLifecycle(this, cameraSelector, preview, imageCapture, imageAnalysis);
} catch (Exception e) {
e.printStackTrace();
}
}, ContextCompat.getMainExecutor(this));
}
private void startTakePhone() {
ImageCapture imageCapture = this.imageCapture;
if (imageCapture == null) {
return;
}
File photoFile = new File(getCacheDir(), "/" + System.currentTimeMillis() + ".jpeg");
// 创建 output option 对象,用以指定照片的输出方式
ImageCapture.OutputFileOptions outputFileOptions = new ImageCapture.OutputFileOptions
.Builder(photoFile)
.build();
imageView.setEnabled(false);
// 执行takePicture(拍照)方法
imageCapture.takePicture(outputFileOptions,
ContextCompat.getMainExecutor(this),
new ImageCapture.OnImageSavedCallback() {// 保存照片时的回调
@Override
public void onImageSaved(@NonNull ImageCapture.OutputFileResults outputFileResults) {
PictureTranslateShowPhotoActivity.startPictureTranslateShowPhotoActivity(CameraActivity.this, Uri.fromFile(photoFile));
imageView.setEnabled(true);
}
@Override
public void onError(@NonNull ImageCaptureException exception) {
LogUtils.e("AAAAAA", exception.getMessage());
imageView.setEnabled(true);
}
});
}
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
if (requestCode == SELECT_TAKE_PHONE) {
if (allPermissionsGranted(grantResults)) {
PermissionUtil.toSystemPhoto(this, SELECT_TAKE_PHONE);
} else {
Toast.makeText(this, getString(R.string.app_toast_enable_permissions), Toast.LENGTH_SHORT).show();
}
}
}
private boolean allPermissionsGranted(int[] grantResults) {
return grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED;
}
/**
* 获取图片回调
*/
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (resultCode == RESULT_OK && data != null) {
if (requestCode == SELECT_TAKE_PHONE) {
Uri selectedImageUri = data.getData();
PictureTranslateShowPhotoActivity.startPictureTranslateShowPhotoActivity(CameraActivity.this, selectedImageUri);
} else {
PictureTranslateShowPhotoActivity.startPictureTranslateShowPhotoActivity(CameraActivity.this, data.getData());
LogUtils.e("uri", data.getData().toString());
}
}
}
@Override
public void onConfigurationChanged(@NonNull Configuration newConfig) {
super.onConfigurationChanged(newConfig);
}
@Override
public void analyze(@NonNull ImageProxy image) {
LogUtils.e("AAAAAA", image.toString());
}
public static void startCameraActivity(Context context) {
Intent intent = new Intent(context, CameraActivity.class);
context.startActivity(intent);
}
}
package com.ads.cal.picturetranslate.activity;
import android.content.Context;
import android.content.Intent;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.media.ExifInterface;
import android.net.Uri;
import android.os.Bundle;
import android.text.TextUtils;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.Toast;
import androidx.annotation.Nullable;
import androidx.camera.core.ExperimentalGetImage;
import com.ads.cal.picturetranslate.LogUtils;
import com.ads.cal.picturetranslate.PictureApplication;
import com.ads.cal.picturetranslate.PictureTranslateUtils;
import com.ads.cal.picturetranslate.R;
import com.ads.cal.picturetranslate.Util;
import com.ads.cal.picturetranslate.base.BaseActivity;
import com.ads.cal.picturetranslate.bean.PictureTranslateBean;
import com.ads.cal.picturetranslate.fragment.LoadingFragment;
import com.edmodo.cropper.CropImageView;
import com.google.mlkit.vision.common.InputImage;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
@ExperimentalGetImage
public class PictureTranslateCutOutPhotoActivity extends BaseActivity {
private CropImageView cropImageView;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_cutoutphoto);
initView();
initData();
}
private void initView() {
cropImageView = findViewById(R.id.crop);
ImageView close = findViewById(R.id.back);
LinearLayout ok = findViewById(R.id.ok_crop);
close.setOnClickListener(v -> {
finish();
});
ok.setOnClickListener(v -> {
showLoadingFragment(new LoadingFragment());
Bitmap cropperBitmap = cropImageView.getCroppedImage();
// 图像名称
String path = getCacheDir().getPath();
String resultPath = saveImage(path, cropperBitmap);
translate(cropperBitmap, resultPath);
});
}
private void translate(Bitmap bitmap, String path) {
if (bitmap == null) {
hideLoadingFragment();
return;
}
PictureTranslateUtils.init().translateChinese(InputImage.fromBitmap(bitmap, 0),
text -> {
hideLoadingFragment();
LogUtils.e("AAAAAA", text.getText());
PictureApplication.getDataBaseManager().insertData(path, text.getText(), System.currentTimeMillis());
PictureTranslateBean bean = new PictureTranslateBean();
bean.setImageUrl(path);
bean.setTitle(text.getText());
bean.setTime(System.currentTimeMillis());
PictureTranslateResultActivity.startPictureTranslateResultActivity(PictureTranslateCutOutPhotoActivity.this, bean);
finish();
if (bitmap.isRecycled()) {
return;
}
bitmap.recycle();
},
e -> {
hideLoadingFragment();
LogUtils.e("AAAAAA", e.getMessage());
if (bitmap.isRecycled()) {
return;
}
bitmap.recycle();
});
}
private void initData() {
Intent intent = getIntent();
Uri uri = null;
if (intent != null) {
uri = intent.getParcelableExtra("uri");
}
if (uri == null) {
return;
}
String path = Util.getFileFromUri(this, uri);
if (TextUtils.isEmpty(path)) {
Toast.makeText(this, getString(R.string.app_toast_the_file_is_corrupted), Toast.LENGTH_SHORT).show();
} else {
Bitmap bitmap = BitmapFactory.decodeFile(path);
ExifInterface exif = null;
try {
exif = new ExifInterface(path);
} catch (IOException e) {
e.printStackTrace();
}
cropImageView.setImageBitmap(bitmap, exif);
}
}
/**
* 存储图像
*/
private String saveImage(String path, Bitmap source) {
LogUtils.e("AAAAAAAAA", path);
OutputStream outputStream = null;
File file;
try {
File dir = new File(path);
if (!dir.exists()) {
dir.mkdirs();
}
file = new File(path, System.currentTimeMillis() + ".jpeg");
LogUtils.e("AAAAAAAAA", file.getPath());
if (file.createNewFile()) {
outputStream = new FileOutputStream(file);
if (source != null) {
source.compress(Bitmap.CompressFormat.JPEG, 100, outputStream);
}
}
} catch (IOException e) {
LogUtils.e("AAAAAAAA", e.getMessage());
return "";
} finally {
if (outputStream != null) {
try {
outputStream.close();
} catch (Throwable t) {
}
}
}
return file.getPath();
}
public static void startCutOutPhotoActivity(Context context, Uri uri) {
Intent intent = new Intent(context, PictureTranslateCutOutPhotoActivity.class);
intent.putExtra("uri", uri);
context.startActivity(intent);
}
}
package com.ads.cal.picturetranslate.activity;
import android.content.Context;
import android.content.Intent;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Matrix;
import android.media.ExifInterface;
import android.net.Uri;
import android.os.Bundle;
import android.text.TextUtils;
import android.view.View;
import android.view.WindowManager;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.Toast;
import androidx.annotation.Nullable;
import androidx.camera.core.ExperimentalGetImage;
import com.ads.cal.picturetranslate.LogUtils;
import com.ads.cal.picturetranslate.PictureApplication;
import com.ads.cal.picturetranslate.PictureTranslateUtils;
import com.ads.cal.picturetranslate.R;
import com.ads.cal.picturetranslate.Util;
import com.ads.cal.picturetranslate.base.BaseActivity;
import com.ads.cal.picturetranslate.bean.PictureTranslateBean;
import com.ads.cal.picturetranslate.fragment.LoadingFragment;
import com.google.mlkit.vision.common.InputImage;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
@ExperimentalGetImage public class PictureTranslateShowPhotoActivity extends BaseActivity {
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
// 隐藏状态栏和虚拟导航栏
getWindow().setFlags(
WindowManager.LayoutParams.FLAG_FULLSCREEN,
WindowManager.LayoutParams.FLAG_FULLSCREEN
);
// 隐藏虚拟导航栏
getWindow().getDecorView().setSystemUiVisibility(
View.SYSTEM_UI_FLAG_LAYOUT_STABLE
| View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
| View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
| View.SYSTEM_UI_FLAG_FULLSCREEN
| View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY
);
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_show_photon);
initView();
initData();
}
private ImageView imageView;
private void initView() {
ImageView close = findViewById(R.id.close);
close.setOnClickListener(v -> finish());
LinearLayout crop = findViewById(R.id.crop);
crop.setOnClickListener(v -> PictureTranslateCutOutPhotoActivity.startCutOutPhotoActivity(this, uri));
LinearLayout extract = findViewById(R.id.extract);
extract.setOnClickListener(v -> {
if (path == null) {
return;
}
showLoadingFragment(new LoadingFragment());
Bitmap bitmap = BitmapFactory.decodeFile(path);
ExifInterface exif = null;
try {
exif = new ExifInterface(path);
} catch (IOException e) {
e.printStackTrace();
}
if (bitmap == null) {
hideLoadingFragment();
return;
}
Bitmap rotatedBitmap = rotateBitmapIfNecessary(bitmap, exif);
// 保存旋转后的图片
String resultPath = saveRotatedImage(rotatedBitmap);
if (!resultPath.isEmpty()) {
translate(rotatedBitmap, resultPath);
} else {
Toast.makeText(this, getString(R.string.app_toast_unable_to_save_picture), Toast.LENGTH_SHORT).show();
}
// 释放位图资源
bitmap.recycle();
});
imageView = findViewById(R.id.bg);
}
private void translate(Bitmap bitmap, String path) {
if (bitmap == null) {
hideLoadingFragment();
return;
}
PictureTranslateUtils.init().translateStandard(InputImage.fromBitmap(bitmap, 0),
text -> {
hideLoadingFragment();
LogUtils.e("AAAAAA", text.getText());
PictureTranslateBean bean = new PictureTranslateBean();
bean.setImageUrl(path);
bean.setTitle(text.getText());
bean.setTime(System.currentTimeMillis());
PictureTranslateResultActivity.startPictureTranslateResultActivity(PictureTranslateShowPhotoActivity.this, bean);
finish();
if (bitmap.isRecycled()) {
return;
}
bitmap.recycle();
},
e -> {
hideLoadingFragment();
LogUtils.e("AAAAAA", e.getMessage());
if (bitmap.isRecycled()) {
return;
}
bitmap.recycle();
});
}
private Uri uri = null;
private String path;
private void initData() {
Intent intent = getIntent();
if (intent != null) {
uri = intent.getParcelableExtra("uri");
}
if (uri == null) {
return;
}
path = Util.getFileFromUri(this, uri);
if (path == null || path.isEmpty()) {
Toast.makeText(this, getString(R.string.app_toast_the_file_is_corrupted), Toast.LENGTH_SHORT).show();
} else {
Bitmap bitmap = BitmapFactory.decodeFile(path);
ExifInterface exif = null;
try {
exif = new ExifInterface(path);
} catch (IOException e) {
e.printStackTrace();
}
setImageBitmap(bitmap, exif);
}
}
public void setImageBitmap(Bitmap bitmap, ExifInterface exif) {
if (bitmap == null) {
return;
}
Matrix matrix = new Matrix();
int orientation = exif != null ? exif.getAttributeInt(ExifInterface.TAG_ORIENTATION, ExifInterface.ORIENTATION_NORMAL) : ExifInterface.ORIENTATION_NORMAL;
int rotate = getRotateValueForOrientation(orientation);
if (rotate != 0) {
matrix.postRotate(rotate);
}
Bitmap rotatedBitmap = Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), matrix, true);
imageView.setImageBitmap(rotatedBitmap);
if (bitmap.isRecycled()) {
return;
}
bitmap.recycle();
}
private Bitmap rotateBitmapIfNecessary(Bitmap bitmap, ExifInterface exif) {
if (exif == null) {
return bitmap;
}
int orientation = exif.getAttributeInt(ExifInterface.TAG_ORIENTATION, ExifInterface.ORIENTATION_NORMAL);
int rotate = getRotateValueForOrientation(orientation);
if (rotate != 0) {
Matrix matrix = new Matrix();
matrix.postRotate(rotate);
return Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), matrix, true);
}
return bitmap;
}
private int getRotateValueForOrientation(int orientation) {
switch (orientation) {
case ExifInterface.ORIENTATION_ROTATE_90:
return 90;
case ExifInterface.ORIENTATION_ROTATE_180:
return 180;
case ExifInterface.ORIENTATION_ROTATE_270:
return 270;
default:
return 0;
}
}
private String saveRotatedImage(Bitmap rotatedBitmap) {
String path = getCacheDir().getPath();
String resultPath = "";
OutputStream outputStream = null;
try {
File dir = new File(path);
if (!dir.exists()) {
dir.mkdirs();
}
File file = new File(dir, System.currentTimeMillis() + ".jpeg");
outputStream = new FileOutputStream(file);
if (rotatedBitmap != null) {
rotatedBitmap.compress(Bitmap.CompressFormat.JPEG, 100, outputStream);
resultPath = file.getPath();
}
} catch (IOException e) {
LogUtils.e("Exception", e.getMessage());
} finally {
if (outputStream != null) {
try {
outputStream.close();
} catch (IOException e) {
LogUtils.e("Exception", e.getMessage());
}
}
}
return resultPath;
}
public static void startPictureTranslateShowPhotoActivity(Context context, Uri uri) {
Intent intent = new Intent(context, PictureTranslateShowPhotoActivity.class);
intent.putExtra("uri", uri);
context.startActivity(intent);
}
}
package com.ads.cal.picturetranslate.activity;
import static com.ads.cal.picturetranslate.SharedPreferencesUtil.KEY_FIRST_INSTALL_APP;
import android.app.Activity;
import android.content.Context;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.text.TextUtils;
import android.view.View;
import android.widget.ProgressBar;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.camera.core.ExperimentalGetImage;
import com.ads.cal.picturetranslate.PictureTranslateUtils;
import com.ads.cal.picturetranslate.R;
import com.ads.cal.picturetranslate.SharedPreferencesUtil;
import com.ads.cal.picturetranslate.base.BaseActivity;
import java.io.IOException;
import java.lang.ref.WeakReference;
import pl.droidsonroids.gif.GifDrawable;
import pl.droidsonroids.gif.GifImageView;
@ExperimentalGetImage
public class PictureTranslateStartActivity extends BaseActivity {
private MyHandler handler;
private ProgressBar bar;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_start);
initView();
}
private void initView() {
GifImageView imageView = findViewById(R.id.gifimg);
try {
GifDrawable gifFromAssets = new GifDrawable(getAssets(), "app_gif.gif");
gifFromAssets.setLoopCount(1);
imageView.setImageDrawable(gifFromAssets);
} catch (IOException e) {
e.printStackTrace();
}
bar = findViewById(R.id.horizontalProgressBar);
TextView try_it_now = findViewById(R.id.try_it_now);
String firstInstall = SharedPreferencesUtil.getString(this, KEY_FIRST_INSTALL_APP, "");
if (!TextUtils.isEmpty(firstInstall)) {
try_it_now.setVisibility(View.GONE);
bar.setVisibility(View.VISIBLE);
handler = new MyHandler(this);
handler.sendEmptyMessage(1);
} else {
try_it_now.setOnClickListener(v -> {
PictureTranslateListMainActivity.PictureTranslateListMainActivityStart(PictureTranslateStartActivity.this);
SharedPreferencesUtil.saveString(PictureTranslateStartActivity.this, KEY_FIRST_INSTALL_APP, "install");
finish();
});
}
}
public void setProgressBarProgress(int i) {
if (i >= 100) {
if (handler != null) {
handler.removeCallbacksAndMessages(null);
}
PictureTranslateListMainActivity.PictureTranslateListMainActivityStart(this);
return;
}
bar.setProgress(i);
}
@Override
protected void onDestroy() {
super.onDestroy();
if (handler != null) {
handler.removeCallbacksAndMessages(null);
}
}
private static class MyHandler extends Handler {
private final WeakReference<Activity> weakReference;
private int i = 0;
public MyHandler(Activity activity) {
super();
weakReference = new WeakReference<>(activity);
}
@Override
public void handleMessage(@NonNull Message msg) {
super.handleMessage(msg);
sendEmptyMessageDelayed(1, 10);
Activity activity = weakReference.get();
if (activity instanceof PictureTranslateStartActivity) {
((PictureTranslateStartActivity) activity).setProgressBarProgress(i++);
}
}
}
}
package com.ads.cal.picturetranslate.adapter;
import android.content.Context;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.PopupWindow;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.camera.core.ExperimentalGetImage;
import androidx.recyclerview.widget.RecyclerView;
import com.ads.cal.picturetranslate.PictureApplication;
import com.ads.cal.picturetranslate.R;
import com.ads.cal.picturetranslate.ThreadUtils;
import com.ads.cal.picturetranslate.Util;
import com.ads.cal.picturetranslate.activity.PictureTranslateResultActivity;
import com.ads.cal.picturetranslate.bean.PictureTranslateBean;
import com.bumptech.glide.Glide;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.List;
@ExperimentalGetImage
public class PictureTranslateListAdapter extends RecyclerView.Adapter<PictureTranslateListAdapter.MyViewHolder> {
private WeakReference<Context> weakReference;
private ArrayList<PictureTranslateBean> data;
public PictureTranslateListAdapter(Context context, ArrayList<PictureTranslateBean> data) {
if (weakReference == null) {
weakReference = new WeakReference<>(context);
}
this.data = data;
}
public void updateData(PictureTranslateBean bean) {
if (data == null) {
data = new ArrayList<>();
} else {
data.add(0, bean);
}
notifyItemChanged(0);
}
public void updateData(List<PictureTranslateBean> beans) {
if (data == null) {
data = new ArrayList<>();
} else {
data.addAll(beans);
}
notifyDataSetChanged();
}
public boolean isDataEmpty() {
return null == data || data.isEmpty();
}
@NonNull
@Override
public MyViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
View view = LayoutInflater.from(weakReference.get()).inflate(R.layout.picture_translate_list_item, null, false);
return new MyViewHolder(view);
}
@Override
public void onBindViewHolder(@NonNull MyViewHolder holder, int position) {
PictureTranslateBean bean = data.get(position);
holder.itemView.setOnLongClickListener(v -> {
showPopupWindow(v, bean);
return true;
});
holder.itemView.setOnClickListener(v -> PictureTranslateResultActivity.startPictureTranslateResultActivity(weakReference.get(), bean));
if (bean == null) {
return;
}
holder.setImage(weakReference.get(), bean.getImageUrl());
holder.setTitle(bean.getTitle());
holder.setTime(bean.getTime());
}
@Override
public int getItemCount() {
return data == null ? 0 : data.size();
}
private void showPopupWindow(View anchorView, PictureTranslateBean bean) {
// 获取 item 在屏幕上的坐标
int[] location = new int[2];
anchorView.getLocationOnScreen(location);
// 设置 PopupWindow 的位置
int xOffset = 0;
int yOffset = 0;
int height = Util.getScreenHeight(weakReference.get());
View contentView = null;
TextView delete = null;
if (location[1] > height / 2) {
contentView = LayoutInflater.from(weakReference.get()).inflate(R.layout.delete_popuwindo_bottom_layout, null, false);
xOffset = anchorView.getWidth() / 2; // X 偏移
yOffset = location[1]; // Y 偏移
delete = contentView.findViewById(R.id.delete);
} else {
contentView = LayoutInflater.from(weakReference.get()).inflate(R.layout.delete_popuwindo_top_layout, null, false);
xOffset = anchorView.getWidth() / 2; // X 偏移
yOffset = location[1] + anchorView.getHeight() / 2; // Y 偏移
delete = contentView.findViewById(R.id.delete);
}
PopupWindow popupWindow = new PopupWindow(weakReference.get());
popupWindow.setContentView(contentView);
// 设置PopupWindow的背景为透明
popupWindow.setBackgroundDrawable(weakReference.get().getResources().getDrawable(android.R.color.transparent));
// 设置PopupWindow可以获取焦点,这样可以响应触摸事件
popupWindow.setFocusable(true);
popupWindow.showAtLocation(anchorView, Gravity.NO_GRAVITY, xOffset, yOffset);
delete.setOnClickListener(v -> {
// 处理删除按钮点击事件,删除对应的 item
deleteItem(bean);
popupWindow.dismiss();
});
}
private void deleteItem(PictureTranslateBean bean) {
int position = data.indexOf(bean);
// 从数据集中移除对应的 item 数据
data.remove(bean);
// 通知 RecyclerView 刷新数据
notifyItemRemoved(position);
notifyItemRangeChanged(position, getItemCount());
ThreadUtils.startRunnable(() -> PictureApplication.getDataBaseManager().deleteData(bean.getTime()));
}
static class MyViewHolder extends RecyclerView.ViewHolder {
private final ImageView imageView;
private final TextView mTitle;
private final TextView time;
public MyViewHolder(@NonNull View itemView) {
super(itemView);
imageView = itemView.findViewById(R.id.item_img);
mTitle = itemView.findViewById(R.id.item_title);
time = itemView.findViewById(R.id.item_time);
}
public void setImage(Context context, String imageUrl) {
// 使用Glide加载图片
Glide.with(context)
.load(imageUrl)
.into(imageView);
}
public void setTitle(String title) {
mTitle.setText(title);
}
public void setTime(long createTime) {
time.setText(Util.getDataTime(createTime));
}
}
}
package com.ads.cal.picturetranslate.base;
import android.os.Bundle;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentActivity;
import androidx.fragment.app.FragmentManager;
import androidx.fragment.app.FragmentTransaction;
import com.ads.cal.picturetranslate.R;
import com.ads.cal.picturetranslate.fragment.LoadingFragment;
public class BaseActivity extends FragmentActivity {
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
getWindow().setStatusBarColor(getColor(R.color.color_0073FF)); // 设置颜色
}
@Override
protected void onResume() {
super.onResume();
}
@Override
protected void onStop() {
super.onStop();
}
@Override
protected void onStart() {
super.onStart();
}
@Override
protected void onRestart() {
super.onRestart();
}
@Override
protected void onDestroy() {
super.onDestroy();
}
protected void showLoadingFragment(LoadingFragment fragment) {
FragmentManager fragmentManager = getSupportFragmentManager();
if (!fragment.isAdded()) {
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
fragmentTransaction.add(android.R.id.content, fragment);
fragmentTransaction.addToBackStack("loadingFragment");
fragmentTransaction.commit();
}
}
protected void hideLoadingFragment() {
FragmentManager fragmentManager = getSupportFragmentManager();
if (fragmentManager.getBackStackEntryCount() > 0) {
fragmentManager.popBackStack();
}
}
protected void showFragment(Fragment fragment, int containerViewId) {
FragmentManager fragmentManager = getSupportFragmentManager();
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
if (!fragment.isAdded()) {
fragmentTransaction.add(containerViewId, fragment);
fragmentTransaction.commit();
} else {
fragmentTransaction.show(fragment);
}
}
protected void hineFragment(Fragment fragment) {
FragmentManager fragmentManager = getSupportFragmentManager();
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
fragmentTransaction.hide(fragment);
}
}
package com.ads.cal.picturetranslate.bean;
import java.io.Serializable;
public class PictureTranslateBean implements Serializable {
private String imageUrl;
private String title;
private long time;
public void setImageUrl(String imageUrl) {
this.imageUrl = imageUrl;
}
public void setTitle(String title) {
this.title = title;
}
public void setTime(long time) {
this.time = time;
}
public long getTime() {
return time;
}
public String getImageUrl() {
return imageUrl;
}
public String getTitle() {
return title;
}
}
package com.ads.cal.picturetranslate.customeview;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.util.AttributeSet;
import android.view.View;
public class CustomProgressBar extends View {
private Paint backgroundPaint; // 进度条背景画笔
private Paint progressBarPaint; // 进度条画笔
private int progress; // 当前进度
private int maxProgress = 100; // 最大进度
private int progressBarHeight = 20; // 进度条高度
public CustomProgressBar(Context context) {
super(context);
init();
}
public CustomProgressBar(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
private void init() {
backgroundPaint = new Paint();
backgroundPaint.setColor(0xFFDFEFFF); // 进度条背景颜色
progressBarPaint = new Paint();
progressBarPaint.setColor(0xFF0075FF); // 进度条颜色
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
// 绘制进度条背景
canvas.drawRect(0, 0, getWidth(), progressBarHeight, backgroundPaint);
// 计算进度条的宽度
int progressBarWidth = (int) ((float) progress / maxProgress * getWidth());
// 绘制进度条
canvas.drawRect(0, 0, progressBarWidth, progressBarHeight, progressBarPaint);
}
// 设置进度
public void setProgress(int progress) {
if (progress >= 0 && progress <= maxProgress) {
this.progress = progress;
invalidate(); // 重新绘制进度条
}
}
}
package com.ads.cal.picturetranslate.db;
import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.database.SQLException;
import android.database.sqlite.SQLiteDatabase;
import com.ads.cal.picturetranslate.LogUtils;
import com.ads.cal.picturetranslate.bean.PictureTranslateBean;
import java.util.ArrayList;
import java.util.List;
public class DataBaseManager {
private final PictureTranslateDataBaseHelper dbHelper;
private SQLiteDatabase database;
public DataBaseManager(Context context) {
dbHelper = new PictureTranslateDataBaseHelper(context);
}
// 打开数据库连接
public void open() throws SQLException {
database = dbHelper.getWritableDatabase();
}
// 关闭数据库连接
public void close() {
dbHelper.close();
}
// 插入数据
public long insertData(String imageUrl, String title, long createTime) {
ContentValues values = new ContentValues();
values.put(PictureTranslateDataBaseHelper.COLUMN_IMAGE_URL, imageUrl);
values.put(PictureTranslateDataBaseHelper.COLUMN_TITLE, title);
values.put(PictureTranslateDataBaseHelper.COLUMN_TIME, createTime);
return database.insert(PictureTranslateDataBaseHelper.TABLE_NAME, null, values);
}
// 查询数据
public List<PictureTranslateBean> queryData() {
String[] columns = {PictureTranslateDataBaseHelper.COLUMN_IMAGE_URL, PictureTranslateDataBaseHelper.COLUMN_TITLE, PictureTranslateDataBaseHelper.COLUMN_TIME};
Cursor cursor = database.query(PictureTranslateDataBaseHelper.TABLE_NAME, columns, null, null, null, null, null);
List<PictureTranslateBean> dataList = new ArrayList<>();
if (cursor != null) {
try {
while (cursor.moveToNext()) {
String imageUrl = cursor.getString(cursor.getColumnIndex(PictureTranslateDataBaseHelper.COLUMN_IMAGE_URL));
String title = cursor.getString(cursor.getColumnIndex(PictureTranslateDataBaseHelper.COLUMN_TITLE));
long time = cursor.getLong(cursor.getColumnIndex(PictureTranslateDataBaseHelper.COLUMN_TIME));
// 创建 PictureTranslateData 对象并添加到列表中
PictureTranslateBean data = new PictureTranslateBean();
data.setTime(time);
data.setTitle(title);
data.setImageUrl(imageUrl);
dataList.add(data);
}
} catch (Exception e) {
LogUtils.e(e.getMessage());
} finally {
cursor.close(); // 记得关闭 Cursor
}
}
return dataList;
}
// 更新数据
public int updateData(String imageUrl, String title, long createTime) {
ContentValues values = new ContentValues();
values.put(PictureTranslateDataBaseHelper.COLUMN_IMAGE_URL, imageUrl);
values.put(PictureTranslateDataBaseHelper.COLUMN_TITLE, title);
values.put(PictureTranslateDataBaseHelper.COLUMN_TIME, createTime);
return database.update(PictureTranslateDataBaseHelper.TABLE_NAME, values, "createTime=" + createTime, null);
}
public boolean deleteData(long createTime) {
int rowsAffected = database.delete(
PictureTranslateDataBaseHelper.TABLE_NAME,
PictureTranslateDataBaseHelper.COLUMN_TIME + "=" + createTime,
null);
return rowsAffected > 0;
}
public boolean deleteData() {
int rowsAffected = database.delete(
PictureTranslateDataBaseHelper.TABLE_NAME,
null,
null);
return rowsAffected > 0;
}
}
package com.ads.cal.picturetranslate.db;
import android.content.Context;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import androidx.annotation.Nullable;
public class PictureTranslateDataBaseHelper extends SQLiteOpenHelper {
// 数据库名称和版本
private static final String DATABASE_NAME = "picture_translate_database.db";
private static final int DATABASE_VERSION = 1;
// 表名和列名
public static final String TABLE_NAME = "picture_translate";
public static final String COLUMN_IMAGE_URL = "imageUrl";
public static final String COLUMN_TITLE = "title";
public static final String COLUMN_TIME = "createTime";
public PictureTranslateDataBaseHelper(@Nullable Context context) {
super(context, DATABASE_NAME, null, DATABASE_VERSION);
}
@Override
public void onCreate(SQLiteDatabase db) {
db.execSQL(CREATE_TABLE);
}
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
}
@Override
public void onOpen(SQLiteDatabase db) {
super.onOpen(db);
}
// 创建表的 SQL 语句
private static final String CREATE_TABLE = "CREATE TABLE " + TABLE_NAME + " (" +
"_id INTEGER PRIMARY KEY AUTOINCREMENT, " +
COLUMN_IMAGE_URL + " TEXT, " +
COLUMN_TITLE + " TEXT, " +
COLUMN_TIME + " INTEGER);";
}
package com.ads.cal.picturetranslate.fragment;
import android.animation.ObjectAnimator;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.animation.LinearInterpolator;
import android.widget.ImageView;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;
import com.ads.cal.picturetranslate.R;
public class LoadingFragment extends Fragment {
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
private ObjectAnimator rotationAnimator;
@Nullable
@Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.loading_fragment, null, false);
ImageView loading = view.findViewById(R.id.loading);
rotationAnimator = ObjectAnimator.ofFloat(loading, "rotation", 0f, 360f);
rotationAnimator.setDuration(2000); // 旋转一周的时间(毫秒)
rotationAnimator.setInterpolator(new LinearInterpolator()); // 线性插值器使动画匀速旋转
rotationAnimator.setRepeatCount(ObjectAnimator.INFINITE); // 设置为无限循环
rotationAnimator.start(); // 启动动画
return view;
}
@Override
public void onResume() {
super.onResume();
}
@Override
public void onDestroy() {
super.onDestroy();
}
@Override
public void onDestroyView() {
super.onDestroyView();
if (rotationAnimator != null) {
rotationAnimator.cancel();
}
}
}
package com.ads.cal.picturetranslate.fragment;
import android.content.Intent;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.RelativeLayout;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;
import com.ads.cal.picturetranslate.R;
import com.ads.cal.picturetranslate.activity.AboutActivity;
public class SettingFragment extends Fragment {
@Nullable
@Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
return initView(inflater);
}
private View initView(LayoutInflater inflater) {
View view = inflater.inflate(R.layout.fragment_setting, null, false);
RelativeLayout share_layout = view.findViewById(R.id.share_layout);
RelativeLayout email_layout = view.findViewById(R.id.email_layout);
RelativeLayout policy_layout = view.findViewById(R.id.policy_layout);
RelativeLayout about_layout = view.findViewById(R.id.about_layout);
share_layout.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent shareIntent = new Intent(Intent.ACTION_SEND);
shareIntent.setType("text/plain");
shareIntent.putExtra(Intent.EXTRA_TEXT, "分享内容"); // 设置要分享的内容
startActivity(Intent.createChooser(shareIntent, "分享到"));
}
});
email_layout.setOnClickListener(v -> {
// 创建一个Intent,指定Action为发送邮件
Intent emailIntent = new Intent(Intent.ACTION_SEND);
// 设置邮件的类型为“text/plain”
emailIntent.setType("text/plain");
// 添加收件人(可选)
emailIntent.putExtra(Intent.EXTRA_EMAIL, new String[]{"zl@cc.com"});
// 启动邮件客户端
startActivity(Intent.createChooser(emailIntent, "选择邮件客户端"));
});
policy_layout.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
}
});
about_layout.setOnClickListener(v -> AboutActivity.startAboutActivity(getContext()));
return view;
}
}
<vector xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:aapt="http://schemas.android.com/aapt"
android:width="108dp"
android:height="108dp"
android:viewportWidth="108"
android:viewportHeight="108">
<path android:pathData="M31,63.928c0,0 6.4,-11 12.1,-13.1c7.2,-2.6 26,-1.4 26,-1.4l38.1,38.1L107,108.928l-32,-1L31,63.928z">
<aapt:attr name="android:fillColor">
<gradient
android:endX="85.84757"
android:endY="92.4963"
android:startX="42.9492"
android:startY="49.59793"
android:type="linear">
<item
android:color="#44000000"
android:offset="0.0" />
<item
android:color="#00000000"
android:offset="1.0" />
</gradient>
</aapt:attr>
</path>
<path
android:fillColor="#FFFFFF"
android:fillType="nonZero"
android:pathData="M65.3,45.828l3.8,-6.6c0.2,-0.4 0.1,-0.9 -0.3,-1.1c-0.4,-0.2 -0.9,-0.1 -1.1,0.3l-3.9,6.7c-6.3,-2.8 -13.4,-2.8 -19.7,0l-3.9,-6.7c-0.2,-0.4 -0.7,-0.5 -1.1,-0.3C38.8,38.328 38.7,38.828 38.9,39.228l3.8,6.6C36.2,49.428 31.7,56.028 31,63.928h46C76.3,56.028 71.8,49.428 65.3,45.828zM43.4,57.328c-0.8,0 -1.5,-0.5 -1.8,-1.2c-0.3,-0.7 -0.1,-1.5 0.4,-2.1c0.5,-0.5 1.4,-0.7 2.1,-0.4c0.7,0.3 1.2,1 1.2,1.8C45.3,56.528 44.5,57.328 43.4,57.328L43.4,57.328zM64.6,57.328c-0.8,0 -1.5,-0.5 -1.8,-1.2s-0.1,-1.5 0.4,-2.1c0.5,-0.5 1.4,-0.7 2.1,-0.4c0.7,0.3 1.2,1 1.2,1.8C66.5,56.528 65.6,57.328 64.6,57.328L64.6,57.328z"
android:strokeWidth="1"
android:strokeColor="#00000000" />
</vector>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<corners android:radius="180dp"/>
<solid android:color="#3B3F43"/>
</shape>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="@color/white"/>
<item android:drawable="@color/color_e9eaef" android:state_focused="true"/>
<item android:drawable="@color/color_e9eaef" android:state_checked="true"/>
</selector>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item android:id="@android:id/background">
<shape android:shape="rectangle">
<solid android:color="#DFEFFF" /> <!-- 背景颜色 -->
<corners android:radius="10dp" /> <!-- 圆角 -->
</shape>
</item>
<item android:id="@android:id/progress">
<clip>
<shape android:shape="rectangle">
<solid android:color="#0075FF" /> <!-- 进度条颜色 -->
<corners android:radius="10dp" /> <!-- 圆角 -->
</shape>
</clip>
</item>
</layer-list>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<corners android:radius="180dp"/>
<solid android:color="#DFEFFF"/>
</shape>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<corners android:radius="180dp"/>
<gradient android:startColor="#00A3FF" android:endColor="#0075FF"/>
</shape>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<solid android:color="@color/white"/>
<stroke android:color="#EFF4F9" android:width="1dp"/>
<corners android:radius="5dp"/>
</shape>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<solid android:color="@color/white"/>
<stroke android:width="2dp" android:color="@color/white"/>
<corners android:radius="4dp"/>
</shape>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="108dp"
android:height="108dp"
android:viewportWidth="108"
android:viewportHeight="108">
<path
android:fillColor="#3DDC84"
android:pathData="M0,0h108v108h-108z" />
<path
android:fillColor="#00000000"
android:pathData="M9,0L9,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,0L19,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M29,0L29,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M39,0L39,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M49,0L49,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M59,0L59,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M69,0L69,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M79,0L79,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M89,0L89,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M99,0L99,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,9L108,9"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,19L108,19"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,29L108,29"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,39L108,39"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,49L108,49"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,59L108,59"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,69L108,69"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,79L108,79"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,89L108,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,99L108,99"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,29L89,29"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,39L89,39"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,49L89,49"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,59L89,59"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,69L89,69"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,79L89,79"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M29,19L29,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M39,19L39,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M49,19L49,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M59,19L59,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M69,19L69,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M79,19L79,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
</vector>
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:clipChildren="false">
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/listData"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_below="@+id/title" />
<TextView
android:id="@+id/title"
android:layout_width="match_parent"
android:layout_height="53dp"
android:layout_marginTop="19dp"
android:background="@color/white"
android:gravity="center_vertical"
android:paddingStart="14dp"
android:paddingEnd="14dp"
android:text="@string/app_recent"
android:textColor="#1C1C1C"
android:textSize="20sp"
android:textStyle="bold" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="53dp"
android:layout_marginTop="19dp"
android:gravity="end|center_vertical"
android:orientation="horizontal">
<ImageView
android:id="@+id/delete"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@mipmap/app_icon_delete"
android:layout_marginEnd="30dp" />
<ImageView
android:id="@+id/select_"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="30dp"
android:src="@mipmap/app_icon_photo" />
</LinearLayout>
<ImageView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:scaleType="fitXY"
android:src="@mipmap/app_status_bar_bg" />
<LinearLayout
android:id="@+id/not_data_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="center"
android:clipChildren="false"
android:gravity="center"
android:orientation="vertical"
android:visibility="gone">
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@mipmap/app_icon_empty" />
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:text="@string/app_not_data" />
</LinearLayout>
<FrameLayout
android:id="@+id/setting_content"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_below="@+id/title"
android:background="@color/white"
android:visibility="gone" />
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:background="@mipmap/app_nav_bg">
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:layout_marginStart="70dp"
android:src="@mipmap/app_icon_photo" />
<ImageView
android:id="@+id/setting"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentEnd="true"
android:layout_centerVertical="true"
android:layout_marginEnd="70dp"
android:src="@mipmap/app_icon_setting" />
</RelativeLayout>
<TextView
android:id="@+id/hint"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_above="@+id/take_photo"
android:layout_centerHorizontal="true"
android:layout_marginBottom="10dp"
android:background="@mipmap/app_takephoto_hint"
android:gravity="center"
android:paddingBottom="15dp"
android:text="@string/app_take_photo_clk_hint"
android:textColor="@color/white"
android:textSize="14sp" />
<ImageView
android:id="@+id/take_photo"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_centerHorizontal="true"
android:layout_marginBottom="17dp"
android:src="@mipmap/app_icon_play" />
</RelativeLayout>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:orientation="vertical">
<pl.droidsonroids.gif.GifImageView
android:id="@+id/image"
android:layout_width="100dp"
android:layout_height="100dp"
/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="20sp"
android:textStyle="bold"
android:textColor="#5B5D62"
android:layout_marginTop="20dp"
android:text="@string/app_name"/>
<TextView
android:id="@+id/versioncode"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="14sp"
android:textColor="#8F9197"/>
</LinearLayout>
<TextView
android:id="@+id/title"
android:layout_width="match_parent"
android:layout_height="53dp"
android:layout_marginTop="19dp"
android:background="@color/white"
android:gravity="center_vertical"
android:paddingStart="14dp"
android:paddingEnd="14dp"
android:text="@string/app_about"
android:textColor="#1C1C1C"
android:textSize="20sp"
android:textStyle="bold" />
<ImageView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:scaleType="fitXY"
android:src="@mipmap/app_status_bar_bg" />
</FrameLayout>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent">
<androidx.camera.view.PreviewView
android:id="@+id/preview"
android:layout_width="match_parent"
android:layout_height="match_parent" />
<ImageView
android:id="@+id/close"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="20dp"
android:layout_marginStart="20dp"
android:src="@mipmap/app_icon_close"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<ImageView
android:id="@+id/select_phone"
android:layout_width="34dp"
android:layout_height="34dp"
android:layout_marginEnd="51dp"
android:background="@drawable/app_white_corner_4"
android:padding="2dp"
android:scaleType="centerCrop"
app:layout_constraintBottom_toBottomOf="@+id/take_photo"
app:layout_constraintEnd_toStartOf="@+id/take_photo"
app:layout_constraintTop_toTopOf="@+id/take_photo" />
<ImageView
android:id="@+id/take_photo"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="30dp"
android:src="@mipmap/app_icon_take_photo"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<com.edmodo.cropper.CropImageView
android:id="@+id/crop"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_below="@+id/top_layout"
android:layout_gravity="center" />
<RelativeLayout
android:id="@+id/top_layout"
android:layout_width="match_parent"
android:layout_height="53dp"
android:background="#0075FF">
<ImageView
android:id="@+id/back"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_centerVertical="true"
android:paddingStart="14dp"
android:paddingEnd="14dp"
android:src="@mipmap/app_icon_back" />
<LinearLayout
android:id="@+id/ok_crop"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_alignParentEnd="true"
android:layout_centerVertical="true"
android:gravity="center"
android:orientation="horizontal"
android:paddingStart="20dp"
android:paddingEnd="20dp">
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="6dp"
android:src="@mipmap/app_icon_ok" />
<TextView
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:gravity="center"
android:text="@string/app_corp"
android:textColor="@color/white"
android:textSize="18sp"
android:textStyle="bold" />
</LinearLayout>
</RelativeLayout>
</RelativeLayout>
This diff is collapsed.
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent">
<ImageView
android:id="@+id/bg"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/black"/>
<ImageView
android:id="@+id/close"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="20dp"
android:layout_marginTop="20dp"
android:src="@mipmap/app_icon_close"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<LinearLayout
android:id="@+id/crop"
android:layout_width="match_parent"
android:layout_height="56dp"
android:layout_marginStart="14dp"
android:layout_marginEnd="14dp"
android:layout_marginBottom="10dp"
android:background="@drawable/app_3b3f43_corner_180"
android:gravity="center"
android:orientation="horizontal"
android:text="@string/app_corp"
android:textColor="@color/white"
android:textSize="18sp"
android:textStyle="bold"
app:layout_constraintBottom_toTopOf="@+id/extract"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent">
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="10dp"
android:src="@mipmap/app_icon_crop" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/app_corp"
android:textColor="@color/white"
android:textSize="18sp"
android:textStyle="bold" />
</LinearLayout>
<LinearLayout
android:id="@+id/extract"
android:layout_width="match_parent"
android:layout_height="56dp"
android:layout_marginStart="14dp"
android:layout_marginEnd="14dp"
android:layout_marginBottom="20dp"
android:background="@drawable/app_extract_corner_180"
android:gravity="center"
android:text="@string/app_extract"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent">
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="10dp"
android:src="@mipmap/app_icon_scan" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="center"
android:text="@string/app_extract"
android:textColor="@color/white"
android:textSize="18sp"
android:textStyle="bold" />
</LinearLayout>
</androidx.constraintlayout.widget.ConstraintLayout>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<pl.droidsonroids.gif.GifImageView
android:id="@+id/gifimg"
android:layout_width="100dp"
android:layout_height="100dp"
android:layout_above="@+id/welcome"
android:layout_centerHorizontal="true"
android:layout_marginBottom="30dp" />
<TextView
android:id="@+id/welcome"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_above="@+id/name"
android:layout_marginBottom="10dp"
android:gravity="center"
android:text="@string/app_welcome"
android:textColor="#5B5D62"
android:textSize="15sp" />
<TextView
android:id="@+id/name"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:gravity="center"
android:text="@string/app_name"
android:textColor="@color/black"
android:textSize="20sp"
android:textStyle="bold" />
<TextView
android:id="@+id/try_it_now"
android:layout_width="match_parent"
android:layout_height="56dp"
android:layout_alignParentBottom="true"
android:layout_marginStart="20dp"
android:layout_marginEnd="20dp"
android:layout_marginBottom="34dp"
android:background="@drawable/app_extract_corner_180"
android:gravity="center"
android:text="@string/app_try_it_now"
android:textColor="@color/white"
android:textSize="18sp"
android:textStyle="bold" />
<ProgressBar
android:id="@+id/horizontalProgressBar"
style="@android:style/Widget.ProgressBar.Horizontal"
android:layout_width="match_parent"
android:layout_height="8dp"
android:layout_alignParentBottom="true"
android:layout_marginStart="30dp"
android:layout_marginEnd="30dp"
android:layout_marginBottom="60dp"
android:visibility="gone"
android:max="100"
android:progressDrawable="@drawable/app_custom_progress_bar" />
</RelativeLayout>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
<TextView
android:id="@+id/delete"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@mipmap/app_delete_bottom"
android:gravity="center_vertical"
android:textSize="16sp"
android:textColor="@color/white"
android:paddingBottom="8dp"
android:paddingStart="20dp"
android:text="@string/app_delete">
</TextView>
</LinearLayout>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
<TextView
android:id="@+id/delete"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@mipmap/app_delete_top"
android:gravity="center_vertical"
android:textSize="16sp"
android:textColor="@color/white"
android:paddingTop="8dp"
android:paddingStart="20dp"
android:text="@string/app_delete">
</TextView>
</LinearLayout>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:paddingStart="20dp"
android:paddingEnd="20dp">
<RelativeLayout
android:id="@+id/share_layout"
android:layout_width="match_parent"
android:layout_height="60dp"
android:layout_marginTop="10dp"
android:background="@drawable/app_setting_item" >
<ImageView
android:id="@+id/share"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:src="@mipmap/app_share_setting"
android:layout_marginStart="14dp"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:layout_toEndOf="@+id/share"
android:textColor="@color/black"
android:textSize="16sp"
android:text="@string/app_share_text_extraction"
android:layout_marginStart="14dp"/>
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentEnd="true"
android:layout_marginEnd="14dp"
android:src="@mipmap/app_icon_arrow"
android:layout_centerVertical="true"/>
</RelativeLayout>
<RelativeLayout
android:id="@+id/email_layout"
android:layout_width="match_parent"
android:layout_height="60dp"
android:layout_marginTop="10dp"
android:background="@drawable/app_setting_item" >
<ImageView
android:id="@+id/email"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:src="@mipmap/app_email"
android:layout_marginStart="14dp"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:layout_toEndOf="@+id/email"
android:textColor="@color/black"
android:textSize="16sp"
android:text="@string/app_email"
android:layout_marginStart="14dp"/>
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentEnd="true"
android:layout_marginEnd="14dp"
android:src="@mipmap/app_icon_arrow"
android:layout_centerVertical="true"/>
</RelativeLayout>
<RelativeLayout
android:id="@+id/policy_layout"
android:layout_width="match_parent"
android:layout_height="60dp"
android:layout_marginTop="10dp"
android:background="@drawable/app_setting_item" >
<ImageView
android:id="@+id/policy"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:src="@mipmap/app_policy"
android:layout_marginStart="14dp"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:layout_toEndOf="@+id/policy"
android:textColor="@color/black"
android:textSize="16sp"
android:text="@string/app_policy"
android:layout_marginStart="14dp"/>
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentEnd="true"
android:layout_marginEnd="14dp"
android:src="@mipmap/app_icon_arrow"
android:layout_centerVertical="true"/>
</RelativeLayout>
<RelativeLayout
android:id="@+id/about_layout"
android:layout_width="match_parent"
android:layout_height="60dp"
android:layout_marginTop="10dp"
android:background="@drawable/app_setting_item" >
<ImageView
android:id="@+id/about"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:src="@mipmap/app_about"
android:layout_marginStart="14dp"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:layout_toEndOf="@+id/about"
android:textColor="@color/black"
android:textSize="16sp"
android:text="@string/app_about"
android:layout_marginStart="14dp"/>
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentEnd="true"
android:layout_marginEnd="14dp"
android:src="@mipmap/app_icon_arrow"
android:layout_centerVertical="true"/>
</RelativeLayout>
</LinearLayout>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:background="#4D000000"
android:orientation="vertical">
<ImageView
android:id="@+id/loading"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@mipmap/app_icon_loading"
android:layout_marginBottom="14dp"/>
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:textColor="@color/white"
android:textSize="18sp"
android:text="@string/app_extracting"
android:textStyle="bold"/>
</LinearLayout>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:paddingStart="14dp"
android:paddingTop="7dp"
android:paddingEnd="14dp"
android:paddingBottom="7dp">
<androidx.cardview.widget.CardView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:cardCornerRadius="3dp">
<ImageView
android:id="@+id/item_img"
android:layout_width="124dp"
android:layout_height="93dp"
android:scaleType="centerCrop" />
</androidx.cardview.widget.CardView>
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginStart="14dp">
<TextView
android:id="@+id/item_title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:ellipsize="end"
android:maxLines="3"
android:textColor="#1B1D22"
android:textSize="16sp" />
<TextView
android:id="@+id/item_time"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:textColor="#8F9197"
android:textSize="13sp" />
</RelativeLayout>
</LinearLayout>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@drawable/ic_launcher_background" />
<foreground android:drawable="@drawable/ic_launcher_foreground" />
<monochrome android:drawable="@drawable/ic_launcher_foreground" />
</adaptive-icon>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@drawable/ic_launcher_background" />
<foreground android:drawable="@drawable/ic_launcher_foreground" />
<monochrome android:drawable="@drawable/ic_launcher_foreground" />
</adaptive-icon>
\ No newline at end of file
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment