V1.0.0(1)完成版

This commit is contained in:
lihongwei 2025-04-02 15:24:10 +08:00
parent 3c9eb2b095
commit 8470c2a07a
65 changed files with 2250 additions and 18 deletions

BIN
app/ARDrawingSpace.jks Normal file

Binary file not shown.

View File

@ -18,4 +18,17 @@
# If you keep the line number information, uncomment this to
# hide the original source file name.
#-renamesourcefileattribute SourceFile
#-renamesourcefileattribute SourceFile
-keepclassmembers class com.ardrawing.ardrawingspace.MyApplication {
public static final java.lang.String DATABASE_NAME;
public static final int DATABASE_VERSION;
}
-keepclassmembers class * {
@androidx.room.Query <methods>;
}
-keep class com.ardrawing.ardrawingspace.room.AppDatabase { *; }
-keep class com.ardrawing.ardrawingspace.room.SpaceEntity { *; }
-keep class com.ardrawing.ardrawingspace.room.SpaceEntityDao { *; }

View File

@ -8,7 +8,6 @@
<uses-feature android:name="android.hardware.camera" />
<uses-permission android:name="android.permission.READ_MEDIA_IMAGES" />
<uses-permission
android:name="android.permission.WRITE_EXTERNAL_STORAGE"
android:maxSdkVersion="32" />
@ -26,6 +25,15 @@
tools:targetApi="31">
<activity
android:name=".activity.MainActivity"
android:exported="false" />
<activity
android:name=".activity.DrawingActivity"
android:exported="false" />
<activity
android:name=".activity.CategoryActivity"
android:exported="false" />
<activity
android:name=".activity.SplashActivity"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />

View File

@ -4,6 +4,8 @@ import android.app.Application;
import android.content.Context;
import android.content.SharedPreferences;
import com.ardrawing.ardrawingspace.util.FileParsingAndInitialization;
public class MyApplication extends Application {
public static MyApplication application;
public static final String DATABASE_NAME = "database";
@ -27,8 +29,8 @@ public class MyApplication extends Application {
}
private void initDatabase() {
// InitDatabase initDatabase = new InitDatabase(getContext());
// initDatabase.insertImagesToDatabase();
FileParsingAndInitialization fileParsingAndInitialization = new FileParsingAndInitialization(application);
fileParsingAndInitialization.insertImagesToDatabase();
}
public static Context getContext() {

View File

@ -0,0 +1,84 @@
package com.ardrawing.ardrawingspace.activity;
import android.os.Bundle;
import androidx.activity.EdgeToEdge;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.graphics.Insets;
import androidx.core.view.ViewCompat;
import androidx.core.view.WindowInsetsCompat;
import androidx.lifecycle.Observer;
import androidx.lifecycle.ViewModelProvider;
import androidx.recyclerview.widget.GridLayoutManager;
import com.ardrawing.ardrawingspace.R;
import com.ardrawing.ardrawingspace.adapter.SpaceRecyclerViewAdapter;
import com.ardrawing.ardrawingspace.databinding.ActivityCategoryBinding;
import com.ardrawing.ardrawingspace.room.SpaceEntity;
import com.ardrawing.ardrawingspace.util.ItemDecoration;
import com.ardrawing.ardrawingspace.viewmodel.SpaceViewModel;
import java.util.ArrayList;
import java.util.List;
public class CategoryActivity extends AppCompatActivity {
private ActivityCategoryBinding binding;
private SpaceRecyclerViewAdapter adapter;
private SpaceViewModel viewModel;
private String title;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
EdgeToEdge.enable(this);
binding = ActivityCategoryBinding.inflate(getLayoutInflater());
setContentView(binding.getRoot());
ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.main), (v, insets) -> {
Insets systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars());
v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom);
return insets;
});
initData();
initEvent();
}
private void initData() {
String path = getIntent().getStringExtra("imagePath");
if (path == null) {
finish();
return;
}
String[] parts = path.split("/");
title = parts[0].split("_")[1];
viewModel = new ViewModelProvider(this).get(SpaceViewModel.class);
binding.recyclerView.setLayoutManager(new GridLayoutManager(this, 2));
adapter = new SpaceRecyclerViewAdapter(this, new ArrayList<>(), 2);
binding.recyclerView.setAdapter(adapter);
binding.recyclerView.addItemDecoration(new ItemDecoration(35, 15, 20));
}
private void initEvent() {
binding.back.setOnClickListener(v -> finish());
binding.text.setText(title);
loadImage();
}
private void loadImage() {
viewModel
.getEntityByImagePath(title)
.observe(this, spaceEntityList -> {
adapter.updateItems(spaceEntityList);
});
}
@Override
protected void onDestroy() {
super.onDestroy();
binding = null;
}
}

View File

@ -0,0 +1,284 @@
package com.ardrawing.ardrawingspace.activity;
import android.Manifest;
import android.content.ContentValues;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.graphics.Matrix;
import android.graphics.PointF;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.os.Environment;
import android.provider.MediaStore;
import android.view.MotionEvent;
import android.view.View;
import android.widget.ImageView;
import android.widget.SeekBar;
import android.widget.Toast;
import androidx.activity.EdgeToEdge;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import androidx.camera.core.Camera;
import androidx.camera.core.CameraSelector;
import androidx.camera.core.ImageCapture;
import androidx.camera.core.ImageCaptureException;
import androidx.camera.core.Preview;
import androidx.camera.lifecycle.ProcessCameraProvider;
import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat;
import androidx.core.graphics.Insets;
import androidx.core.view.ViewCompat;
import androidx.core.view.WindowInsetsCompat;
import com.ardrawing.ardrawingspace.R;
import com.ardrawing.ardrawingspace.databinding.ActivityDrawingBinding;
import com.ardrawing.ardrawingspace.util.DocumentManipulationTool;
import com.ardrawing.ardrawingspace.util.PermissionTool;
import com.google.common.util.concurrent.ListenableFuture;
import org.jetbrains.annotations.Nullable;
public class DrawingActivity extends AppCompatActivity implements SeekBar.OnSeekBarChangeListener, View.OnTouchListener {
private ActivityDrawingBinding binding;
private static final int CAMERA_PERMISSION_REQUEST = 200;
private static final int STORAGE_PERMISSION_REQUEST = 201;
private static final int PICK_IMAGE_REQUEST = 202;
private static final int MODE_NONE = 0;
private static final int MODE_DRAG = 1;
private static final int MODE_ZOOM = 2;
private final Matrix matrix = new Matrix();
private final Matrix savedMatrix = new Matrix();
private final PointF startPoint = new PointF();
private float initialDistance;
private int mode = MODE_NONE;
private boolean isFlash = false;
private Camera camera;
private ImageCapture imageCapture;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
binding = ActivityDrawingBinding.inflate(getLayoutInflater());
setContentView(binding.getRoot());
EdgeToEdge.enable(this);
ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.main), (v, insets) -> {
Insets systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars());
v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom);
return insets;
});
setupEventListeners();
}
private void setupEventListeners() {
String imagePath = getIntent().getStringExtra("imagePath");
if (imagePath == null) {
finish();
return;
}
DocumentManipulationTool.loadImageByPath(imagePath, binding.imageView, this);
binding.imageView.setOnTouchListener(this);
binding.seekbar.setOnSeekBarChangeListener(this);
binding.photo.setOnClickListener(v -> handleImagePicker());
binding.flash.setOnClickListener(v -> toggleFlash());
binding.back.setOnClickListener(v -> finish());
binding.camera.setOnClickListener(v -> captureAndSaveImage());
checkAndRequestPermissions();
}
private void checkAndRequestPermissions() {
String[] permissions = getPermissions();
if (!PermissionTool.hasPermission(this, permissions)) {
PermissionTool.reqPermission(this, permissions, CAMERA_PERMISSION_REQUEST);
} else {
setupCamera();
}
}
private void setupCamera() {
ListenableFuture<ProcessCameraProvider> future = ProcessCameraProvider.getInstance(this);
future.addListener(() -> {
try {
ProcessCameraProvider provider = future.get();
Preview preview = new Preview.Builder().build();
preview.setSurfaceProvider(binding.preview.getSurfaceProvider());
imageCapture = new ImageCapture.Builder().build();
CameraSelector selector = new CameraSelector.Builder()
.requireLensFacing(CameraSelector.LENS_FACING_BACK)
.build();
provider.unbindAll();
camera = provider.bindToLifecycle(this, selector, preview, imageCapture);
} catch (Exception ignored) {
}
}, ContextCompat.getMainExecutor(this));
}
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
handlePermissionResult(requestCode, grantResults);
}
private void handlePermissionResult(int requestCode, int[] grantResults) {
if (requestCode == CAMERA_PERMISSION_REQUEST && PermissionTool.gotPermission(grantResults)) {
setupCamera();
} else if (requestCode == STORAGE_PERMISSION_REQUEST && PermissionTool.gotPermission(grantResults)) {
handleImagePicker();
} else {
showToast("Permission denied. Please enable it in Settings.");
}
}
private void captureAndSaveImage() {
if (imageCapture == null) {
showToast("Camera not Init");
return;
}
if (!hasCameraPermission()) {
requestCameraPermission();
return;
}
ImageCapture.OutputFileOptions options = new ImageCapture.OutputFileOptions.Builder(
getContentResolver(),
MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
prepareContentValues()
).build();
imageCapture.takePicture(options, ContextCompat.getMainExecutor(this), new ImageCapture.OnImageSavedCallback() {
@Override
public void onImageSaved(@NonNull ImageCapture.OutputFileResults output) {
showToast("Successful photo shoot");
}
@Override
public void onError(@NonNull ImageCaptureException exception) {
showToast("Photo shoot failed");
}
});
}
private void handleImagePicker() {
String[] permissions = PermissionTool.getPermission();
if (ContextCompat.checkSelfPermission(this, permissions[0]) != PackageManager.PERMISSION_GRANTED) {
ActivityCompat.requestPermissions(this, permissions, STORAGE_PERMISSION_REQUEST);
} else {
Intent intent = new Intent(Intent.ACTION_PICK, MediaStore.Images.Media.EXTERNAL_CONTENT_URI);
startActivityForResult(intent, PICK_IMAGE_REQUEST);
}
}
@Override
protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (requestCode == PICK_IMAGE_REQUEST && resultCode == RESULT_OK && data != null) {
Uri imageUri = data.getData();
if (imageUri != null) {
binding.imageView.setImageURI(imageUri);
}
}
}
@Override
public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
binding.imageView.setAlpha((100 - progress) / 100f);
}
@Override
public void onStartTrackingTouch(SeekBar seekBar) {
}
@Override
public void onStopTrackingTouch(SeekBar seekBar) {
}
@Override
public boolean onTouch(View v, MotionEvent event) {
ImageView view = (ImageView) v;
switch (event.getActionMasked()) {
case MotionEvent.ACTION_DOWN:
savedMatrix.set(matrix);
startPoint.set(event.getX(), event.getY());
mode = MODE_DRAG;
break;
case MotionEvent.ACTION_POINTER_DOWN:
initialDistance = DocumentManipulationTool.computeTouchDistance(event);
if (initialDistance > 10f) {
savedMatrix.set(matrix);
mode = MODE_ZOOM;
}
break;
case MotionEvent.ACTION_MOVE:
if (mode == MODE_DRAG) {
matrix.set(savedMatrix);
matrix.postTranslate(event.getX() - startPoint.x, event.getY() - startPoint.y);
} else if (mode == MODE_ZOOM && event.getPointerCount() >= 2) {
float newDist = DocumentManipulationTool.computeTouchDistance(event);
if (newDist > 10f) {
float scale = newDist / initialDistance;
matrix.set(savedMatrix);
matrix.postScale(scale, scale, view.getWidth() / 2f, view.getHeight() / 2f);
}
}
break;
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_POINTER_UP:
mode = MODE_NONE;
break;
}
view.setImageMatrix(matrix);
return true;
}
private void toggleFlash() {
if (camera != null) {
isFlash = !isFlash;
camera.getCameraControl().enableTorch(isFlash);
binding.flash.setImageResource(isFlash ? R.drawable.flash : R.drawable.un_flash);
}
}
private boolean hasCameraPermission() {
return ContextCompat.checkSelfPermission(this, Manifest.permission.CAMERA) == PackageManager.PERMISSION_GRANTED;
}
private void requestCameraPermission() {
ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.CAMERA}, CAMERA_PERMISSION_REQUEST);
}
private ContentValues prepareContentValues() {
ContentValues values = new ContentValues();
values.put(MediaStore.MediaColumns.DISPLAY_NAME, "photo_" + System.currentTimeMillis() + ".jpg");
values.put(MediaStore.MediaColumns.MIME_TYPE, "image/jpeg");
values.put(MediaStore.Images.Media.RELATIVE_PATH, Environment.DIRECTORY_PICTURES);
return values;
}
private String[] getPermissions() {
return Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU ?
new String[]{Manifest.permission.CAMERA, Manifest.permission.READ_MEDIA_IMAGES} :
new String[]{Manifest.permission.CAMERA, Manifest.permission.READ_EXTERNAL_STORAGE, Manifest.permission.WRITE_EXTERNAL_STORAGE};
}
private void showToast(String message) {
Toast.makeText(this, message, Toast.LENGTH_SHORT).show();
}
@Override
protected void onDestroy() {
super.onDestroy();
binding = null;
}
}

