cpp
# include "MD5.h"
using namespace std;
# define S11 7
# define S12 12
# define S13 17
# define S14 22
# define S21 5
# define S22 9
# define S23 14
# define S24 20
# define S31 4
# define S32 11
# define S33 16
# define S34 23
# define S41 6
# define S42 10
# define S43 15
# define S44 21
# define F ( x, y, z) ( ( ( x) & ( y) ) | ( ( ~ x) & ( z) ) )
# define G ( x, y, z) ( ( ( x) & ( z) ) | ( ( y) & ( ~ z) ) )
# define H ( x, y, z) ( ( x) ^ ( y) ^ ( z) )
# define I ( x, y, z) ( ( y) ^ ( ( x) | ( ~ z) ) )
# define ROTATE_LEFT ( x, n) ( ( ( x) << ( n) ) | ( ( x) >> ( 32 - ( n) ) ) )
# define FF ( a, b, c, d, x, s, ac) {
\
( a) += F ( ( b) , ( c) , ( d) ) + ( x) + ac; \
( a) = ROTATE_LEFT ( ( a) , ( s) ) ; \
( a) += ( b) ; \
}
# define GG ( a, b, c, d, x, s, ac) {
\
( a) += G ( ( b) , ( c) , ( d) ) + ( x) + ac; \
( a) = ROTATE_LEFT ( ( a) , ( s) ) ; \
( a) += ( b) ; \
}
# define HH ( a, b, c, d, x, s, ac) {
\
( a) += H ( ( b) , ( c) , ( d) ) + ( x) + ac; \
( a) = ROTATE_LEFT ( ( a) , ( s) ) ; \
( a) += ( b) ; \
}
# define II ( a, b, c, d, x, s, ac) {
\
( a) += I ( ( b) , ( c) , ( d) ) + ( x) + ac; \
( a) = ROTATE_LEFT ( ( a) , ( s) ) ; \
( a) += ( b) ; \
}
const byte MD5:: PADDING[ 64 ] = {
0x80 } ;
const char MD5:: HEX[ 16 ] = {
'0' , '1' , '2' , '3' ,
'4' , '5' , '6' , '7' ,
'8' , '9' , 'a' , 'b' ,
'c' , 'd' , 'e' , 'f'
} ;
MD5 :: MD5 ( ) {
reset ( ) ;
}
MD5 :: MD5 ( const void * input, size_t length) {
reset ( ) ;
update ( input, length) ;
}
MD5 :: MD5 ( const string& str) {
reset ( ) ;
update ( str) ;
}
MD5 :: MD5 ( ifstream& in) {
reset ( ) ;
update ( in) ;
}
byte* MD5 :: digest ( ) {
if ( ! _finished) {
_finished = true ;
final ( ) ;
}
return _digest;
}
void MD5 :: reset ( ) {
_finished = false ;
_count[ 0 ] = _count[ 1 ] = 0 ;
_state[ 0 ] = 0x67452301 ;
_state[ 1 ] = 0xefcdab89 ;
_state[ 2 ] = 0x98badcfe ;
_state[ 3 ] = 0x10325476 ;
}
void MD5 :: update ( const void * input, size_t length) {
update ( ( const byte* ) input, length) ;
}
void MD5 :: update ( const string& str) {
update ( ( const byte* ) str. c_str ( ) , str. length ( ) ) ;
}
void MD5 :: update ( ifstream& in) {
if ( ! in) {
return ;
}
std:: streamsize length;
char buffer[ BUFFER_SIZE] ;
while ( ! in. eof ( ) ) {
in. read ( buffer, BUFFER_SIZE) ;
length = in. gcount ( ) ;
if ( length > 0 ) {
update ( buffer, length) ;
}
}
in. close ( ) ;
}
void MD5 :: update ( const byte* input, size_t length) {
uint32 i, index, partLen;
_finished = false ;
index = ( uint32) ( ( _count[ 0 ] >> 3 ) & 0x3f ) ;
if ( ( _count[ 0 ] += ( ( uint32) length << 3 ) ) < ( ( uint32) length << 3 ) ) {
++ _count[ 1 ] ;
}
_count[ 1 ] += ( ( uint32) length >> 29 ) ;
partLen = 64 - index;
if ( length >= partLen) {
memcpy ( & _buffer[ index] , input, partLen) ;
transform ( _buffer) ;
for ( i = partLen; i + 63 < length; i += 64 ) {
transform ( & input[ i] ) ;
}
index = 0 ;
}
else {
i = 0 ;
}
memcpy ( & _buffer[ index] , & input[ i] , length - i) ;
}
void MD5 :: final ( ) {
byte bits[ 8 ] ;
uint32 oldState[ 4 ] ;
uint32 oldCount[ 2 ] ;
uint32 index, padLen;
memcpy ( oldState, _state, 16 ) ;
memcpy ( oldCount, _count, 8 ) ;
encode ( _count, bits, 8 ) ;
index = ( uint32) ( ( _count[ 0 ] >> 3 ) & 0x3f ) ;
padLen = ( index < 56 ) ? ( 56 - index) : ( 120 - index) ;
update ( PADDING, padLen) ;
update ( bits, 8 ) ;
encode ( _state, _digest, 16 ) ;
memcpy ( _state, oldState, 16 ) ;
memcpy ( _count, oldCount, 8 ) ;
}
void MD5 :: transform ( const byte block[ 64 ] ) {
uint32 a = _state[ 0 ] , b = _state[ 1 ] , c = _state[ 2 ] , d = _state[ 3 ] , x[ 16 ] ;
decode ( block, x, 64 ) ;
FF ( a, b, c, d, x[ 0 ] , S11, 0xd76aa478 ) ;
FF ( d, a, b, c, x[ 1 ] , S12, 0xe8c7b756 ) ;
FF ( c, d, a, b, x[ 2 ] , S13, 0x242070db ) ;
FF ( b, c, d, a, x[ 3 ] , S14, 0xc1bdceee ) ;
FF ( a, b, c, d, x[ 4 ] , S11, 0xf57c0faf ) ;
FF ( d, a, b, c, x[ 5 ] , S12, 0x4787c62a ) ;
FF ( c, d, a, b, x[ 6 ] , S13, 0xa8304613 ) ;
FF ( b, c, d, a, x[ 7 ] , S14, 0xfd469501 ) ;
FF ( a, b, c, d, x[ 8 ] , S11, 0x698098d8 ) ;
FF ( d, a, b, c, x[ 9 ] , S12, 0x8b44f7af ) ;
FF ( c, d, a, b, x[ 10 ] , S13, 0xffff5bb1 ) ;
FF ( b, c, d, a, x[ 11 ] , S14, 0x895cd7be ) ;
FF ( a, b, c, d, x[ 12 ] , S11, 0x6b901122 ) ;
FF ( d, a, b, c, x[ 13 ] , S12, 0xfd987193 ) ;
FF ( c, d, a, b, x[ 14 ] , S13, 0xa679438e ) ;
FF ( b, c, d, a, x[ 15 ] , S14, 0x49b40821 ) ;
GG ( a, b, c, d, x[ 1 ] , S21, 0xf61e2562 ) ;
GG ( d, a, b, c, x[ 6 ] , S22, 0xc040b340 ) ;
GG ( c, d, a, b, x[ 11 ] , S23, 0x265e5a51 ) ;
GG ( b, c, d, a, x[ 0 ] , S24, 0xe9b6c7aa ) ;
GG ( a, b, c, d, x[ 5 ] , S21, 0xd62f105d ) ;
GG ( d, a, b, c, x[ 10 ] , S22, 0x2441453 ) ;
GG ( c, d, a, b, x[ 15 ] , S23, 0xd8a1e681 ) ;
GG ( b, c, d, a, x[ 4 ] , S24, 0xe7d3fbc8 ) ;
GG ( a, b, c, d, x[ 9 ] , S21, 0x21e1cde6 ) ;
GG ( d, a, b, c, x[ 14 ] , S22, 0xc33707d6 ) ;
GG ( c, d, a, b, x[ 3 ] , S23, 0xf4d50d87 ) ;
GG ( b, c, d, a, x[ 8 ] , S24, 0x455a14ed ) ;
GG ( a, b, c, d, x[ 13 ] , S21, 0xa9e3e905 ) ;
GG ( d, a, b, c, x[ 2 ] , S22, 0xfcefa3f8 ) ;
GG ( c, d, a, b, x[ 7 ] , S23, 0x676f02d9 ) ;
GG ( b, c, d, a, x[ 12 ] , S24, 0x8d2a4c8a ) ;
HH ( a, b, c, d, x[ 5 ] , S31, 0xfffa3942 ) ;
HH ( d, a, b, c, x[ 8 ] , S32, 0x8771f681 ) ;
HH ( c, d, a, b, x[ 11 ] , S33, 0x6d9d6122 ) ;
HH ( b, c, d, a, x[ 14 ] , S34, 0xfde5380c ) ;
HH ( a, b, c, d, x[ 1 ] , S31, 0xa4beea44 ) ;
HH ( d, a, b, c, x[ 4 ] , S32, 0x4bdecfa9 ) ;
HH ( c, d, a, b, x[ 7 ] , S33, 0xf6bb4b60 ) ;
HH ( b, c, d, a, x[ 10 ] , S34, 0xbebfbc70 ) ;
HH ( a, b, c, d, x[ 13 ] , S31, 0x289b7ec6 ) ;
HH ( d, a, b, c, x[ 0 ] , S32, 0xeaa127fa ) ;
HH ( c, d, a, b, x[ 3 ] , S33, 0xd4ef3085 ) ;
HH ( b, c, d, a, x[ 6 ] , S34, 0x4881d05 ) ;
HH ( a, b, c, d, x[ 9 ] , S31, 0xd9d4d039 ) ;
HH ( d, a, b, c, x[ 12 ] , S32, 0xe6db99e5 ) ;
HH ( c, d, a, b, x[ 15 ] , S33, 0x1fa27cf8 ) ;
HH ( b, c, d, a, x[ 2 ] , S34, 0xc4ac5665 ) ;
II ( a, b, c, d, x[ 0 ] , S41, 0xf4292244 ) ;
II ( d, a, b, c, x[ 7 ] , S42, 0x432aff97 ) ;
II ( c, d, a, b, x[ 14 ] , S43, 0xab9423a7 ) ;
II ( b, c, d, a, x[ 5 ] , S44, 0xfc93a039 ) ;
II ( a, b, c, d, x[ 12 ] , S41, 0x655b59c3 ) ;
II ( d, a, b, c, x[ 3 ] , S42, 0x8f0ccc92 ) ;
II ( c, d, a, b, x[ 10 ] , S43, 0xffeff47d ) ;
II ( b, c, d, a, x[ 1 ] , S44, 0x85845dd1 ) ;
II ( a, b, c, d, x[ 8 ] , S41, 0x6fa87e4f ) ;
II ( d, a, b, c, x[ 15 ] , S42, 0xfe2ce6e0 ) ;
II ( c, d, a, b, x[ 6 ] , S43, 0xa3014314 ) ;
II ( b, c, d, a, x[ 13 ] , S44, 0x4e0811a1 ) ;
II ( a, b, c, d, x[ 4 ] , S41, 0xf7537e82 ) ;
II ( d, a, b, c, x[ 11 ] , S42, 0xbd3af235 ) ;
II ( c, d, a, b, x[ 2 ] , S43, 0x2ad7d2bb ) ;
II ( b, c, d, a, x[ 9 ] , S44, 0xeb86d391 ) ;
_state[ 0 ] += a;
_state[ 1 ] += b;
_state[ 2 ] += c;
_state[ 3 ] += d;
}
void MD5 :: encode ( const uint32* input, byte* output, size_t length) {
for ( size_t i = 0 , j = 0 ; j < length; ++ i, j += 4 ) {
output[ j] = ( byte) ( input[ i] & 0xff ) ;
output[ j + 1 ] = ( byte) ( ( input[ i] >> 8 ) & 0xff ) ;
output[ j + 2 ] = ( byte) ( ( input[ i] >> 16 ) & 0xff ) ;
output[ j + 3 ] = ( byte) ( ( input[ i] >> 24 ) & 0xff ) ;
}
}
void MD5 :: decode ( const byte* input, uint32* output, size_t length) {
for ( size_t i = 0 , j = 0 ; j < length; ++ i, j += 4 ) {
output[ i] = ( ( uint32) input[ j] ) | ( ( ( uint32) input[ j + 1 ] ) << 8 ) |
( ( ( uint32) input[ j + 2 ] ) << 16 ) | ( ( ( uint32) input[ j + 3 ] ) << 24 ) ;
}
}
string MD5 :: bytesToHexString ( byte* input, size_t length) {
string str;
str. reserve ( length << 1 ) ;
for ( size_t i = 0 ; i < length; ++ i) {
int t = input[ i] ;
int a = t / 16 ;
int b = t % 16 ;
str. append ( 1 , HEX[ a] ) ;
str. append ( 1 , HEX[ b] ) ;
}
return str;
}
string MD5 :: toString ( ) {
return bytesToHexString ( digest ( ) , 16 ) ;
}
# ifndef MD5_H
# define MD5_H
# include <string>
# include <fstream>
typedef unsigned char byte;
typedef unsigned int uint32;
using std:: string;
using std:: ifstream;
class MD5 {
public :
MD5 ( ) ;
MD5 ( const void * input, size_t length) ;
MD5 ( const string& str) ;
MD5 ( ifstream& in) ;
void update ( const void * input, size_t length) ;
void update ( const string& str) ;
void update ( ifstream& in) ;
byte* digest ( ) ;
string toString ( ) ;
void reset ( ) ;
private :
void update ( const byte* input, size_t length) ;
void final ( ) ;
void transform ( const byte block[ 64 ] ) ;
void encode ( const uint32* input, byte* output, size_t length) ;
void decode ( const byte* input, uint32* output, size_t length) ;
public :
string bytesToHexString ( byte* input, size_t length) ;
MD5 ( const MD5& ) ;
MD5& operator = ( const MD5& ) ;
private :
uint32 _state[ 4 ] ;
uint32 _count[ 2 ] ;
byte _buffer[ 64 ] ;
byte _digest[ 16 ] ;
bool _finished;
static const byte PADDING[ 64 ] ;
static const char HEX[ 16 ] ;
enum {
BUFFER_SIZE = 1024 } ;
} ;
# endif
# include <jni.h>
# include <string>
# include <iostream>
# include <android/log.h>
# include "MD5.h"
# define LOG_TAG "TAG_C"
# define LOGD ( . . . ) __android_log_print ( ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__)
using namespace std;
static const char * PACKAGE_NAME = "com.package.xxx" ;
static const char * APP_SIGNATURE_MD5 = "e0cc175b9c0f1b6a831c399e269772661" ;
const char * APP_SIGNATURE = "f70d01010105000382010f003082010a02820101008c0d4146f43b2d21546818dffa" ;
extern "C"
JNIEXPORT jstring JNICALL
Java_com_packet_1xxx_EncryptionUtil_encryptionMD5 ( JNIEnv * env, jclass clazz, jobject context,
jstring input, jint result_type) {
if ( ! context) {
return env-> NewStringUTF ( "error content" ) ;
}
if ( ! context) {
return env-> NewStringUTF ( "error input string" ) ;
}
jclass j_context_clz = env-> GetObjectClass ( context) ;
jmethodID j_mid = env-> GetMethodID ( j_context_clz, "getPackageName" , "()Ljava/lang/String;" ) ;
jstring j_package_name = static_cast < jstring> ( env-> CallObjectMethod ( context, j_mid) ) ;
const char * c_package_name = env-> GetStringUTFChars ( j_package_name, NULL ) ;
LOGD ( "input package name --> %s " , c_package_name) ;
if ( strcmp ( c_package_name, PACKAGE_NAME) != 0 ) {
return env-> NewStringUTF ( "error_package" ) ;
}
j_mid = env-> GetMethodID ( j_context_clz, "getPackageManager" ,
"()Landroid/content/pm/PackageManager;" ) ;
jobject j_package_manager = env-> CallObjectMethod ( context, j_mid) ;
jclass j_package_manager_clz = env-> GetObjectClass ( j_package_manager) ;
j_mid = env-> GetMethodID ( j_package_manager_clz, "getPackageInfo" ,
"(Ljava/lang/String;I)Landroid/content/pm/PackageInfo;" ) ;
jobject j_package_info = env-> CallObjectMethod ( j_package_manager, j_mid, j_package_name,
0x00000040 ) ;
clazz = env-> GetObjectClass ( j_package_info) ;
jfieldID j_signature_id = env-> GetFieldID ( clazz, "signatures" ,
"[Landroid/content/pm/Signature;" ) ;
jobjectArray signature_array = static_cast < jobjectArray> ( env-> GetObjectField ( j_package_info,
j_signature_id) ) ;
jobject signature_first = env-> GetObjectArrayElement ( signature_array, 0 ) ;
clazz = env-> GetObjectClass ( signature_first) ;
j_mid = env-> GetMethodID ( clazz, "toCharsString" , "()Ljava/lang/String;" ) ;
jstring j_signature = static_cast < jstring> ( env-> CallObjectMethod ( signature_first, j_mid) ) ;
const char * c_signature = env-> GetStringUTFChars ( j_signature, NULL ) ;
if ( strncmp ( c_signature, APP_SIGNATURE, strlen ( APP_SIGNATURE) ) != 0 ) {
return env-> NewStringUTF ( "error_signature" ) ;
}
const char * c_input = env-> GetStringUTFChars ( input, NULL ) ;
char * k = "!1@$%2t5&" ;
std:: string const & cc = std:: string ( c_input) + std:: string ( k) ;
char const * c = cc. c_str ( ) ;
MD5 md5_input;
md5_input. update ( c) ;
string result = md5_input. bytesToHexString ( md5_input. digest ( ) , 16 ) ;
if ( result_type == 1 ) {
transform ( result. begin ( ) , result. end ( ) , result. begin ( ) , :: toupper) ;
}
return env-> NewStringUTF ( result. c_str ( ) ) ;
}
# 指定 cmake 的最小版本
cmake_minimum_required(VERSION 3.4.1)
# 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.
native-lib
# Sets the library as a shared library.
SHARED
# Provides a relative path to your source file(s).
native-lib.cpp
MD5.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.
native-lib
# Links the target library to the log library
# included in the NDK.
${log-lib})
java
package com. package. xxx ;
import android. content. Context ;
public class EncryptionUtil {
static {
System . loadLibrary ( "native-lib" ) ;
}
public static native String encryptionMD5 ( Context context, String input, int resultType) ;
}
package com. package. xxx ;
import android. os. Bundle ;
import android. util. Log ;
import android. view. View ;
import androidx. appcompat. app. AppCompatActivity ;
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate ( Bundle savedInstanceState) {
super . onCreate ( savedInstanceState) ;
setContentView ( R . layout. activity_main) ;
Log . e ( "TAG1" , "-->" + AppUtil . getSign ( this , getPackageName ( ) ) ) ;
Log . e ( "TAG2" , "-->" + AppUtil . getSignMD5 ( this , getPackageName ( ) ) ) ;
Log . e ( "TAG3" , "==>" + EncryptionUtil . encryptionMD5 ( this , "a=zs&bl=18&63644" , 0 ) ) ;
}
public void encryptionMD5 ( View view) {
Log . e ( "TAG4" , "------>" + EncryptionUtil . encryptionMD5 ( this , "abc" , 0 ) ) ;
}
}
package com. package. xxx ;
import android. content. Context ;
import android. content. pm. PackageInfo ;
import android. content. pm. PackageManager ;
import java. security. MessageDigest ;
import java. security. NoSuchAlgorithmException ;
public class AppUtil {
public static String getSign ( Context context, String packageName) {
try {
PackageInfo info = context. getPackageManager ( ) . getPackageInfo ( packageName, PackageManager . GET_SIGNATURES) ;
byte [ ] bytes = info. signatures[ 0 ] . toByteArray ( ) ;
return byte2hex ( bytes) ;
} catch ( Exception e) {
e. printStackTrace ( ) ;
}
return "" ;
}
public static String getSignMD5 ( Context context, String packageName) {
try {
PackageInfo info = context. getPackageManager ( ) . getPackageInfo ( packageName, PackageManager . GET_SIGNATURES) ;
byte [ ] bytes = info. signatures[ 0 ] . toByteArray ( ) ;
return encryptionMD5 ( bytes) ;
} catch ( Exception e) {
e. printStackTrace ( ) ;
}
return "" ;
}
private static String encryptionMD5 ( byte [ ] byteStr) {
MessageDigest messageDigest = null ;
StringBuffer md5StrBuff = new StringBuffer ( ) ;
try {
messageDigest = MessageDigest . getInstance ( "MD5" ) ;
messageDigest. reset ( ) ;
messageDigest. update ( byteStr) ;
byte [ ] byteArray = messageDigest. digest ( ) ;
for ( int i = 0 ; i < byteArray. length; i++ ) {
if ( Integer . toHexString ( 0xFF & byteArray[ i] ) . length ( ) == 1 ) {
md5StrBuff. append ( "0" ) . append ( Integer . toHexString ( 0xFF & byteArray[ i] ) ) ;
} else {
md5StrBuff. append ( Integer . toHexString ( 0xFF & byteArray[ i] ) ) ;
}
}
} catch ( NoSuchAlgorithmException e) {
e. printStackTrace ( ) ;
}
return md5StrBuff. toString ( ) ;
}
private static String byte2hex ( byte [ ] bytes) {
String hs = "" ;
String tmp = "" ;
for ( int i = 0 ; i < bytes. length; i++ ) {
tmp = ( Integer . toHexString ( bytes[ i] & 0XFF ) ) ;
if ( tmp. length ( ) == 1 )
hs = hs + "0" + tmp;
else
hs = hs + tmp;
}
return hs. toUpperCase ( ) ;
}
}
< ? xml version= "1.0" encoding= "utf-8" ? >
< LinearLayout 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"
android: orientation= "vertical"
tools: context= ".MainActivity" >
< Button
android: layout_width= "match_parent"
android: layout_height= "50dp"
android: onClick= "encryptionMD5"
android: text= "MD5加密" / >
< / LinearLayout>
<?xml version="1.0" encoding="utf-8"?>
< manifest xmlns: android= " http://schemas.android.com/apk/res/android"
package = " com.package.xxx" >
< application
android: allowBackup= " true"
android: icon= " @mipmap/ic_launcher"
android: label= " @string/app_name"
android: roundIcon= " @mipmap/ic_launcher_round"
android: supportsRtl= " true"
android: theme= " @style/AppTheme" >
< activity android: name= " .MainActivity" >
< intent-filter>
< action android: name= " android.intent.action.MAIN" />
< category android: name= " android.intent.category.LAUNCHER" />
</ intent-filter>
</ activity>
</ application>
</ manifest>
apply plugin: 'com.android.application'
android {
ndkVersion '23.1.7779620'
compileSdkVersion 29
buildToolsVersion "29.0.2"
defaultConfig {
applicationId "com.package.xxx"
minSdkVersion 15
targetSdkVersion 29
versionCode 1
versionName "1.0"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
externalNativeBuild {
cmake {
cppFlags ""
}
}
}
// 配置打包签名信息
signingConfigs {
release {
keyAlias "test"
keyPassword "123123"
storeFile file("../ndk_md5.jks")//密钥
storePassword "123123"
}
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
// 配置签名信息
signingConfig signingConfigs.release
}
debug {
// 配置签名信息
signingConfig signingConfigs.release
}
}
externalNativeBuild {
cmake {
path "src/main/cpp/CMakeLists.txt"
version "3.10.2"
}
}
}
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation 'androidx.appcompat:appcompat:1.0.2'
implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
testImplementation 'junit:junit:4.12'
androidTestImplementation 'androidx.test:runner:1.1.1'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.1.1'
}
// Top-level build file where you can add configuration options common to all sub-projects/modules.
buildscript {
repositories {
google()
jcenter()
}
dependencies {
classpath 'com.android.tools.build:gradle:4.0.1'
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
}
}
allprojects {
repositories {
google()
jcenter()
}
}
task clean(type: Delete) {
delete rootProject.buildDir
}