diff --git a/app/.gitignore b/app/.gitignore new file mode 100644 index 0000000..42afabf --- /dev/null +++ b/app/.gitignore @@ -0,0 +1 @@ +/build \ No newline at end of file diff --git a/app/build.gradle b/app/build.gradle new file mode 100644 index 0000000..4be26b7 --- /dev/null +++ b/app/build.gradle @@ -0,0 +1,66 @@ +plugins { + id 'com.android.application' +} + +android { + compileSdk 32 + + defaultConfig { + applicationId "com.xypower.mpremote" + minSdk 24 + targetSdk 32 + versionCode 1 + versionName "1.0" + + testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" + externalNativeBuild { + cmake { + cppFlags '' + } + } + } + + buildTypes { + release { + minifyEnabled false + proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' + } + } + compileOptions { + sourceCompatibility JavaVersion.VERSION_1_8 + targetCompatibility JavaVersion.VERSION_1_8 + } + externalNativeBuild { + cmake { + path file('src/main/cpp/CMakeLists.txt') + version '3.18.1' + } + } + buildFeatures { + viewBinding true + } + + packagingOptions { + exclude 'META-INF/LICENSE-notice.md' + exclude 'META-INF/LICENSE.md' + + } + +} + +dependencies { + + implementation 'androidx.appcompat:appcompat:1.3.0' + implementation 'com.google.android.material:material:1.4.0' + implementation 'androidx.constraintlayout:constraintlayout:2.0.4' + implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.6.10" + + // https://mvnrepository.com/artifact/dev.mobile/dadb + implementation 'dev.mobile:dadb:1.2.7' + implementation files('libs/common-release.aar') + + testImplementation 'junit:junit:4.13.2' + androidTestImplementation 'androidx.test.ext:junit:1.1.3' + androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0' + +} \ No newline at end of file diff --git a/app/libs/common-release.aar b/app/libs/common-release.aar new file mode 100644 index 0000000..79a6c02 Binary files /dev/null and b/app/libs/common-release.aar differ diff --git a/app/proguard-rules.pro b/app/proguard-rules.pro new file mode 100644 index 0000000..481bb43 --- /dev/null +++ b/app/proguard-rules.pro @@ -0,0 +1,21 @@ +# 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 diff --git a/app/src/androidTest/java/com/xypower/mpremote/ExampleInstrumentedTest.java b/app/src/androidTest/java/com/xypower/mpremote/ExampleInstrumentedTest.java new file mode 100644 index 0000000..c34baa5 --- /dev/null +++ b/app/src/androidTest/java/com/xypower/mpremote/ExampleInstrumentedTest.java @@ -0,0 +1,26 @@ +package com.xypower.mpremote; + +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 Testing documentation + */ +@RunWith(AndroidJUnit4.class) +public class ExampleInstrumentedTest { + @Test + public void useAppContext() { + // Context of the app under test. + Context appContext = InstrumentationRegistry.getInstrumentation().getTargetContext(); + assertEquals("com.xypower.mpremote", appContext.getPackageName()); + } +} \ No newline at end of file diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml new file mode 100644 index 0000000..c3bfd51 --- /dev/null +++ b/app/src/main/AndroidManifest.xml @@ -0,0 +1,46 @@ + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/cpp/CMakeLists.txt b/app/src/main/cpp/CMakeLists.txt new file mode 100644 index 0000000..dd89586 --- /dev/null +++ b/app/src/main/cpp/CMakeLists.txt @@ -0,0 +1,48 @@ +# For more information about using CMake with Android Studio, read the +# documentation: https://d.android.com/studio/projects/add-native-code.html + +# Sets the minimum version of CMake required to build the native library. + +cmake_minimum_required(VERSION 3.18.1) + +# Declares and names the project. + +project("mpremote") + +# Creates and names a library, sets it as either STATIC +# or SHARED, and provides the relative paths to its source code. +# You can define multiple libraries, and CMake builds them for you. +# Gradle automatically packages shared libraries with your APK. + +add_library( # Sets the name of the library. + mpremote + + # Sets the library as a shared library. + SHARED + + # Provides a relative path to your source file(s). + native-lib.cpp) + +# Searches for a specified prebuilt library and stores the path as a +# variable. Because CMake includes system libraries in the search path by +# default, you only need to specify the name of the public NDK library +# you want to add. CMake verifies that the library exists before +# completing its build. + +find_library( # Sets the name of the path variable. + log-lib + + # Specifies the name of the NDK library that + # you want CMake to locate. + log) + +# Specifies libraries CMake should link to your target library. You +# can link multiple libraries, such as libraries you define in this +# build script, prebuilt third-party libraries, or system libraries. + +target_link_libraries( # Specifies the target library. + mpremote + + # Links the target library to the log library + # included in the NDK. + ${log-lib}) \ No newline at end of file diff --git a/app/src/main/cpp/native-lib.cpp b/app/src/main/cpp/native-lib.cpp new file mode 100644 index 0000000..b781097 --- /dev/null +++ b/app/src/main/cpp/native-lib.cpp @@ -0,0 +1,10 @@ +#include +#include + +extern "C" JNIEXPORT jstring JNICALL +Java_com_xypower_mpremote_MainActivity_stringFromJNI( + JNIEnv* env, + jobject /* this */) { + std::string hello = "Hello from C++"; + return env->NewStringUTF(hello.c_str()); +} \ No newline at end of file diff --git a/app/src/main/java/com/xypower/mpremote/ImageActivity.java b/app/src/main/java/com/xypower/mpremote/ImageActivity.java new file mode 100644 index 0000000..ba32970 --- /dev/null +++ b/app/src/main/java/com/xypower/mpremote/ImageActivity.java @@ -0,0 +1,155 @@ +package com.xypower.mpremote; + +import android.annotation.SuppressLint; + +import androidx.appcompat.app.ActionBar; +import androidx.appcompat.app.AppCompatActivity; + +import android.content.Intent; +import android.graphics.Bitmap; +import android.graphics.BitmapFactory; +import android.graphics.drawable.BitmapDrawable; +import android.graphics.drawable.Drawable; +import android.os.Build; +import android.os.Bundle; +import android.os.Handler; +import android.os.Looper; +import android.view.MotionEvent; +import android.view.View; +import android.view.WindowInsets; + +import com.xypower.mpremote.databinding.ActivityImageBinding; + +import java.io.File; + +/** + * An example full-screen activity that shows and hides the system UI (i.e. + * status bar and navigation/system bar) with user interaction. + */ +public class ImageActivity extends AppCompatActivity { + /** + * Whether or not the system UI should be auto-hidden after + * {@link #AUTO_HIDE_DELAY_MILLIS} milliseconds. + */ + private static final boolean AUTO_HIDE = true; + + /** + * If {@link #AUTO_HIDE} is set, the number of milliseconds to wait after + * user interaction before hiding the system UI. + */ + private static final int AUTO_HIDE_DELAY_MILLIS = 3000; + + /** + * Some older devices needs a small delay between UI widget updates + * and a change of the status and navigation bar. + */ + private static final int UI_ANIMATION_DELAY = 300; + private final Handler mHideHandler = new Handler(Looper.myLooper()); + + /** + * Touch listener to use for in-layout UI controls to delay hiding the + * system UI. This is to prevent the jarring behavior of controls going away + * while interacting with activity UI. + */ + private final View.OnTouchListener mDelayHideTouchListener = new View.OnTouchListener() { + @Override + public boolean onTouch(View view, MotionEvent motionEvent) { + switch (motionEvent.getAction()) { + case MotionEvent.ACTION_DOWN: + + break; + case MotionEvent.ACTION_UP: + ImageActivity.this.finish(); + break; + default: + break; + } + return false; + } + }; + private ActivityImageBinding binding; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + binding = ActivityImageBinding.inflate(getLayoutInflater()); + setContentView(binding.getRoot()); + + binding.imageViewOpt.setImageDrawable(null); + binding.imageViewAE.setImageDrawable(null); + + binding.imageViewOpt.setVisibility(View.GONE); + + binding.imageViewAE.setClickable(true); + binding.imageViewAE.setOnTouchListener(mDelayHideTouchListener); + + binding.imageViewAE.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + ImageActivity.this.finish(); + } + }); + + binding.imageViewOpt.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + ImageActivity.this.finish(); + } + }); + + Intent intent = getIntent(); + String path = intent.getStringExtra("path"); + if (path != null) { + loadImage(path); + } + + } + + private boolean loadImage(String path) { + + binding.imageViewOpt.setVisibility(View.GONE); + binding.imageViewAE.setVisibility(View.GONE); + + File file = new File(path); + if (!file.exists()) { + return false; + } + Drawable drawable = loadDrawable(path); + Drawable drawableAE = loadDrawable(path + ".ae.jpg"); + if (drawableAE == null && drawable == null) { + return false; + } + if (drawableAE != null && drawable != null) { + binding.imageViewOpt.setImageDrawable(drawable); + binding.imageViewAE.setImageDrawable(drawableAE); + binding.imageViewOpt.setVisibility(View.VISIBLE); + binding.imageViewAE.setVisibility(View.VISIBLE); + } + else if (drawable != null) { + binding.imageViewOpt.setImageDrawable(drawable); + binding.imageViewAE.setImageDrawable(null); + binding.imageViewOpt.setVisibility(View.VISIBLE); + // binding.imageViewAE.setVisibility(View.VISIBLE); + } + + return true; + } + + private Drawable loadDrawable(String file) { + if (file == null || file.isEmpty()) { + return null; + } + + Drawable drawable = null; + try { + Bitmap bitmap = BitmapFactory.decodeFile(file); + drawable = new BitmapDrawable(getResources(), bitmap); + } catch (Exception e) { + e.printStackTrace(); + } + + return drawable; + } + +} \ No newline at end of file diff --git a/app/src/main/java/com/xypower/mpremote/MainActivity.java b/app/src/main/java/com/xypower/mpremote/MainActivity.java new file mode 100644 index 0000000..e834ed2 --- /dev/null +++ b/app/src/main/java/com/xypower/mpremote/MainActivity.java @@ -0,0 +1,394 @@ +package com.xypower.mpremote; + +import androidx.appcompat.app.ActionBar; +import androidx.appcompat.app.AppCompatActivity; + +import android.content.Context; +import android.content.Intent; +import android.content.res.Resources; +import android.net.wifi.WifiManager; +import android.os.Bundle; +import android.os.Handler; +import android.text.TextUtils; +import android.text.format.Formatter; +import android.view.View; +import android.widget.TextView; +import android.widget.Toast; + +import com.xypower.common.FileUtils; +import com.xypower.common.MicroPhotoContext; +import com.xypower.mpremote.databinding.ActivityMainBinding; + +import org.json.JSONException; +import org.json.JSONObject; + +import java.io.BufferedReader; +import java.io.File; +import java.io.FileInputStream; +import java.io.InputStreamReader; +import java.io.UnsupportedEncodingException; +import java.net.Socket; +import java.security.PrivateKey; +import java.text.SimpleDateFormat; +import java.util.Date; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import dadb.AdbKeyPair; +import dadb.AdbShellResponse; +import dadb.AdbShellStream; +import dadb.AdbStream; +import dadb.Dadb; + +public class MainActivity extends AppCompatActivity { + + // Used to load the 'mpremote' library on application startup. + static { + System.loadLibrary("mpremote"); + } + + private static final String PACKAGE_NAME_MP = "com.xypower.mpapp"; + private static final String REMOTE_PATH_ROOT = "/sdcard/" + PACKAGE_NAME_MP + "/"; + private static final String REMOTE_PATH_DATA = REMOTE_PATH_ROOT + "data/"; + private static final String REMOTE_PATH_PHOTOS = REMOTE_PATH_ROOT + "photos/"; + private static final String REMOTE_PATH_TMP = REMOTE_PATH_ROOT + "tmp/"; + + private static final String KEY_APP_BV = "app.bv"; + private static final String KEY_APP_BCV = "app.bcv"; + private static final String KEY_RO_SERIALNO = "ro.serialno"; + + private static final int ADB_SERVER_PORT = 5555; + + private static final String WIFI_IP_PREFIX = "192.168.50."; + // private static final String WIFI_IP_DEVIDE = "192.168.50.1"; + private static final String WIFI_IP_DEVIDE = "192.168.50.137"; + + // [vendor.ril.nw.signalstrength.lte.1]: [-85,27] + //[vendor.ril.nw.signalstrength.lte.2]: [-86,27] + + private ActivityMainBinding binding; + private Handler mHandler; + private Dadb mAdb; + + private Map mProps = new HashMap<>(); + + private int mBatteryVoltage = -1; + private int mBatteryChargingVoltage = -1; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + binding = ActivityMainBinding.inflate(getLayoutInflater()); + setContentView(binding.getRoot()); + + mHandler = new Handler(); + + File file = new File(getFilesDir(), ".keypair"); + if (!file.exists()) { + file.mkdirs(); + } + + final File pubKeyFile = new File(file, "pub.key"); + final File priKeyFile = new File(file, "pri.key"); + if (!priKeyFile.exists() || !pubKeyFile.exists()) { + AdbKeyPair.generate(priKeyFile, pubKeyFile); + } + + final File fileTmp = new File(getFilesDir(), "tmp"); + if (!fileTmp.exists()) { + fileTmp.mkdirs(); + } + + final AdbKeyPair adbKeyPair = AdbKeyPair.read(priKeyFile, pubKeyFile); + final Context context = getApplicationContext(); + + (new Thread(new Runnable() { + @Override + public void run() { + + WifiManager wifiManager = (WifiManager) getApplicationContext().getSystemService(Context.WIFI_SERVICE); + + String ipAddressByWifi = null; + if (wifiManager != null) { + ipAddressByWifi = Formatter.formatIpAddress(wifiManager.getDhcpInfo().ipAddress); + } + + if (ipAddressByWifi.contains(WIFI_IP_PREFIX)) { + + Socket mSocket = null; + try { + mSocket = new Socket(WIFI_IP_DEVIDE, ADB_SERVER_PORT); + if (mSocket.isConnected()) { + mSocket.close(); + } + } catch (Exception ex) { + ex.printStackTrace(); + } + + Dadb adb = Dadb.discover(WIFI_IP_DEVIDE, adbKeyPair); + + if (adb != null) { + + mAdb = adb; + + AdbShellResponse adbShellResponse = null; + try { + adbShellResponse = mAdb.shell("getprop"); + } catch (Exception ex) { + ex.printStackTrace(); + } + + if (adbShellResponse.getExitCode() == 0) { + String[] lines = FileUtils.splitLines(adbShellResponse.getAllOutput()); + for (String line : lines) { + int pos = line.indexOf("]: ["); + if (pos != -1) { + String key = line.substring(1, pos); + String val = line.substring(pos + 4, line.length() - 1); + mProps.put(key, val); + } + } + } + + String remoteFilePath = REMOTE_PATH_TMP + "bv.txt"; + String cmd = "am start -n " + PACKAGE_NAME_MP + "/" + PACKAGE_NAME_MP + ".BridgeActivity --es action \"query_bv\" --es path \"" + remoteFilePath + "\""; + adbShellResponse = null; + try { + adbShellResponse = mAdb.shell(cmd); + } catch (Exception ex) { + ex.printStackTrace(); + } + + if (adbShellResponse.getExitCode() == 0) { + File localFilePath = new File(fileTmp, "bv.txt"); + if (localFilePath.exists()) { + localFilePath.delete(); + } + + for (int idx = 0; idx < 10; idx++) { + + boolean res = pullFile(mAdb, remoteFilePath, localFilePath); + + if (res) { + String content = FileUtils.readTextFile(localFilePath.getAbsolutePath()); + if (!TextUtils.isEmpty(content)) { + int pos = content.indexOf(" "); + if (pos != -1) { + String bv = content.substring(0, pos); + String bcv = content.substring(pos + 1); + + if (!TextUtils.isEmpty(bv)) { + mBatteryVoltage = Integer.parseInt(bv); + } + if (!TextUtils.isEmpty(bcv)) { + mBatteryChargingVoltage = Integer.parseInt(bcv); + } + } + } + + localFilePath.delete(); + break; + } + + try { + Thread.sleep(1000); + } catch (Exception ex) { + ex.printStackTrace(); + } + } + + try { + mAdb.shell("rm " + remoteFilePath); + } catch (Exception ex) { + ex.printStackTrace(); + } + } + + File appConfigFile = new File(fileTmp, "App.json"); + boolean res = pullFile(adb, REMOTE_PATH_DATA + "App.json", appConfigFile); + if (res) { + + final MicroPhotoContext.AppConfig appConfig = MicroPhotoContext.getMpAppConfig(context, appConfigFile.getAbsolutePath()); + MainActivity.this.mHandler.post(new Runnable() { + @Override + public void run() { + MainActivity.this.showAppInfo(appConfig); + } + }); + } + + } else { + mHandler.post(new Runnable() { + @Override + public void run() { + Toast.makeText(MainActivity.this, R.string.err_dev_not_found, Toast.LENGTH_LONG).show(); + } + }); + } + + } + + // Dadb dadb = Dadb.create("localhost", 5555, adbKeyPair); + // Dadb.discover(); + + + + } + })).start(); + + binding.takePhoto1.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View view) { + takePhoto(1, 255, true); + } + }); + + } + + + + protected boolean pullFile(Dadb adb, String remoteFilePath, File localFilePath) { + if (localFilePath.exists()) { + localFilePath.delete(); + } + + boolean res = false; + try { + adb.pull(localFilePath, remoteFilePath); + res = true; + } catch (Exception ex) { + ex.printStackTrace(); + } + + return res && localFilePath.exists(); + } + + protected long takePhoto(int channel, int preset, boolean photoOrVideo) { + if (mAdb == null) { + return 0; + } + + SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyyMMddhhmmss"); + Date dt = new Date(); + int cameraId = channel - 1; + + final String fileName = "IMG-" + Integer.toString(channel) + simpleDateFormat.format(dt) + ".jpg"; + final String remoteFilePath = REMOTE_PATH_TMP + fileName; + + String usb = (channel == 4) ? "true" : "false"; + final String cmd = "am start -n " + PACKAGE_NAME_MP + "/" + PACKAGE_NAME_MP + ".BridgeActivity --es action \"take_photo\" --es path \"" + + remoteFilePath + "\" --ez usb " + usb + " --ei cameraId " + Integer.toString(cameraId) + " --ei channel " + + Integer.toString(channel) + " --ei preset " + Integer.toString(preset); + // adbShellResponse = null; + + (new Thread(new Runnable() { + @Override + public void run() { + AdbShellResponse adbShellResponse = null; + try { + adbShellResponse = mAdb.shell(cmd); + } catch (Exception ex) { + ex.printStackTrace(); + } + + if (adbShellResponse != null) { + if (adbShellResponse.getExitCode() == 0) { + + File localFilePath = new File(getFilesDir(), fileName); + + for (int idx = 0; idx < 10; idx++) { + + boolean res = pullFile(mAdb, remoteFilePath, localFilePath); + + if (res) { + + mHandler.post(new Runnable() { + @Override + public void run() { + showPhoto(localFilePath.getAbsolutePath()); + } + }); + break; + } + + try { + Thread.sleep(1000); + } catch (Exception ex) { + ex.printStackTrace(); + } + } + + try { + mAdb.shell("rm " + remoteFilePath); + } catch (Exception ex) { + ex.printStackTrace(); + } + + } + } + } + })).start(); + + return 0; + } + + private void showPhoto(final String filePath) { + Intent intent = new Intent(MainActivity.this, ImageActivity.class); + intent.putExtra("path", filePath); + // intent.putExtra("info", info); + startActivity(intent); + } + + private void showVideo(final String filePath) { + + } + + private void showAppInfo(final MicroPhotoContext.AppConfig appConfig) { + ActionBar actionBar = getSupportActionBar(); + + // 视频浏览,拍照,设备信息及状态查询(编号,版本,双SIM卡状态,电池及太阳能电压等),可以参考东视 + + Resources resources = getResources(); + // String buildTime = BuildConfig.BUILD_ + // Date date = new Date(BuildConfig.BUILD_TIMESTAMP); + // SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); + // SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm"); + String title = resources.getString(R.string.app_name); + if (!TextUtils.isEmpty(appConfig.cmdid)) { + title += " - " + appConfig.cmdid; + } + actionBar.setTitle(title); + + StringBuilder stringBuilder = new StringBuilder(); + + if (mProps.containsKey(KEY_RO_SERIALNO)) { + String val = (String)mProps.get(KEY_RO_SERIALNO); + stringBuilder.append("序列号:" + val); + stringBuilder.append("\r\n"); + } + + stringBuilder.append("主站:" + appConfig.server + ":" + Integer.toString(appConfig.port)); + stringBuilder.append("\r\n"); + stringBuilder.append("通道数:" + Integer.toString(appConfig.channels)); + stringBuilder.append("\r\n"); + + if (mBatteryVoltage != -1) { + stringBuilder.append("电池电压:" + Integer.toString(mBatteryVoltage / 1000) + "." + Integer.toString((mBatteryVoltage % 1000) / 100)); + stringBuilder.append("\r\n"); + } + if (mBatteryChargingVoltage != -1) { + stringBuilder.append("充电电压:" + Integer.toString(mBatteryChargingVoltage / 1000) + "." + Integer.toString((mBatteryChargingVoltage % 1000) / 100)); + stringBuilder.append("\r\n"); + } + + binding.deviceInfo.setText(stringBuilder.toString()); + } + + /** + * A native method that is implemented by the 'mpremote' native library, + * which is packaged with this application. + */ + public native String stringFromJNI(); +} \ No newline at end of file diff --git a/app/src/main/java/com/xypower/mpremote/WifiScanActivity.java b/app/src/main/java/com/xypower/mpremote/WifiScanActivity.java new file mode 100644 index 0000000..d1006a6 --- /dev/null +++ b/app/src/main/java/com/xypower/mpremote/WifiScanActivity.java @@ -0,0 +1,14 @@ +package com.xypower.mpremote; + +import androidx.appcompat.app.AppCompatActivity; + +import android.os.Bundle; + +public class WifiScanActivity extends AppCompatActivity { + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_wifi_scan); + } +} \ No newline at end of file diff --git a/app/src/main/res/drawable-v24/ic_launcher_foreground.xml b/app/src/main/res/drawable-v24/ic_launcher_foreground.xml new file mode 100644 index 0000000..2b068d1 --- /dev/null +++ b/app/src/main/res/drawable-v24/ic_launcher_foreground.xml @@ -0,0 +1,30 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_launcher_background.xml b/app/src/main/res/drawable/ic_launcher_background.xml new file mode 100644 index 0000000..07d5da9 --- /dev/null +++ b/app/src/main/res/drawable/ic_launcher_background.xml @@ -0,0 +1,170 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/layout/activity_image.xml b/app/src/main/res/layout/activity_image.xml new file mode 100644 index 0000000..6c1602a --- /dev/null +++ b/app/src/main/res/layout/activity_image.xml @@ -0,0 +1,33 @@ + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml new file mode 100644 index 0000000..61fbbd7 --- /dev/null +++ b/app/src/main/res/layout/activity_main.xml @@ -0,0 +1,31 @@ + + + + + +