View File

@ -1,26 +1,166 @@
package com.ardrawing.ardrawingspace.activity;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import androidx.activity.EdgeToEdge;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.content.ContextCompat;
import androidx.core.graphics.Insets;
import androidx.core.view.ViewCompat;
import androidx.core.view.WindowInsetsCompat;
import com.ardrawing.ardrawingspace.R;
import com.ardrawing.ardrawingspace.adapter.ViewPagerAdapter;
import com.ardrawing.ardrawingspace.databinding.ActivityMainBinding;
import com.ardrawing.ardrawingspace.databinding.TabCustomBinding;
import com.google.android.material.tabs.TabLayout;
import com.google.android.material.tabs.TabLayoutMediator;
public class MainActivity extends AppCompatActivity {
private ActivityMainBinding viewBinding;
private static final int[] ACTIVE_ICONS = {
R.drawable.home, R.drawable.resource_import, R.drawable.collection
};
private static final int[] INACTIVE_ICONS = {
R.drawable.un_home, R.drawable.un_import, R.drawable.un_collection
};
private static final int COLOR_BLACK = R.color.black;
private static final int COLOR_GRAY = R.color.gray;
private static final TabConfig[] TAB_CONFIGS = {
new TabConfig(R.drawable.home, "Category", COLOR_BLACK),
new TabConfig(R.drawable.un_import, "Import", COLOR_GRAY),
new TabConfig(R.drawable.un_collection, "Favorite", COLOR_GRAY)
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
configureEdgeToEdge();
initializeBinding();
setupWindowInsets();
initData();
initEvent();
}
private void configureEdgeToEdge() {
EdgeToEdge.enable(this);
setContentView(R.layout.activity_main);
ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.main), (v, insets) -> {
Insets systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars());
v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom);
return insets;
}
private void initializeBinding() {
viewBinding = ActivityMainBinding.inflate(LayoutInflater.from(this));
setContentView(viewBinding.getRoot());
}
private void setupWindowInsets() {
View mainLayout = findViewById(R.id.main);
ViewCompat.setOnApplyWindowInsetsListener(mainLayout, (view, windowInsets) -> {
Insets bars = windowInsets.getInsets(WindowInsetsCompat.Type.systemBars());
view.setPadding(bars.left, bars.top, bars.right, bars.bottom);
return windowInsets;
});
}
private void initData() {
ViewPagerAdapter pagerAdapter = new ViewPagerAdapter(this);
viewBinding.viewPager.setAdapter(pagerAdapter);
}
private void initEvent() {
TabLayoutMediator mediator = new TabLayoutMediator(
viewBinding.tabLayout,
viewBinding.viewPager,
this::configureTabView
);
mediator.attach();
viewBinding.tabLayout.addOnTabSelectedListener(createTabListener());
}
private void configureTabView(TabLayout.Tab tab, int index) {
TabCustomBinding tabViewBinding = TabCustomBinding.inflate(LayoutInflater.from(this));
tab.setCustomView(tabViewBinding.getRoot());
TabConfig config = determineTabConfig(index);
tabViewBinding.imageView.setImageResource(config.iconId);
tabViewBinding.title.setText(config.label);
tabViewBinding.title.setTextColor(fetchColor(config.textColorId));
}
private TabLayout.OnTabSelectedListener createTabListener() {
return new TabLayout.OnTabSelectedListener() {
@Override
public void onTabSelected(TabLayout.Tab tab) {
refreshTabAppearance(tab, true);
}
@Override
public void onTabUnselected(TabLayout.Tab tab) {
refreshTabAppearance(tab, false);
}
@Override
public void onTabReselected(TabLayout.Tab tab) {
}
private void refreshTabAppearance(TabLayout.Tab tab, boolean active) {
View customView = tab.getCustomView();
if (customView != null) {
TabCustomBinding tabBinding = TabCustomBinding.bind(customView);
updateTabIcon(tabBinding, tab.getPosition(), active);
updateTabTextColor(tabBinding, active);
}
}
private void updateTabIcon(TabCustomBinding tabBinding, int pos, boolean active) {
int iconId = fetchIconResource(pos, active);
tabBinding.imageView.setImageResource(iconId);
}
private void updateTabTextColor(TabCustomBinding tabBinding, boolean active) {
int colorId = active ? COLOR_BLACK : COLOR_GRAY;
tabBinding.title.setTextColor(fetchColor(colorId));
}
private int fetchIconResource(int pos, boolean active) {
return active ? ACTIVE_ICONS[pos] : INACTIVE_ICONS[pos];
}
};
}
private TabConfig determineTabConfig(int index) {
if (index >= 0 && index < TAB_CONFIGS.length) {
return TAB_CONFIGS[index];
}
throw new IllegalStateException("Unexpected tab index: " + index);
}
private static class TabConfig {
final int iconId;
final String label;
final int textColorId;
TabConfig(int iconId, String label, int textColorId) {
this.iconId = iconId;
this.label = label;
this.textColorId = textColorId;
}
}
private int fetchColor(int colorRes) {
return ContextCompat.getColor(this, colorRes);
}
@Override
protected void onDestroy() {
super.onDestroy();
if (viewBinding != null) {
viewBinding = null;
}
}
}

View File

@ -0,0 +1,78 @@
package com.ardrawing.ardrawingspace.activity;
import android.content.Intent;
import android.os.Bundle;
import android.os.CountDownTimer;
import androidx.activity.EdgeToEdge;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.graphics.Insets;
import androidx.core.view.ViewCompat;
import androidx.core.view.WindowInsetsCompat;
import com.ardrawing.ardrawingspace.R;
import com.ardrawing.ardrawingspace.databinding.ActivitySplashBinding;
import com.bumptech.glide.Glide;
import com.bumptech.glide.load.resource.bitmap.RoundedCorners;
import kotlin.Unit;
import kotlin.jvm.functions.Function0;
import kotlin.jvm.functions.Function1;
public class SplashActivity extends AppCompatActivity {
private ActivitySplashBinding binding;
private static final long TOTAL_TIME = 1000;
private CountDownTimer countDownTimer;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
EdgeToEdge.enable(this);
binding = ActivitySplashBinding.inflate(getLayoutInflater());
setContentView(binding.getRoot());
ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.main), (v, insets) -> {
Insets systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars());
v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom);
return insets;
});
Glide.with(this)
.load(R.mipmap.ic_launcher)
.transform(new RoundedCorners(16))
.into(binding.splashImage);
countDownTimer = new CountDownTimer(TOTAL_TIME, 100) {
@Override
public void onTick(long millisUntilFinished) {
int percentage = (int) (100 - (float) millisUntilFinished / TOTAL_TIME * 100);
binding.progressBar.setProgress(percentage);
}
@Override
public void onFinish() {
startMain();
}
};
countDownTimer.start();
}
private void startMain() {
binding.progressBar.setProgress(100);
Intent intent = new Intent(SplashActivity.this, MainActivity.class);
startActivity(intent);
finish();
}
@Override
protected void onDestroy() {
super.onDestroy();
if (countDownTimer != null) {
countDownTimer.cancel();
}
binding = null;
}
}

View File

@ -0,0 +1,205 @@
package com.ardrawing.ardrawingspace.adapter;
import android.content.Context;
import android.content.Intent;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.DiffUtil;
import androidx.recyclerview.widget.RecyclerView;
import com.ardrawing.ardrawingspace.R;
import com.ardrawing.ardrawingspace.activity.CategoryActivity;
import com.ardrawing.ardrawingspace.activity.DrawingActivity;
import com.ardrawing.ardrawingspace.room.AppDatabase;
import com.ardrawing.ardrawingspace.room.SpaceEntity;
import com.ardrawing.ardrawingspace.util.AppExecutors;
import com.ardrawing.ardrawingspace.util.DeleteCallback;
import com.bumptech.glide.Glide;
import com.bumptech.glide.load.resource.bitmap.CenterCrop;
import com.bumptech.glide.load.resource.bitmap.RoundedCorners;
import com.bumptech.glide.request.RequestOptions;
import java.util.ArrayList;
import java.util.List;
public class SpaceRecyclerViewAdapter extends RecyclerView.Adapter<SpaceRecyclerViewAdapter.ItemViewHolder> {
private static final int DISPLAY_MODE_CATEGORY = 0;
private static final int DISPLAY_MODE_DRAWING = 1;
private static final int CORNER_RADIUS = 32;
private static final int PLACEHOLDER_RES = R.mipmap.placeholder;
private static final int LIKE_ICON = R.drawable.like;
private static final int UNLIKE_ICON = R.drawable.un_like;
private List<SpaceEntity> spaceEntityList;
private final Context context;
private final int displayMode;
private DeleteCallback deleteCallback;
public SpaceRecyclerViewAdapter(Context context, List<SpaceEntity> spaceEntityList, int displayMode) {
this.context = context;
this.spaceEntityList = new ArrayList<>(spaceEntityList);
this.displayMode = displayMode;
}
public void updateItems(List<SpaceEntity> newItems) {
DiffUtil.DiffResult diffResult = DiffUtil.calculateDiff(new SpaceDiffCallback(this.spaceEntityList, newItems));
this.spaceEntityList = new ArrayList<>(newItems);
diffResult.dispatchUpdatesTo(this);
}
public void setDeleteCallback(DeleteCallback deleteCallback) {
this.deleteCallback = deleteCallback;
}
@NonNull
@Override
public ItemViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
LayoutInflater inflater = LayoutInflater.from(context);
View itemView = inflater.inflate(R.layout.item_space, parent, false);
return new ItemViewHolder(itemView);
}
@Override
public void onBindViewHolder(@NonNull ItemViewHolder holder, int position) {
SpaceEntity currentItem = spaceEntityList.get(position);
String displayName = getTitle(currentItem);
holder.renderItem(currentItem, displayName, displayMode);
}
private String getTitle(SpaceEntity item) {
if (displayMode == DISPLAY_MODE_CATEGORY) {
String path = item.getImagePath();
String[] segments = path.split("/");
return segments.length > 0 ? segments[0].split("_")[1] : "";
}
return "";
}
@Override
public int getItemCount() {
return spaceEntityList.size();
}
public class ItemViewHolder extends RecyclerView.ViewHolder {
private final ImageView previewImage;
private final ImageView likeIcon;
private final ImageView deleteIcon;
private final TextView titleText;
public ItemViewHolder(@NonNull View view) {
super(view);
previewImage = view.findViewById(R.id.item_image);
likeIcon = view.findViewById(R.id.like);
deleteIcon = view.findViewById(R.id.delete);
titleText = view.findViewById(R.id.title);
}
void renderItem(SpaceEntity item, String displayName, int mode) {
if (mode == DISPLAY_MODE_CATEGORY) {
titleText.setText(displayName);
titleText.setVisibility(View.VISIBLE);
likeIcon.setVisibility(View.GONE);
deleteIcon.setVisibility(View.GONE);
} else if (mode == DISPLAY_MODE_DRAWING) {
titleText.setVisibility(View.GONE);
likeIcon.setVisibility(View.VISIBLE);
deleteIcon.setVisibility(View.VISIBLE);
}
loadImageContent(item.getImagePath());
updateFavoriteIcon(item);
attachListeners(item, mode);
}
private void loadImageContent(String path) {
String formattedPath = adjustImagePath(path);
Glide.with(context)
.load(formattedPath)
.placeholder(PLACEHOLDER_RES)
.apply(new RequestOptions()
.transform(new CenterCrop(), new RoundedCorners(CORNER_RADIUS)))
.into(previewImage);
}
private String adjustImagePath(String path) {
return path.startsWith("/data/user/") ? path : "file:///android_asset/" + path;
}
private void updateFavoriteIcon(SpaceEntity item) {
int iconRes = item.isFavorite() ? LIKE_ICON : UNLIKE_ICON;
likeIcon.setImageResource(iconRes);
}
private void attachListeners(SpaceEntity item, int mode) {
previewImage.setOnClickListener(v -> handleImageTap(item, mode));
likeIcon.setOnClickListener(v -> toggleFavorite(item));
deleteIcon.setOnClickListener(v -> onDeleteClicked(item));
}
private void onDeleteClicked(SpaceEntity item) {
int position = getAdapterPosition();
if (position != RecyclerView.NO_POSITION && deleteCallback != null) {
deleteCallback.onDelete(item);
}
}
private void handleImageTap(SpaceEntity item, int mode) {
Intent intent = createIntent(item, mode);
context.startActivity(intent);
}
private Intent createIntent(SpaceEntity item, int mode) {
Class<?> targetActivity = mode == DISPLAY_MODE_CATEGORY ? CategoryActivity.class : DrawingActivity.class;
return new Intent(context, targetActivity)
.putExtra("imagePath", item.getImagePath());
}
private void toggleFavorite(SpaceEntity item) {
item.setFavorite(!item.isFavorite());
persistItemUpdate(item);
notifyItemChanged(getAdapterPosition());
}
private void persistItemUpdate(SpaceEntity item) {
AppExecutors.getInstance().diskIO().execute(() -> {
AppDatabase db = AppDatabase.getInstance(context);
db.dao().update(item);
});
}
}
private static class SpaceDiffCallback extends DiffUtil.Callback {
private final List<SpaceEntity> oldList;
private final List<SpaceEntity> newList;
public SpaceDiffCallback(List<SpaceEntity> oldList, List<SpaceEntity> newList) {
this.oldList = oldList != null ? new ArrayList<>(oldList) : new ArrayList<>();
this.newList = newList != null ? new ArrayList<>(newList) : new ArrayList<>();
}
@Override
public int getOldListSize() {
return oldList.size();
}
@Override
public int getNewListSize() {
return newList.size();
}
@Override
public boolean areItemsTheSame(int oldItemPosition, int newItemPosition) {
return oldList.get(oldItemPosition).getId() == newList.get(newItemPosition).getId();
}
@Override
public boolean areContentsTheSame(int oldItemPosition, int newItemPosition) {
return oldList.get(oldItemPosition).equals(newList.get(newItemPosition));
}
}
}

View File

@ -0,0 +1,36 @@
package com.ardrawing.ardrawingspace.adapter;
import androidx.annotation.NonNull;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentActivity;
import androidx.viewpager2.adapter.FragmentStateAdapter;
import com.ardrawing.ardrawingspace.fragment.FavoriteFragment;
import com.ardrawing.ardrawingspace.fragment.HomeFragment;
import com.ardrawing.ardrawingspace.fragment.ImportFragment;
import java.util.ArrayList;
import java.util.List;
public class ViewPagerAdapter extends FragmentStateAdapter {
private final List<Fragment> fragmentList;
public ViewPagerAdapter(@NonNull FragmentActivity fragmentActivity) {
super(fragmentActivity);
fragmentList = new ArrayList<>();
fragmentList.add(new HomeFragment());
fragmentList.add(new ImportFragment());
fragmentList.add(new FavoriteFragment());
}
@NonNull
@Override
public Fragment createFragment(int position) {
return fragmentList.get(position);
}
@Override
public int getItemCount() {
return fragmentList.size();
}
}

View File

@ -0,0 +1,76 @@
package com.ardrawing.ardrawingspace.fragment;
import android.os.Bundle;
import androidx.fragment.app.Fragment;
import androidx.lifecycle.Observer;
import androidx.lifecycle.ViewModelProvider;
import androidx.recyclerview.widget.GridLayoutManager;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import com.ardrawing.ardrawingspace.R;
import com.ardrawing.ardrawingspace.adapter.SpaceRecyclerViewAdapter;
import com.ardrawing.ardrawingspace.databinding.FragmentFavoriteBinding;
import com.ardrawing.ardrawingspace.room.SpaceEntity;
import com.ardrawing.ardrawingspace.util.ItemDecoration;
import com.ardrawing.ardrawingspace.viewmodel.SpaceViewModel;
import java.util.ArrayList;
import java.util.List;
public class FavoriteFragment extends Fragment {
private FragmentFavoriteBinding binding;
private SpaceViewModel viewModel;
private SpaceRecyclerViewAdapter adapter;
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
binding = FragmentFavoriteBinding.inflate(inflater, container, false);
initData();
initEvent();
return binding.getRoot();
}
private void initData() {
viewModel = new ViewModelProvider(this).get(SpaceViewModel.class);
binding.recyclerView.setLayoutManager(new GridLayoutManager(getContext(), 2));
adapter = new SpaceRecyclerViewAdapter(requireContext(), new ArrayList<>(), 2);
binding.recyclerView.setAdapter(adapter);
binding.recyclerView.addItemDecoration(new ItemDecoration(35, 15, 20));
}
private void initEvent() {
loadLike();
}
private void loadLike() {
viewModel
.getFavorite()
.observe(getViewLifecycleOwner(), new Observer<List<SpaceEntity>>() {
@Override
public void onChanged(List<SpaceEntity> spaceEntityList) {
if (spaceEntityList.isEmpty()) {
binding.text.setVisibility(View.VISIBLE);
} else {
binding.text.setVisibility(View.GONE);
}
adapter.updateItems(spaceEntityList);
}
});
}
@Override
public void onDestroyView() {
super.onDestroyView();
binding = null;
}
}

View File

@ -0,0 +1,77 @@
package com.ardrawing.ardrawingspace.fragment;
import android.os.Bundle;
import androidx.fragment.app.Fragment;
import androidx.lifecycle.Observer;
import androidx.lifecycle.ViewModelProvider;
import androidx.recyclerview.widget.GridLayoutManager;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import com.ardrawing.ardrawingspace.R;
import com.ardrawing.ardrawingspace.adapter.SpaceRecyclerViewAdapter;
import com.ardrawing.ardrawingspace.databinding.FragmentFavoriteBinding;
import com.ardrawing.ardrawingspace.databinding.FragmentHomeBinding;
import com.ardrawing.ardrawingspace.room.SpaceEntity;
import com.ardrawing.ardrawingspace.util.ItemDecoration;
import com.ardrawing.ardrawingspace.viewmodel.SpaceViewModel;
import java.util.ArrayList;
import java.util.List;
public class HomeFragment extends Fragment {
private FragmentHomeBinding binding;
private SpaceViewModel viewModel;
private SpaceRecyclerViewAdapter adapter;
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
binding = FragmentHomeBinding.inflate(inflater, container, false);
initData();
initEvent();
return binding.getRoot();
}
private void initData() {
viewModel = new ViewModelProvider(this).get(SpaceViewModel.class);
binding.recyclerView.setLayoutManager(new GridLayoutManager(getContext(), 2));
adapter = new SpaceRecyclerViewAdapter(requireContext(), new ArrayList<>(), 0);
binding.recyclerView.setAdapter(adapter);
binding.recyclerView.addItemDecoration(new ItemDecoration(35, 15, 20));
}
private void initEvent() {
loadLike();
}
private void loadLike() {
viewModel
.getMinById()
.observe(getViewLifecycleOwner(), new Observer<List<SpaceEntity>>() {
@Override
public void onChanged(List<SpaceEntity> spaceEntityList) {
if (spaceEntityList.isEmpty()) {
binding.text.setVisibility(View.VISIBLE);
} else {
binding.text.setVisibility(View.GONE);
}
adapter.updateItems(spaceEntityList);
}
});
}
@Override
public void onDestroyView() {
super.onDestroyView();
binding = null;
}
}

View File

@ -0,0 +1,118 @@
package com.ardrawing.ardrawingspace.fragment;
import android.app.Activity;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
import androidx.fragment.app.Fragment;
import androidx.lifecycle.Observer;
import androidx.lifecycle.ViewModelProvider;
import androidx.recyclerview.widget.GridLayoutManager;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Toast;
import com.ardrawing.ardrawingspace.adapter.SpaceRecyclerViewAdapter;
import com.ardrawing.ardrawingspace.databinding.FragmentImportBinding;
import com.ardrawing.ardrawingspace.room.SpaceEntity;
import com.ardrawing.ardrawingspace.util.DeleteCallback;
import com.ardrawing.ardrawingspace.util.ItemDecoration;
import com.ardrawing.ardrawingspace.util.UploadUtil;
import com.ardrawing.ardrawingspace.viewmodel.SpaceViewModel;
import java.util.ArrayList;
import java.util.List;
public class ImportFragment extends Fragment implements DeleteCallback {
private static final int PICK_IMAGE_REQUEST_CODE = 202;
private FragmentImportBinding binding;
private SpaceRecyclerViewAdapter adapter;
private SpaceViewModel viewModel;
private final List<String> imagePaths = new ArrayList<>();
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
binding = FragmentImportBinding.inflate(inflater, container, false);
initData();
initEvent();
return binding.getRoot();
}
private void initData() {
viewModel = new ViewModelProvider(this).get(SpaceViewModel.class);
binding.recyclerView.setLayoutManager(new GridLayoutManager(getContext(), 2));
adapter = new SpaceRecyclerViewAdapter(requireContext(), new ArrayList<>(), 1);
binding.recyclerView.setAdapter(adapter);
adapter.setDeleteCallback(this);
binding.recyclerView.addItemDecoration(new ItemDecoration(35, 15, 20));
}
private void initEvent() {
binding.upload.setOnClickListener(v -> {
openImagePicker();
});
loadAllImportImage();
}
private void loadAllImportImage() {
viewModel
.getImportLiveData()
.observe(getViewLifecycleOwner(), new Observer<List<SpaceEntity>>() {
@Override
public void onChanged(List<SpaceEntity> spaceEntityList) {
if (spaceEntityList.isEmpty()) {
binding.text.setVisibility(View.VISIBLE);
} else {
binding.text.setVisibility(View.GONE);
}
adapter.updateItems(spaceEntityList);
}
});
}
private void openImagePicker() {
Intent intent = new Intent(Intent.ACTION_PICK, android.provider.MediaStore.Images.Media.EXTERNAL_CONTENT_URI);
startActivityForResult(intent, PICK_IMAGE_REQUEST_CODE);
}
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (requestCode == PICK_IMAGE_REQUEST_CODE && resultCode == Activity.RESULT_OK && data != null) {
Uri selectedImageUri = data.getData();
if (selectedImageUri != null) {
processImage(selectedImageUri);
}
}
}
private void processImage(Uri imageUri) {
if (UploadUtil.isImageSizeValid(imageUri, requireContext())) {
UploadUtil.saveImageByPath(imageUri, imagePaths, viewModel, requireContext());
} else {
Toast.makeText(getContext(), "The image size is out of limit", Toast.LENGTH_SHORT).show();
}
}
@Override
public void onDestroy() {
super.onDestroy();
binding = null;
}
@Override
public void onDelete(SpaceEntity entity) {
UploadUtil.deleteImageByPath(entity, viewModel, requireContext());
loadAllImportImage();
}
}

View File

@ -0,0 +1,58 @@
package com.ardrawing.ardrawingspace.util;
import android.os.Handler;
import android.os.Looper;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
public class AppExecutors {
private static AppExecutors instance;
// 磁盘 I/O 线程池单线程用于数据库操作
private final Executor diskIO;
// 主线程执行器用于 UI 更新
private final Executor mainThread;
// 网络线程池可根据需要调整线程数
private final Executor networkIO;
private AppExecutors() {
this.diskIO = Executors.newSingleThreadExecutor();
this.mainThread = new MainThreadExecutor();
this.networkIO = Executors.newFixedThreadPool(3); // 3 个线程处理网络任务
}
public static AppExecutors getInstance() {
if (instance == null) {
synchronized (AppExecutors.class) {
if (instance == null) {
instance = new AppExecutors();
}
}
}
return instance;
}
public Executor diskIO() {
return diskIO;
}
public Executor mainThread() {
return mainThread;
}
public Executor networkIO() {
return networkIO;
}
private static class MainThreadExecutor implements Executor {
private final Handler mainThreadHandler = new Handler(Looper.getMainLooper());
@Override
public void execute(Runnable command) {
mainThreadHandler.post(command);
}
}
}

View File

@ -0,0 +1,7 @@
package com.ardrawing.ardrawingspace.util;
import com.ardrawing.ardrawingspace.room.SpaceEntity;
public interface DeleteCallback {
void onDelete(SpaceEntity entity);
}

View File

@ -0,0 +1,78 @@
package com.ardrawing.ardrawingspace.util;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.util.Log;
import android.view.MotionEvent;
import android.widget.ImageView;
import android.widget.Toast;
import java.io.IOException;
import java.io.InputStream;
public class DocumentManipulationTool {
private static final boolean IS_DEBUG = false;
public static float computeTouchDistance(MotionEvent event) {
if (event == null || event.getPointerCount() < 2) return 0f;
float deltaX = event.getX(1) - event.getX(0);
float deltaY = event.getY(1) - event.getY(0);
return (float) Math.sqrt(deltaX * deltaX + deltaY * deltaY);
}
public static void loadImageByPath(String path, ImageView imageView, Context context) {
if (isInvalidInput(path, imageView, context)) return;
if (path.startsWith("/data/user/")) {
loadCheck(path, imageView, context);
} else {
loadTry(path, imageView, context);
}
}
private static boolean isInvalidInput(String path, ImageView imageView, Context context) {
return path == null || imageView == null || context == null;
}
private static void loadCheck(String path, ImageView imageView, Context context) {
Bitmap bitmap = decodeBitmapFromFile(path);
if (bitmap != null) {
imageView.setImageBitmap(bitmap);
} else {
displayError(context, "Failed to load image from storage");
}
}
private static void loadTry(String path, ImageView imageView, Context context) {
try (InputStream inputStream = context.getAssets().open(path)) {
Bitmap bitmap = BitmapFactory.decodeStream(inputStream);
if (bitmap != null) {
imageView.setImageBitmap(bitmap);
} else {
displayError(context, "Failed to decode asset image");
}
} catch (IOException e) {
displayError(context, "Failed to load image from assets");
}
}
private static Bitmap decodeBitmapFromFile(String path) {
if (path.isEmpty()) return null;
return BitmapFactory.decodeFile(path);
}
private static void displayError(Context context, String message) {
if (context != null) {
showToast(context, message);
}
}
private static void showToast(Context context, String message) {
if (IS_DEBUG) {
Log.d("FileUtil", message);
}
Toast.makeText(context, message, Toast.LENGTH_SHORT).show();
}
}

View File

@ -0,0 +1,71 @@
package com.ardrawing.ardrawingspace.util;
import android.app.Application;
import android.content.res.AssetManager;
import android.util.Log;
import com.ardrawing.ardrawingspace.room.SpaceEntity;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
public class FileParsingAndInitialization {
private final Application application;
private static final String IMAGE_PREFIX = "png_";
private static final String IMAGE_EXTENSION = ".jpg";
public FileParsingAndInitialization(Application application) {
this.application = application;
}
public void insertImagesToDatabase() {
AssetManager assetManager = application.getAssets();
try {
String[] directories = assetManager.list("");
if (directories == null) {
Log.w("InitDatabase", "No directories found in assets");
return;
}
for (String dir : directories) {
if (!dir.startsWith(IMAGE_PREFIX)) continue;
importImagesFromDirectory(dir);
}
} catch (IOException e) {
Log.e("InitDatabase", "Failed to list asset directories", e);
}
}
private void importImagesFromDirectory(String directory) {
AssetManager assetManager = application.getAssets();
try {
String[] files = assetManager.list(directory);
if (files == null) {
Log.w("InitDatabase", "No files found in directory: " + directory);
return;
}
List<SpaceEntity> imageList = new ArrayList<>();
for (String file : files) {
if (file.endsWith(IMAGE_EXTENSION)) {
String imagePath = directory + "/" + file;
imageList.add(new SpaceEntity(imagePath, false, false));
}
}
if (!imageList.isEmpty()) {
insertImagesAsync(imageList);
}
} catch (IOException e) {
Log.e("InitDatabase", "Failed to list files in directory: " + directory, e);
}
}
private void insertImagesAsync(List<SpaceEntity> imageList) {
new Thread(() -> {
SpaceRepository repository = new SpaceRepository(application);
repository.insertAll(imageList);
}).start();
}
}

View File

@ -0,0 +1,63 @@
package com.ardrawing.ardrawingspace.util;
import android.Manifest;
import android.app.Activity;
import android.content.pm.PackageManager;
import android.os.Build;
import android.util.Log;
import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat;
public final class PermissionTool {
private PermissionTool() {
// 防止实例化
}
private static final boolean IS_DEBUG = false;
public static boolean hasPermission(Activity activity, String[] permissions) {
if (isInvalidInput(activity, permissions)) return false;
for (String permission : permissions) {
if (ContextCompat.checkSelfPermission(activity, permission) != PackageManager.PERMISSION_GRANTED) {
return false;
}
}
return true;
}
public static void reqPermission(Activity activity, String[] permissions, int requestCode) {
if (isValidInput(activity, permissions)) {
ActivityCompat.requestPermissions(activity, permissions, requestCode);
}
}
public static boolean gotPermission(int[] grantResults) {
if (grantResults == null || grantResults.length == 0) return false;
for (int result : grantResults) {
if (result != PackageManager.PERMISSION_GRANTED) {
return false;
}
}
return true;
}
public static String[] getPermission() {
return Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU
? new String[]{Manifest.permission.READ_MEDIA_IMAGES}
: new String[]{Manifest.permission.READ_EXTERNAL_STORAGE, Manifest.permission.WRITE_EXTERNAL_STORAGE};
}
private static boolean isInvalidInput(Activity activity, String[] permissions) {
return activity == null || permissions == null;
}
private static boolean isValidInput(Activity activity, String[] permissions) {
if (IS_DEBUG) {
Log.d("PermissionUtil", "Validating input");
}
return activity != null && permissions != null;
}
}

View File

@ -0,0 +1,21 @@
package com.ardrawing.ardrawingspace.util;
import android.app.Application;
import com.ardrawing.ardrawingspace.room.SpaceEntity;
import com.ardrawing.ardrawingspace.viewmodel.SpaceViewModel;
import java.util.List;
public class SpaceRepository {
private final Application application;
public SpaceRepository(Application application) {
this.application = application;
}
public void insertAll(List<SpaceEntity> entities) {
SpaceViewModel viewModel = new SpaceViewModel(application);
viewModel.insertAll(entities);
}
}

View File

@ -0,0 +1,156 @@
package com.ardrawing.ardrawingspace.util;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.net.Uri;
import android.os.Handler;
import android.os.Looper;
import android.util.Log;
import android.widget.Toast;
import com.ardrawing.ardrawingspace.room.SpaceEntity;
import com.ardrawing.ardrawingspace.viewmodel.SpaceViewModel;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.List;
public class UploadUtil {
private static final Handler mainHandler = new Handler(Looper.getMainLooper());
private static final boolean IS_DEBUG = false;
public static void saveImageByPath(Uri imageUri, List<String> imagePaths, SpaceViewModel viewModel, Context context) {
if (isInvalidInput(imageUri, imagePaths, viewModel, context)) return;
try (InputStream inputStream = context.getContentResolver().openInputStream(imageUri)) {
Bitmap bitmap = decodeBitmapFromStream(inputStream);
if (bitmap == null) {
displayToast(context, "Unable to load image");
return;
}
File imageFile = createImageFile(context, generateUniqueImageName());
saveBitmapToFile(bitmap, imageFile);
String savedImagePath = imageFile.getAbsolutePath();
executeImageSavingAsync(savedImagePath, imagePaths, viewModel, context, imageFile);
} catch (IOException e) {
displayToast(context, "Failed to save picture");
}
}
public static void deleteImageByPath(SpaceEntity spaceEntity, SpaceViewModel viewModel, Context context) {
if (isInvalidEntity(spaceEntity, viewModel, context)) return;
File imageFile = new File(spaceEntity.getImagePath());
if (!imageFile.exists()) {
displayToast(context, "Image does not exist");
return;
}
if (imageFile.delete()) {
viewModel.delete(spaceEntity);
displayToast(context, "Image deleted successfully");
} else {
displayToast(context, "Failed to delete image");
}
}
private static void executeImageSavingAsync(String savedImagePath, List<String> imagePaths, SpaceViewModel viewModel, Context context, File imageFile) {
new Thread(() -> {
if (checkImageDuplicate(savedImagePath, imagePaths, viewModel)) {
imageFile.delete();
displayToast(context, "The image already exists");
return;
}
imagePaths.add(savedImagePath);
viewModel.insert(new SpaceEntity(savedImagePath, true, false));
}).start();
}
private static boolean checkImageDuplicate(String imagePath, List<String> imagePaths, SpaceViewModel viewModel) {
File newImageFile = new File(imagePath);
if (!newImageFile.exists()) return false;
for (String path : imagePaths) {
File existingFile = new File(path);
if (compareFilesBySize(existingFile, newImageFile)) {
return true;
}
}
List<SpaceEntity> spaceEntityList = viewModel.getImport();
for (SpaceEntity data : spaceEntityList) {
File existingFile = new File(data.getImagePath());
if (compareFilesBySize(existingFile, newImageFile)) {
return true;
}
}
return false;
}
private static boolean compareFilesBySize(File file1, File file2) {
return file1.exists() && file2.exists() && file1.length() == file2.length();
}
private static Bitmap decodeBitmapFromStream(InputStream inputStream) {
return BitmapFactory.decodeStream(inputStream);
}
private static File createImageFile(Context context, String fileName) {
return new File(context.getFilesDir(), fileName + ".jpg");
}
private static void saveBitmapToFile(Bitmap bitmap, File file) throws IOException {
try (FileOutputStream outputStream = new FileOutputStream(file)) {
bitmap.compress(Bitmap.CompressFormat.JPEG, 100, outputStream);
}
}
private static void displayToast(Context context, String message) {
if (isInvalidToastInput(context, message)) return;
mainHandler.post(() -> Toast.makeText(context, message, Toast.LENGTH_SHORT).show());
}
private static boolean isInvalidToastInput(Context context, String message) {
if (IS_DEBUG) {
Log.d("UploadUtil", "Checking toast input");
}
return context == null || message == null;
}
private static boolean isInvalidInput(Uri imageUri, List<String> imagePaths, SpaceViewModel viewModel, Context context) {
return imageUri == null || imagePaths == null || viewModel == null || context == null;
}
private static boolean isInvalidEntity(SpaceEntity spaceEntity, SpaceViewModel viewModel, Context context) {
return spaceEntity == null || spaceEntity.getImagePath() == null || viewModel == null || context == null;
}
private static String generateUniqueImageName() {
return String.valueOf(System.currentTimeMillis());
}
public static boolean isImageSizeValid(Uri imageUri, Context context) {
if (isInvalidSizeCheck(imageUri, context)) return false;
try (InputStream inputStream = context.getContentResolver().openInputStream(imageUri)) {
if (inputStream == null) return false;
long imageSize = inputStream.available();
long maxSize = 10 * 1024 * 1024; // 10MB
return imageSize <= maxSize;
} catch (IOException e) {
return false;
}
}
private static boolean isInvalidSizeCheck(Uri imageUri, Context context) {
return imageUri == null || context == null;
}
}

View File

@ -28,6 +28,10 @@ public class SpaceViewModel extends AndroidViewModel {
executorService.execute(() -> dao.insert(spaceEntity));
}
public void insertAll(List<SpaceEntity> spaceEntityList) {
executorService.execute(() -> dao.insertAll(spaceEntityList));
}
public void delete(SpaceEntity spaceEntity) {
executorService.execute(() -> dao.delete(spaceEntity));
}

View File

@ -0,0 +1,12 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="32dp"
android:height="32dp"
android:viewportWidth="1024"
android:viewportHeight="1024">
<path
android:fillColor="#FF000000"
android:pathData="M515.2,102.4c5.9,0 11.9,0.6 17.9,2 42.2,9.5 68.3,50.3 58.5,91.1L434,502.8l157.7,325.7c9.8,40.8 -16.3,81.6 -58.5,91.1a80.9,80.9 0,0 1,-17.9 2c-35.6,0 -67.8,-23.6 -76.3,-58.6L272.4,503.8h-0.5l0.3,-1 -0.3,-1h0.5l166.6,-340.8C447.4,126 479.6,102.4 515.2,102.4m0,-58.5c-61.2,0 -114.1,39.3 -131.2,96.3L222.1,471.4a59,59 0,0 0,-8.7 29.6,58.5 58.5,0 0,0 8.9,33.8l161.4,348.2C400.7,940.5 453.8,980.1 515.3,980.1c10.3,0 20.7,-1.2 30.8,-3.5 36.3,-8.2 66.8,-29.9 86.2,-61.1a131.4,131.4 0,0 0,16.3 -100.9,59.5 59.5,0 0,0 -4.2,-11.8l-145,-299.4 144.3,-281.3a58.8,58.8 0,0 0,4.8 -13,131.5 131.5,0 0,0 -16.3,-100.9c-19.3,-31.1 -49.9,-52.8 -86.1,-61A138.8,138.8 0,0 0,515.2 43.9z"/>
<path
android:fillColor="#FF000000"
android:pathData="M357.1,458.8a14.6,14.6 0,0 1,-13.1 -21.2l73.1,-146.3a14.6,14.6 0,0 1,26.2 13.1l-73.1,146.3c-2.6,5.1 -7.7,8.1 -13.1,8.1zM459.5,254a14.6,14.6 0,0 1,-13.1 -21.2l14.6,-29.3a14.6,14.6 0,0 1,26.2 13.1l-14.6,29.3c-2.6,5.1 -7.7,8.1 -13.1,8.1zM693.6,458.2a58.5,58.5 0,1 1,0 117,58.5 58.5,0 0,1 0,-117m0,-58.5c-64.5,0 -117,52.5 -117,117s52.5,117 117,117 117,-52.5 117,-117 -52.5,-117 -117,-117z"/>
</vector>

View File

@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="32dp"
android:height="32dp"
android:viewportWidth="1024"
android:viewportHeight="1024">
<path
android:pathData="M512,320c-141.4,0 -256,114.6 -256,256s114.6,256 256,256c141.4,0 256,-114.6 256,-256s-114.6,-256 -256,-256zM657.8,701c-69,80.5 -190.2,89.8 -270.7,20.8 -80.5,-69 -89.8,-190.2 -20.8,-270.7 69,-80.5 190.2,-89.8 270.7,-20.8 80.5,69 89.8,190.2 20.8,270.7zM512,448c8.8,0 16,7.2 16,16s-7.2,16 -16,16c-53,0 -96,43 -96,96l0,0.1c0,8.8 -7.2,16 -16,16s-16,-7.2 -16,-16l0,-0.1c0,-70.7 57.3,-128 128,-128zM943.7,289.3l-138.7,-23.1 -43.9,-109.9c-14.7,-36.6 -49.7,-60.3 -89.1,-60.3l-320,0c-39.4,0 -74.4,23.7 -89.2,60.4l-43.9,109.9 -138.7,23.1c-46.5,7.7 -80.3,47.5 -80.3,94.7l0,480c0,52.9 43.1,96 96,96l832,0c52.9,0 96,-43.1 96,-96l0,-480c0,-47.1 -33.8,-86.9 -80.3,-94.7zM960,864c0,17.7 -14.3,32 -32,32l-832,0c-17.7,0 -32,-14.3 -32,-32l0,-480c0,-15.6 11.3,-29 26.8,-31.6l174.2,-29 57.3,-143.3c4.9,-12.1 16.6,-20.1 29.7,-20.1l320,0c13.1,0 24.8,8 29.7,20.1l57.3,143.3 174.2,29c15.4,2.6 26.8,15.9 26.8,31.6l0,480z"
android:fillColor="@color/black"/>
</vector>

View File

@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="32dp"
android:height="32dp"
android:viewportWidth="1024"
android:viewportHeight="1024">
<path
android:fillColor="#FF000000"
android:pathData="M146.3,768h731.4L877.7,73.1L219.4,73.1a74,74 0,0 0,-73.1 73.1v621.7zM219.4,0h694.9c10.7,0 19.5,3.4 26.3,10.2 6.8,6.9 10.2,15.7 10.2,26.3L950.8,804.6a35.6,35.6 0,0 1,-10.2 26.3,35.6 35.6,0 0,1 -26.3,10.2L109.7,841.1l-36.6,66.3L73.1,146.3C73.9,105.1 88.2,70.7 116,42.9 143.8,15.1 178.3,0.8 219.4,0zM201.1,841.1a56.1,56.1 0,0 0,-38.3 16.5,51.9 51.9,0 0,0 -15.5,38.3c0,15.2 5.1,28 15.5,38.3 10.2,10.2 23,15.8 38.3,16.5L877.7,950.9v-109.7L201.1,841.1zM201.1,768L950.8,768L950.8,950.9a74.1,74.1 0,0 1,-73.1 73.1L201.1,1024c-36.6,-0.7 -66.9,-13.2 -90.8,-37.2C86.3,962.9 73.9,932.6 73.1,896c0.7,-36.6 13.2,-66.9 37.2,-90.8 24,-24 54.3,-36.4 90.8,-37.2zM365.7,73.1v286.9l109.7,-88L585.1,360L585.1,73.1L365.7,73.1zM292.6,0h365.7v435.4a35.7,35.7 0,0 1,-21.1 32.6,34.5 34.5,0 0,1 -38.3,-4L475.4,365.7 352,464a34.5,34.5 0,0 1,-38.3 3.9A35.7,35.7 0,0 1,292.6 435.5L292.6,0z"/>
</vector>

View File

@ -0,0 +1,15 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="32dp"
android:height="32dp"
android:viewportWidth="1024"
android:viewportHeight="1024">
<path
android:fillColor="#FF0000"
android:pathData="M896,186.2 L128,186.2c-12.8,0 -23.3,10.4 -23.3,23.3l0,162.9c0,12.8 10.4,23.3 23.3,23.3s23.3,-10.4 23.3,-23.3L151.3,232.7l721.5,0 0,151.3c0,12.8 10.4,23.3 23.3,23.3s23.3,-10.4 23.3,-23.3L919.3,209.5C919.3,196.6 908.8,186.2 896,186.2z"/>
<path
android:fillColor="#FF0000"
android:pathData="M826.2,325.8c-12.8,0 -23.3,10.4 -23.3,23.3l0,570.2L651.6,919.3 651.6,570.2c0,-12.8 -10.4,-23.3 -23.3,-23.3s-23.3,10.4 -23.3,23.3l0,349.1L418.9,919.3 418.9,453.8l209.5,0c12.8,0 23.3,-10.4 23.3,-23.3s-10.4,-23.3 -23.3,-23.3L395.6,407.3c-12.8,0 -23.3,10.4 -23.3,23.3l0,488.7L221.1,919.3 221.1,349.1c0,-12.8 -10.4,-23.3 -23.3,-23.3s-23.3,10.4 -23.3,23.3l0,593.5c0,12.8 10.4,23.3 23.3,23.3l628.4,0c12.8,0 23.3,-10.4 23.3,-23.3L849.5,349.1C849.5,336.2 839,325.8 826.2,325.8z"/>
<path
android:fillColor="#FF0000"
android:pathData="M244.4,139.6l523.6,0c12.8,0 23.3,-10.4 23.3,-23.3s-10.4,-23.3 -23.3,-23.3L244.4,93.1c-12.8,0 -23.3,10.4 -23.3,23.3S231.5,139.6 244.4,139.6z"/>
</vector>

View File

@ -0,0 +1,12 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="32dp"
android:height="32dp"
android:viewportWidth="1024"
android:viewportHeight="1024">
<path
android:fillColor="@color/white"
android:pathData="M702.9,92.7l-109.1,384h161.9L395.7,916.2l70.6,-381H268.3L702.9,92.7m0,-54.9c-0.7,0 -1.4,3 -2.2,3 -12.3,0.4 -23.6,6.1 -32.8,13 -2.3,1.7 -4.5,4.3 -6.5,6.4l-434.7,437.7c-16.7,16.8 -21.6,39.9 -12.5,61.7 9.1,21.8 30.4,34.1 54,34.1h127.9l-58,319.3c-4.8,26.2 8.7,53.4 32.8,64.6 7.9,3.7 16.4,6 24.7,6a58.1,58.1 0,0 0,45.1 -20.9l360,-434.2c14.5,-17.5 17.5,-49 7.9,-69.5 -9.6,-20.5 -30.3,-40.9 -53,-40.9h-84.7l87.6,-305.3c1.8,-5.7 2.8,-11 2.8,-17.3 0,-31.9 -25.5,-57.8 -57.3,-57.8l-1.3,-0z"/>
<path
android:fillColor="@color/white"
android:pathData="M395.7,501.5a14.6,14.6 0,0 1,-10.7 -24.6l190.2,-204.8a14.6,14.6 0,1 1,21.4 19.9l-190.2,204.8a14.5,14.5 0,0 1,-10.7 4.7zM587.5,618.5a14.6,14.6 0,0 1,-11.5 -23.6l56.9,-73.1a14.6,14.6 0,1 1,23.1 18l-56.9,73.1a14.6,14.6 0,0 1,-11.6 5.6zM542,677a14.4,14.4 0,0 1,-9 -3.1,14.6 14.6,0 0,1 -2.6,-20.5l11.4,-14.6a14.6,14.6 0,0 1,20.5 -2.6,14.6 14.6,0 0,1 2.6,20.5l-11.4,14.6a14.6,14.6 0,0 1,-11.6 5.7z"/>
</vector>

View File

@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="32dp"
android:height="32dp"
android:viewportWidth="1024"
android:viewportHeight="1024">
<path
android:pathData="M230.4,992c-64,0 -115.2,-51.2 -115.2,-115.2L115.2,544 76.8,582.4C70.4,588.8 57.6,595.2 44.8,595.2c-12.8,0 -25.6,-6.4 -32,-12.8C6.4,569.6 0,556.8 0,544 0,537.6 6.4,524.8 12.8,512l467.2,-467.2C486.4,38.4 499.2,32 512,32s25.6,6.4 32,12.8L1011.2,512C1017.6,524.8 1024,537.6 1024,544c0,12.8 -6.4,25.6 -12.8,32 -6.4,6.4 -19.2,12.8 -32,12.8s-25.6,-6.4 -32,-12.8L512,147.2 211.2,448l0,422.4c0,12.8 12.8,25.6 25.6,25.6l115.2,0 0,-185.6c0,-25.6 19.2,-44.8 44.8,-44.8l230.4,0c25.6,0 44.8,19.2 44.8,44.8s-19.2,44.8 -44.8,44.8L441.6,755.2l0,185.6c0,25.6 -19.2,44.8 -44.8,44.8L230.4,985.6zM627.2,992c-25.6,0 -44.8,-19.2 -44.8,-44.8 0,-25.6 19.2,-44.8 44.8,-44.8l160,0c12.8,0 25.6,-12.8 25.6,-25.6l0,-256c0,-25.6 19.2,-44.8 44.8,-44.8 25.6,0 44.8,19.2 44.8,44.8l0,256c0,64 -51.2,115.2 -115.2,115.2L627.2,992z"
android:fillColor="@color/black"/>
</vector>

View File

@ -0,0 +1,4 @@
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<corners android:radius="8dp"/>
<gradient android:startColor="#F5F5F5" android:endColor="#FFFFFF" android:angle="270"/>
</shape>

View File

@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="32dp"
android:height="32dp"
android:viewportWidth="1024"
android:viewportHeight="1024">
<path
android:fillColor="#FF0000"
android:pathData="M780.2,97.5a73.1,73.1 0,0 1,73.1 73.1v755.8l-341.3,-152.4L170.7,926.5L170.7,170.7a73.1,73.1 0,0 1,73.1 -73.1h536.4zM780.2,170.7L243.8,170.7v643l268.2,-119.7 268.2,119.7L780.2,170.7z"/>
</vector>

View File

@ -0,0 +1,15 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="32dp"
android:height="32dp"
android:viewportWidth="1024"
android:viewportHeight="1024">
<path
android:pathData="M96,192v640c0,17.7 14.3,32 32,32h448.3c17.4,0 32.2,-13.7 32.7,-31.1 0.5,-18.1 -14,-32.9 -32,-32.9L203.3,800l194.8,-194.8 31.1,31.1 45.3,45.3 36.7,36.7c12.5,12.5 32.8,12.5 45.3,0s12.5,-32.8 0,-45.3l-36.7,-36.7 247.9,-247.9 97,97c2.6,12.5 12.4,22.3 24.9,24.9 2.1,0.4 4.3,0.7 6.5,0.7 17.7,0 31.9,-14.9 31.9,-32.6L928,192c0,-17.7 -14.3,-32 -32,-32L128,160c-17.7,0 -32,14.3 -32,32zM864,394.3l-96.4,-96.4 -293.2,293.2 -76.4,-76.4 -238,238.1L160,224h704v170.3z"
android:fillColor="@color/black"/>
<path
android:pathData="M914.7,799.5c0,-17.7 -14.6,-32.2 -32.2,-32.2l-115,-0.2 149.6,-149.6c12.4,-12.4 12.6,-32.7 0.2,-45.1 -12.5,-12.5 -33.1,-12.4 -45.6,0.1L722.2,721.8l0.4,-114.7c0,-17.7 -14.4,-31.7 -31.7,-32 -18,0.4 -32.2,14.5 -32.2,32.2l-0.1,192.1c0,17.7 14.3,32 32,32h192c17.7,0.1 32.1,-14.2 32.1,-31.9z"
android:fillColor="@color/black"/>
<path
android:pathData="M320.1,383.8m-64,0a64,64 0,1 0,128 0,64 64,0 1,0 -128,0Z"
android:fillColor="@color/black"/>
</vector>

View File

@ -0,0 +1,20 @@
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item android:id="@android:id/background">
<shape>
<corners android:radius="5dp" />
<solid android:color="#D3D3D3" />
</shape>
</item>
<item android:id="@android:id/progress">
<clip>
<shape>
<corners android:radius="5dp" />
<gradient
android:startColor="#4891FF"
android:endColor="#6CE89E"
android:angle="0" />
</shape>
</clip>
</item>
</layer-list>

View File

@ -0,0 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="oval">
<solid android:color="#6CE89E" />
<stroke android:color="@color/white" android:width="4dp"/>
<size
android:width="16dp"
android:height="16dp" />
</shape>

View File

@ -0,0 +1,18 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="32dp"
android:height="32dp"
android:viewportWidth="1024"
android:viewportHeight="1024">
<path
android:fillColor="#FF000000"
android:pathData="M560.9,379.4c-13.1,-16.7 -31.5,-26.4 -50.4,-26.4 -18.2,0 -35.1,8.5 -47.5,24.4L355.3,521.4c-13.9,17.7 -17.5,37.3 -9.6,53.5 7.4,15.3 23.5,23 44.1,23h67.9a14.2,14.2 0,1 0,0 -28.4h-67.9c-9.3,0 -16.1,-1.9 -18.6,-7 -2.9,-6.1 -0.5,-14.2 6.6,-23.3l107.7,-144.3c14.3,-18.2 38.3,-16.9 52.9,1.7l108,141.3c7,8.9 9.4,17.9 6.3,24.3 -3.5,7.2 -13.1,7.2 -18.5,7.2h-48.5c-7.8,0 -9.1,8.5 -9.1,16.4v51.1c0,30.4 -27.2,57 -55.4,57 -23.7,0 -44.5,-11.1 -51.8,-29.7a13.8,13.8 0,0 0,-18.1 -8c-7.3,2.9 -10.3,11.1 -7.4,18.4 11.5,29.5 41.9,47.8 77.2,47.8 44.1,0 84,-39.1 84,-85.4v-39.1h29.1c20.5,0 36.5,-7.8 44.1,-23.3 8,-16.5 4.5,-35.9 -9.4,-53.6l-107.9,-141.6z"/>
<path
android:fillColor="#FF000000"
android:pathData="M460.9,628.8a13.8,13.8 0,0 0,0 -20.1c-5.3,-5.2 -14.8,-5.2 -20,0a13.8,13.8 0,0 0,-4.1 10.1c0,3.7 1.4,7.4 4.1,10.1 2.7,2.6 6.2,4.1 10.1,4.1 3.7,0 7.2,-1.6 9.9,-4.3z"/>
<path
android:fillColor="#FF000000"
android:pathData="M512,50C257.3,50 50,257.3 50,512 50,766.7 257.3,974 512,974S974,766.7 974,512C974,257.3 766.7,50 512,50zM512,917.1C288.6,917.1 106.9,735.4 106.9,512S288.6,106.9 512,106.9 917.1,288.6 917.1,512 735.4,917.1 512,917.1z"/>
<path
android:fillColor="#FF000000"
android:pathData="M606.5,167a14.3,14.3 0,0 0,-17.5 10c-2.1,7.6 2.4,15.4 10,17.5C741.6,233.4 841.2,364 841.2,512a14.2,14.2 0,0 0,28.4 0c0,-160.8 -108.2,-302.7 -263.1,-345zM512,182.8a342.5,342.5 0,0 1,19.2 0.5,14.2 14.2,0 0,0 0.8,-28.4 370.6,370.6 0,0 0,-19.9 -0.6,14.2 14.2,0 0,0 -0,28.4z"/>
</vector>

View File

@ -0,0 +1,9 @@
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<gradient
android:angle="45"
android:startColor="#87CEEB"
android:endColor="#90EE90"
android:type="linear"
android:useLevel="false" />
<corners android:radius="16dp" />
</shape>

View File

@ -0,0 +1,4 @@
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<corners android:radius="4dp"/>
<solid android:color="#E0E0E0"/>
</shape>

View File

@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="32dp"
android:height="32dp"
android:viewportWidth="1024"
android:viewportHeight="1024">
<path
android:fillColor="@color/gray"
android:pathData="M146.3,768h731.4L877.7,73.1L219.4,73.1a74,74 0,0 0,-73.1 73.1v621.7zM219.4,0h694.9c10.7,0 19.5,3.4 26.3,10.2 6.8,6.9 10.2,15.7 10.2,26.3L950.8,804.6a35.6,35.6 0,0 1,-10.2 26.3,35.6 35.6,0 0,1 -26.3,10.2L109.7,841.1l-36.6,66.3L73.1,146.3C73.9,105.1 88.2,70.7 116,42.9 143.8,15.1 178.3,0.8 219.4,0zM201.1,841.1a56.1,56.1 0,0 0,-38.3 16.5,51.9 51.9,0 0,0 -15.5,38.3c0,15.2 5.1,28 15.5,38.3 10.2,10.2 23,15.8 38.3,16.5L877.7,950.9v-109.7L201.1,841.1zM201.1,768L950.8,768L950.8,950.9a74.1,74.1 0,0 1,-73.1 73.1L201.1,1024c-36.6,-0.7 -66.9,-13.2 -90.8,-37.2C86.3,962.9 73.9,932.6 73.1,896c0.7,-36.6 13.2,-66.9 37.2,-90.8 24,-24 54.3,-36.4 90.8,-37.2zM365.7,73.1v286.9l109.7,-88L585.1,360L585.1,73.1L365.7,73.1zM292.6,0h365.7v435.4a35.7,35.7 0,0 1,-21.1 32.6,34.5 34.5,0 0,1 -38.3,-4L475.4,365.7 352,464a34.5,34.5 0,0 1,-38.3 3.9A35.7,35.7 0,0 1,292.6 435.5L292.6,0z"/>
</vector>

View File

@ -0,0 +1,12 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="32dp"
android:height="32dp"
android:viewportWidth="1024"
android:viewportHeight="1024">
<path
android:fillColor="@color/black"
android:pathData="M702.9,92.7l-109.1,384h161.9L395.7,916.2l70.6,-381H268.3L702.9,92.7m0,-54.9c-0.7,0 -1.4,3 -2.2,3 -12.3,0.4 -23.6,6.1 -32.8,13 -2.3,1.7 -4.5,4.3 -6.5,6.4l-434.7,437.7c-16.7,16.8 -21.6,39.9 -12.5,61.7 9.1,21.8 30.4,34.1 54,34.1h127.9l-58,319.3c-4.8,26.2 8.7,53.4 32.8,64.6 7.9,3.7 16.4,6 24.7,6a58.1,58.1 0,0 0,45.1 -20.9l360,-434.2c14.5,-17.5 17.5,-49 7.9,-69.5 -9.6,-20.5 -30.3,-40.9 -53,-40.9h-84.7l87.6,-305.3c1.8,-5.7 2.8,-11 2.8,-17.3 0,-31.9 -25.5,-57.8 -57.3,-57.8l-1.3,-0z"/>
<path
android:fillColor="@color/black"
android:pathData="M395.7,501.5a14.6,14.6 0,0 1,-10.7 -24.6l190.2,-204.8a14.6,14.6 0,1 1,21.4 19.9l-190.2,204.8a14.5,14.5 0,0 1,-10.7 4.7zM587.5,618.5a14.6,14.6 0,0 1,-11.5 -23.6l56.9,-73.1a14.6,14.6 0,1 1,23.1 18l-56.9,73.1a14.6,14.6 0,0 1,-11.6 5.6zM542,677a14.4,14.4 0,0 1,-9 -3.1,14.6 14.6,0 0,1 -2.6,-20.5l11.4,-14.6a14.6,14.6 0,0 1,20.5 -2.6,14.6 14.6,0 0,1 2.6,20.5l-11.4,14.6a14.6,14.6 0,0 1,-11.6 5.7z"/>
</vector>

View File

@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="32dp"
android:height="32dp"
android:viewportWidth="1024"
android:viewportHeight="1024">
<path
android:pathData="M230.4,992c-64,0 -115.2,-51.2 -115.2,-115.2L115.2,544 76.8,582.4C70.4,588.8 57.6,595.2 44.8,595.2c-12.8,0 -25.6,-6.4 -32,-12.8C6.4,569.6 0,556.8 0,544 0,537.6 6.4,524.8 12.8,512l467.2,-467.2C486.4,38.4 499.2,32 512,32s25.6,6.4 32,12.8L1011.2,512C1017.6,524.8 1024,537.6 1024,544c0,12.8 -6.4,25.6 -12.8,32 -6.4,6.4 -19.2,12.8 -32,12.8s-25.6,-6.4 -32,-12.8L512,147.2 211.2,448l0,422.4c0,12.8 12.8,25.6 25.6,25.6l115.2,0 0,-185.6c0,-25.6 19.2,-44.8 44.8,-44.8l230.4,0c25.6,0 44.8,19.2 44.8,44.8s-19.2,44.8 -44.8,44.8L441.6,755.2l0,185.6c0,25.6 -19.2,44.8 -44.8,44.8L230.4,985.6zM627.2,992c-25.6,0 -44.8,-19.2 -44.8,-44.8 0,-25.6 19.2,-44.8 44.8,-44.8l160,0c12.8,0 25.6,-12.8 25.6,-25.6l0,-256c0,-25.6 19.2,-44.8 44.8,-44.8 25.6,0 44.8,19.2 44.8,44.8l0,256c0,64 -51.2,115.2 -115.2,115.2L627.2,992z"
android:fillColor="@color/gray"/>
</vector>

View File

@ -0,0 +1,18 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="32dp"
android:height="32dp"
android:viewportWidth="1024"
android:viewportHeight="1024">
<path
android:fillColor="@color/gray"
android:pathData="M560.9,379.4c-13.1,-16.7 -31.5,-26.4 -50.4,-26.4 -18.2,0 -35.1,8.5 -47.5,24.4L355.3,521.4c-13.9,17.7 -17.5,37.3 -9.6,53.5 7.4,15.3 23.5,23 44.1,23h67.9a14.2,14.2 0,1 0,0 -28.4h-67.9c-9.3,0 -16.1,-1.9 -18.6,-7 -2.9,-6.1 -0.5,-14.2 6.6,-23.3l107.7,-144.3c14.3,-18.2 38.3,-16.9 52.9,1.7l108,141.3c7,8.9 9.4,17.9 6.3,24.3 -3.5,7.2 -13.1,7.2 -18.5,7.2h-48.5c-7.8,0 -9.1,8.5 -9.1,16.4v51.1c0,30.4 -27.2,57 -55.4,57 -23.7,0 -44.5,-11.1 -51.8,-29.7a13.8,13.8 0,0 0,-18.1 -8c-7.3,2.9 -10.3,11.1 -7.4,18.4 11.5,29.5 41.9,47.8 77.2,47.8 44.1,0 84,-39.1 84,-85.4v-39.1h29.1c20.5,0 36.5,-7.8 44.1,-23.3 8,-16.5 4.5,-35.9 -9.4,-53.6l-107.9,-141.6z"/>
<path
android:fillColor="@color/gray"
android:pathData="M460.9,628.8a13.8,13.8 0,0 0,0 -20.1c-5.3,-5.2 -14.8,-5.2 -20,0a13.8,13.8 0,0 0,-4.1 10.1c0,3.7 1.4,7.4 4.1,10.1 2.7,2.6 6.2,4.1 10.1,4.1 3.7,0 7.2,-1.6 9.9,-4.3z"/>
<path
android:fillColor="@color/gray"
android:pathData="M512,50C257.3,50 50,257.3 50,512 50,766.7 257.3,974 512,974S974,766.7 974,512C974,257.3 766.7,50 512,50zM512,917.1C288.6,917.1 106.9,735.4 106.9,512S288.6,106.9 512,106.9 917.1,288.6 917.1,512 735.4,917.1 512,917.1z"/>
<path
android:fillColor="@color/gray"
android:pathData="M606.5,167a14.3,14.3 0,0 0,-17.5 10c-2.1,7.6 2.4,15.4 10,17.5C741.6,233.4 841.2,364 841.2,512a14.2,14.2 0,0 0,28.4 0c0,-160.8 -108.2,-302.7 -263.1,-345zM512,182.8a342.5,342.5 0,0 1,19.2 0.5,14.2 14.2,0 0,0 0.8,-28.4 370.6,370.6 0,0 0,-19.9 -0.6,14.2 14.2,0 0,0 -0,28.4z"/>
</vector>

View File

@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="32dp"
android:height="32dp"
android:viewportWidth="1024"
android:viewportHeight="1024">
<path
android:fillColor="@color/gray"
android:pathData="M780.2,97.5a73.1,73.1 0,0 1,73.1 73.1v755.8l-341.3,-152.4L170.7,926.5L170.7,170.7a73.1,73.1 0,0 1,73.1 -73.1h536.4zM780.2,170.7L243.8,170.7v643l268.2,-119.7 268.2,119.7L780.2,170.7z"/>
</vector>

View File

@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="32dp"
android:height="32dp"
android:viewportWidth="1024"
android:viewportHeight="1024">
<path
android:fillColor="#FF000000"
android:pathData="M512,85.3a426.7,426.7 0,1 0,426.7 426.7A426.7,426.7 0,0 0,512 85.3zM676.3,429.7l-30.3,30.3a20.9,20.9 0,0 1,-29.9 0L554.7,398.1v348.6a21.3,21.3 0,0 1,-21.3 21.3h-42.7a21.3,21.3 0,0 1,-21.3 -21.3L469.3,398.1l-61.4,61.9a21.3,21.3 0,0 1,-30.3 0l-29.9,-30.3a21.3,21.3 0,0 1,0 -30.3l133.5,-134a32,32 0,0 1,22.6 -9.4h16.2a32.9,32.9 0,0 1,22.6 9.4l133.5,134a21.3,21.3 0,0 1,0 30.3z"/>
</vector>

View File

@ -0,0 +1,40 @@
<?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"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/main"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:application=".activity.CategoryActivity">
<ImageView
android:id="@+id/back"
android:layout_width="30dp"
android:layout_height="30dp"
android:layout_marginStart="25dp"
android:src="@drawable/back"
app:layout_constraintBottom_toBottomOf="@+id/text"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="@+id/text" />
<TextView
android:id="@+id/text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="25dp"
android:gravity="center"
android:textSize="24sp"
android:text="@string/app_name"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/recycler_view"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_marginTop="25dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintTop_toBottomOf="@+id/text" />
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@ -0,0 +1,98 @@
<?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"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/main"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#00000000"
tools:application=".activity.DrawingActivity">
<androidx.camera.view.PreviewView
android:id="@+id/preview"
android:layout_width="match_parent"
android:layout_height="match_parent" />
<androidx.appcompat.widget.AppCompatImageView
android:id="@+id/image_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scaleType="matrix" />
<FrameLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="16dp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">
<ImageView
android:id="@+id/back"
android:layout_width="48dp"
android:layout_height="match_parent"
android:clickable="true"
android:padding="8dp"
android:src="@drawable/back" />
</FrameLayout>
<LinearLayout
android:id="@+id/bottom_controls"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#80000000"
android:gravity="center"
android:orientation="vertical"
android:padding="16dp"
app:layout_constraintBottom_toBottomOf="parent">
<SeekBar
android:id="@+id/seekbar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="32dp"
android:layout_marginEnd="32dp"
android:layout_marginBottom="16dp"
android:max="100"
android:maxHeight="5dp"
android:progress="0"
android:progressDrawable="@drawable/progress_gradient"
android:thumb="@drawable/progress_thumb" />
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="center"
android:orientation="horizontal">
<ImageView
android:id="@+id/flash"
android:layout_width="48dp"
android:layout_height="48dp"
android:background="?attr/selectableItemBackgroundBorderless"
android:padding="8dp"
android:src="@drawable/un_flash" />
<ImageView
android:id="@+id/camera"
android:layout_width="48dp"
android:layout_height="48dp"
android:layout_marginStart="32dp"
android:layout_marginEnd="32dp"
android:background="?attr/selectableItemBackgroundBorderless"
android:padding="8dp"
android:src="@drawable/camera" />
<ImageView
android:id="@+id/photo"
android:layout_width="48dp"
android:layout_height="48dp"
android:background="?attr/selectableItemBackgroundBorderless"
android:padding="8dp"
android:src="@drawable/photo" />
</LinearLayout>
</LinearLayout>
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@ -5,15 +5,42 @@
android:id="@+id/main"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".activity.MainActivity">
android:background="#F5F5F5"
tools:application=".activity.MainActivity">
<TextView
android:id="@+id/title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Hello World!"
app:layout_constraintBottom_toBottomOf="parent"
android:layout_marginTop="8dp"
android:text="@string/app_name"
android:textSize="24sp"
android:textStyle="bold"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<com.google.android.material.tabs.TabLayout
android:id="@+id/tabLayout"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:background="@android:color/transparent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/title"
app:tabGravity="fill"
app:tabIndicatorHeight="0dp"
app:tabMode="fixed" />
<androidx.viewpager2.widget.ViewPager2
android:id="@+id/viewPager"
android:layout_width="0dp"
android:layout_height="0dp"
android:layout_marginTop="8dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/tabLayout" />
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@ -0,0 +1,48 @@
<?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"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/main"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".activity.SplashActivity">
<ImageView
android:id="@+id/splash_image"
android:layout_width="150dp"
android:layout_height="150dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.388" />
<TextView
android:id="@+id/splash_title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="32dp"
android:text="@string/app_name"
android:textSize="25sp"
android:textStyle="bold"
android:gravity="center"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/splash_image" />
<ProgressBar
android:id="@+id/progress_bar"
style="?android:attr/progressBarStyleHorizontal"
android:layout_width="0dp"
android:layout_height="5dp"
android:layout_marginStart="53dp"
android:layout_marginEnd="53dp"
android:layout_marginBottom="80dp"
android:max="100"
android:progress="0"
android:progressDrawable="@drawable/progress_gradient"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@ -0,0 +1,28 @@
<?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"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:application=".fragment.FavoriteFragment">
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/recycler_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginTop="16dp" />
<TextView
android:id="@+id/text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="There's nothing here yet"
android:textColor="@color/gray"
android:textSize="18sp"
android:visibility="gone"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@ -0,0 +1,28 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
xmlns:app="http://schemas.android.com/apk/res-auto"
tools:application=".fragment.HomeFragment">
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/recycler_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginTop="16dp" />
<TextView
android:id="@+id/text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="There's nothing here yet"
android:textColor="@color/gray"
android:textSize="18sp"
android:visibility="gone"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@ -0,0 +1,61 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
xmlns:app="http://schemas.android.com/apk/res-auto"
tools:application=".fragment.ImportFragment">
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/recycler_view"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_marginBottom="5dp"
app:layout_constraintBottom_toTopOf="@+id/upload"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="There's nothing here yet"
android:textColor="@color/gray"
android:textSize="18sp"
android:visibility="gone"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<LinearLayout
android:id="@+id/upload"
android:layout_width="0dp"
android:layout_height="50dp"
android:layout_marginTop="16dp"
android:layout_marginEnd="8dp"
android:layout_marginBottom="25dp"
android:gravity="center"
android:orientation="horizontal"
android:background="@drawable/rounded_rectangle"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintWidth_percent="0.5">
<ImageView
android:layout_width="40dp"
android:layout_height="48dp"
android:layout_gravity="center_horizontal"
android:layout_marginEnd="10dp"
android:padding="5dp"
android:src="@drawable/upload" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:text="UPLOAD"
android:textColor="@color/black" />
</LinearLayout>
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@ -0,0 +1,69 @@
<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="200dp"
android:background="@drawable/layout_background">
<ImageView
android:id="@+id/item_image"
android:layout_width="0dp"
android:layout_height="0dp"
android:background="@android:color/white"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toTopOf="@+id/title_container"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent" />
<ImageView
android:id="@+id/like"
android:layout_width="30dp"
android:layout_height="30dp"
android:src="@drawable/un_like"
android:background="?attr/selectableItemBackgroundBorderless"
android:elevation="4dp"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintEnd_toEndOf="parent"
android:layout_marginTop="16dp"
android:layout_marginEnd="16dp" />
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/title_container"
android:layout_width="0dp"
android:layout_height="wrap_content"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toStartOf="@+id/delete">
<TextView
android:id="@+id/title"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:textColor="@color/black"
android:textSize="14sp"
android:textStyle="bold"
android:padding="8dp"
android:gravity="center"
android:background="@drawable/text_background"
android:visibility="gone"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
<ImageView
android:id="@+id/delete"
android:layout_width="30dp"
android:layout_height="30dp"
android:src="@drawable/delete"
android:background="?attr/selectableItemBackgroundBorderless"
android:elevation="4dp"
android:visibility="gone"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
android:layout_marginBottom="16dp"
android:layout_marginEnd="16dp" />
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@ -0,0 +1,31 @@
<?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:id="@+id/main"
android:layout_width="match_parent"
android:layout_height="match_parent">
<ImageView
android:id="@+id/image_view"
android:layout_width="30dp"
android:layout_height="30dp"
android:layout_marginTop="20dp"
android:layout_marginBottom="12dp"
app:layout_constraintBottom_toTopOf="@+id/title"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="15dp"
android:textStyle="bold"
android:gravity="center"
android:text="@string/app_name"
android:textSize="14sp"
app:layout_constraintTop_toBottomOf="@+id/image_view" />
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@ -1,6 +0,0 @@
<?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>

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 982 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 44 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 341 KiB

View File

@ -2,4 +2,5 @@
<resources>
<color name="black">#FF000000</color>
<color name="white">#FFFFFFFF</color>
<color name="gray">#C0C0C0</color>
</resources>

View File

@ -1,3 +1,5 @@
<resources>
<string name="app_name">AR Drawing Space</string>
<!-- TODO: Remove or change this placeholder text -->
<string name="hello_blank_fragment">Hello blank fragment</string>
</resources>

6
keystore.properties Normal file
View File

@ -0,0 +1,6 @@
app_name=AR Drawing Space
package_name=com.ardrawing.ardrawingspace
keystoreFile=app/ARDrawingSpace.jks
key_alias=ARDrawingSpacekey0
key_store_password=ARDrawingSpace
key_password=ARDrawingSpace