diff --git a/AndroidStudioProjects/.gitignore b/AndroidStudioProjects/.gitignore new file mode 100644 index 0000000000000000000000000000000000000000..0bfb13f474940f7034dcbc0894f0ea6898928946 --- /dev/null +++ b/AndroidStudioProjects/.gitignore @@ -0,0 +1,21 @@ +*.iml +.gradle +/local.properties +/.idea +.idea/caches/build_file_checksums.ser +/.idea/libraries +/.idea/modules.xml +/.idea/workspace.xml +.idea +/.idea +/.idea/vcs.xml +/.idea/misc.xml +.DS_Store +/build +/captures +.externalNativeBuild +/README.md +/apk +*.apk +*.zip +.idea/ \ No newline at end of file diff --git a/AndroidStudioProjects/app/.gitignore b/AndroidStudioProjects/app/.gitignore new file mode 100644 index 0000000000000000000000000000000000000000..42afabfd2abebf31384ca7797186a27a4b7dbee8 --- /dev/null +++ b/AndroidStudioProjects/app/.gitignore @@ -0,0 +1 @@ +/build \ No newline at end of file diff --git a/AndroidStudioProjects/app/build.gradle b/AndroidStudioProjects/app/build.gradle new file mode 100644 index 0000000000000000000000000000000000000000..2e1c5b1f82d9929464c2519e97fbabef5f76c30f --- /dev/null +++ b/AndroidStudioProjects/app/build.gradle @@ -0,0 +1,38 @@ +plugins { + id 'com.android.application' +} + +android { + compileSdk 31 + + defaultConfig { + applicationId "com.example.myapplication" + minSdk 21 + targetSdk 31 + versionCode 1 + versionName "1.0" + + testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" + } + + buildTypes { + release { + minifyEnabled false + proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' + } + } + compileOptions { + sourceCompatibility JavaVersion.VERSION_1_8 + targetCompatibility JavaVersion.VERSION_1_8 + } +} + +dependencies { + + implementation 'androidx.appcompat:appcompat:1.2.0' + implementation 'com.google.android.material:material:1.3.0' + implementation 'androidx.constraintlayout:constraintlayout:2.0.4' + testImplementation 'junit:junit:4.+' + androidTestImplementation 'androidx.test.ext:junit:1.1.2' + androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0' +} \ No newline at end of file diff --git a/AndroidStudioProjects/app/proguard-rules.pro b/AndroidStudioProjects/app/proguard-rules.pro new file mode 100644 index 0000000000000000000000000000000000000000..481bb434814107eb79d7a30b676d344b0df2f8ce --- /dev/null +++ b/AndroidStudioProjects/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/AndroidStudioProjects/app/release/app-release.apk b/AndroidStudioProjects/app/release/app-release.apk new file mode 100644 index 0000000000000000000000000000000000000000..e619d317c9d761b2c02dc8c5fa2f84a3bd5f579e Binary files /dev/null and b/AndroidStudioProjects/app/release/app-release.apk differ diff --git a/AndroidStudioProjects/app/release/output-metadata.json b/AndroidStudioProjects/app/release/output-metadata.json new file mode 100644 index 0000000000000000000000000000000000000000..0eb5fde04801f55a01514421f0b76a639253cdbc --- /dev/null +++ b/AndroidStudioProjects/app/release/output-metadata.json @@ -0,0 +1,20 @@ +{ + "version": 3, + "artifactType": { + "type": "APK", + "kind": "Directory" + }, + "applicationId": "com.example.myapplication", + "variantName": "release", + "elements": [ + { + "type": "SINGLE", + "filters": [], + "attributes": [], + "versionCode": 1, + "versionName": "1.0", + "outputFile": "app-release.apk" + } + ], + "elementType": "File" +} \ No newline at end of file diff --git a/AndroidStudioProjects/app/src/androidTest/java/com/example/myapplication/ExampleInstrumentedTest.java b/AndroidStudioProjects/app/src/androidTest/java/com/example/myapplication/ExampleInstrumentedTest.java new file mode 100644 index 0000000000000000000000000000000000000000..982ba517a4b447bebc00ed3d1e784228226aef3b --- /dev/null +++ b/AndroidStudioProjects/app/src/androidTest/java/com/example/myapplication/ExampleInstrumentedTest.java @@ -0,0 +1,26 @@ +package com.example.myapplication; + +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.example.myapplication", appContext.getPackageName()); + } +} \ No newline at end of file diff --git a/AndroidStudioProjects/app/src/main/AndroidManifest.xml b/AndroidStudioProjects/app/src/main/AndroidManifest.xml new file mode 100644 index 0000000000000000000000000000000000000000..0a2ce869d17e93356f9909bc6c7564739eefe198 --- /dev/null +++ b/AndroidStudioProjects/app/src/main/AndroidManifest.xml @@ -0,0 +1,24 @@ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/AndroidStudioProjects/app/src/main/java/com/example/myapplication/ClearEditText.java b/AndroidStudioProjects/app/src/main/java/com/example/myapplication/ClearEditText.java new file mode 100644 index 0000000000000000000000000000000000000000..6561a7d70429d7d62c10c6a039aafbaec6e2fa7e --- /dev/null +++ b/AndroidStudioProjects/app/src/main/java/com/example/myapplication/ClearEditText.java @@ -0,0 +1,296 @@ +//package com.example.myapplication; +// +// +//import android.animation.ObjectAnimator; +//import android.content.Context; +//import android.content.res.TypedArray; +//import android.graphics.Color; +//import android.text.Editable; +//import android.text.InputFilter; +//import android.text.InputType; +//import android.text.TextWatcher; +//import android.text.method.HideReturnsTransformationMethod; +//import android.text.method.PasswordTransformationMethod; +//import android.util.AttributeSet; +//import android.util.TypedValue; +//import android.view.View; +//import android.view.animation.AnimationUtils; +//import android.widget.EditText; +//import android.widget.ImageView; +//import android.widget.RelativeLayout; +// +//import androidx.viewpager.widget.ViewPager; +// +//public class ClearEditText extends RelativeLayout { +// private ImageView ivLeftIcon; +// private EditText myEdie; +// private ImageView ivEditClean; +// private ImageView ivEditEye; +// private boolean isChecked = true; +// private Context mContext; +// private TypedArray mTypedArray; +// private boolean showClean = true;//清空图标是否显示,true:显示 +// private boolean showEye = false;//密码可见图标是否显示,true:显示 +// private int drawableLeft = -1;//是否显示输入框左侧图片 +// private int drawableEyeOpen = R.drawable.clear_icon_eye_open;//可以看见密码小眼睛样式 +// private int drawableEyeClose = R.drawable.clear_icon_eye_close;//不可见密码小眼睛样式 +// private int drawableClean = R.drawable.clear_icon_close;//清除按钮图片 +// private int cleanPadding = 0;//清除按钮padding边距 +// private String hintStr; +// private String textStr; +// private int mTextColorHint = Color.BLACK; +// private int mTextColor = Color.BLACK; +// private int mTextSize = -1; +// private int mMaxLength = 2000; +// private int mMaxLines = 1; +// private int mInputType = 0;//输入类型,就做了不限制、数字、文本密码三种 +// private boolean isInput = false;//输入1个字符后更改状态为true,保证小眼睛移动一次 +// private boolean isHideClean = false;//输入字符后,清除了需要小眼睛归为,清除按钮隐藏 +// private int ivWidth = 45;//关闭按钮的宽度 +// +// public ClearEditText(Context context) { +// super(context); +// mContext = context; +// initView(); +// } +// +// public ClearEditText(Context context, AttributeSet attrs, int defStyle) { +// super(context, attrs, defStyle); +// mContext = context; +// mTypedArray = mContext.obtainStyledAttributes(attrs, R.styleable.myEditText); +// initView(); +// } +// +// public ClearEditText(Context context, AttributeSet attrs) { +// super(context, attrs); +// mContext = context; +// mTypedArray = mContext.obtainStyledAttributes(attrs, R.styleable.myEditText); +// showClean = mTypedArray.getBoolean(R.styleable.myEditText_showClean, showClean); +// drawableClean = mTypedArray.getResourceId(R.styleable.myEditText_drawableClean, drawableClean); +// cleanPadding = mTypedArray.getDimensionPixelSize(R.styleable.myEditText_cleanPadding, cleanPadding); +// +// showEye = mTypedArray.getBoolean(R.styleable.myEditText_showEye, showEye); +// drawableLeft = mTypedArray.getResourceId(R.styleable.myEditText_drawableLeft, -1); +// drawableEyeClose = mTypedArray.getResourceId(R.styleable.myEditText_drawableEyeClose, drawableEyeClose); +// drawableEyeOpen = mTypedArray.getResourceId(R.styleable.myEditText_drawableEyeOpen, drawableEyeOpen); +// +// hintStr = mTypedArray.getString(R.styleable.myEditText_hint); +// textStr = mTypedArray.getString(R.styleable.myEditText_text); +// mTextColorHint = mTypedArray.getColor(R.styleable.myEditText_textColorHint, mTextColorHint); +// mTextColor = mTypedArray.getColor(R.styleable.myEditText_textColor, mTextColor); +// mTextSize = mTypedArray.getDimensionPixelSize(R.styleable.myEditText_textSize, mTextSize); +// mMaxLength = mTypedArray.getInteger(R.styleable.myEditText_maxLength, mMaxLength); +// mMaxLines = mTypedArray.getDimensionPixelSize(R.styleable.myEditText_maxLines, mMaxLines); +// mInputType = mTypedArray.getInteger(R.styleable.myEditText_inputType, mInputType); +// +// mTypedArray.recycle(); +// initView(); +// } +// +// // 初始化视图 +// private void initView() { +// View view = View.inflate(getContext(), R.layout.clear_layout_view, null); +// ivLeftIcon = (ImageView) view.findViewById(R.id.iv_edit_left_icon); +// myEdie = (EditText) view.findViewById(R.id.view_edit_show); +// ivEditClean = (ImageView) view.findViewById(R.id.iv_edit_clean); +// ivEditEye = (ImageView) view.findViewById(R.id.iv_edit_eye); +// +// myEdie.setHint(hintStr); +// myEdie.setHintTextColor(mTextColorHint); +// myEdie.setText(textStr); +// myEdie.setTextColor(mTextColor); +// myEdie.setMaxLines(mMaxLines); +// myEdie.setFilters(new InputFilter[]{new InputFilter.LengthFilter(mMaxLength)}); +// if (mTextSize != -1) { +// myEdie.setTextSize(TypedValue.COMPLEX_UNIT_PX, mTextSize); +// } else { +// myEdie.setTextSize(15); +// } +// if (mInputType == 1) { +// myEdie.setInputType(InputType.TYPE_CLASS_NUMBER); +// } else if (mInputType == 2) { +// myEdie.setInputType(InputType.TYPE_TEXT_VARIATION_PASSWORD | InputType.TYPE_CLASS_TEXT); +// } else { +// myEdie.setInputType(InputType.TYPE_NUMBER_VARIATION_NORMAL | InputType.TYPE_CLASS_TEXT); +// } +// if (showEye) { +// myEdie.setTransformationMethod(new AsteriskPasswordTransformationMethod()); +// } +// if (showClean && showEye) { +// int left = myEdie.getPaddingLeft(); +// int top = myEdie.getPaddingTop(); +// int bottom = myEdie.getPaddingBottom(); +// myEdie.setPadding(left, top, Utils.dp2px(mContext, 90), bottom); +// } else if (!showClean && !showEye) { +// int left = myEdie.getPaddingLeft(); +// int top = myEdie.getPaddingTop(); +// int right = myEdie.getPaddingRight(); +// int bottom = myEdie.getPaddingBottom(); +// myEdie.setPadding(left, top, right, bottom); +// } else { +// int left = myEdie.getPaddingLeft(); +// int top = myEdie.getPaddingTop(); +// int bottom = myEdie.getPaddingBottom(); +// myEdie.setPadding(left, top, Utils.dp2px(mContext, 45), bottom); +// } +// +// myEdie.addTextChangedListener(new TextWatcher() { +// @Override +// public void beforeTextChanged(CharSequence s, int start, int count, int after) { +// +// } +// +// @Override +// public void onTextChanged(CharSequence s, int start, int before, int count) { +// if (s.length() > 0) { +// isHideClean = false; +// } +// } +// +// @Override +// public void afterTextChanged(Editable s) { +// if (s.length() > 0 && !isInput) {//输入字符大于0且只有一个字符时候显示清除按钮动画,小眼睛移动出位置给清除按钮使用 +// showEditClean(); +// moveEditEye(); +// isInput = true; +// } else if (s.length() == 0) {//无字符小眼睛归位 +// UndoEditEye(); +// } +// if (s.length() == 0 & !isHideClean) { +// hideEditClean(); +// isHideClean = true; +// isInput = false; +// } +// if (onEditInputListener != null) { +// onEditInputListener.input(getText()); +// } +// } +// }); +// +// setEditClean(showClean); +// ivEditClean.setOnClickListener(new OnClickListener() { +// @Override +// public void onClick(View v) { +// myEdie.setText(""); +// } +// }); +// ivEditClean.setImageResource(drawableClean); +// ivEditClean.setPadding(cleanPadding, cleanPadding, cleanPadding, cleanPadding); +// +// setEditEye(showEye); +// ivEditEye.setOnClickListener(new OnClickListener() { +// @Override +// public void onClick(View v) { +// if (isChecked) { +// // 输入一个对用户可见的密码 +// myEdie.setTransformationMethod(HideReturnsTransformationMethod.getInstance()); +// myEdie.setSelection(getText().length()); +// ivEditEye.setImageResource(drawableEyeOpen); +// isChecked = false; +// } else { +// // 输入一个对用户不可见的密码 +// myEdie.setTransformationMethod(new AsteriskPasswordTransformationMethod()); +// myEdie.setSelection(getText().length()); +// ivEditEye.setImageResource(drawableEyeClose); +// isChecked = true; +// } +// } +// }); +// if (drawableLeft != -1) { +// ivLeftIcon.setVisibility(View.VISIBLE); +// ivLeftIcon.setImageResource(drawableLeft); +// } else { +// ivLeftIcon.setVisibility(View.GONE); +// } +// view.setLayoutParams(new LayoutParams(ViewPager.LayoutParams.MATCH_PARENT, ViewPager.LayoutParams.WRAP_CONTENT)); +// addView(view); +// } +// +// //密码不可见时候,使用*替换密码 +// public class AsteriskPasswordTransformationMethod extends PasswordTransformationMethod { +// @Override +// public CharSequence getTransformation(CharSequence source, View view) { +// return new PasswordCharSequence(source); +// } +// +// private class PasswordCharSequence implements CharSequence { +// +// private CharSequence mSource; +// +// public PasswordCharSequence(CharSequence source) { +// mSource = source; // Store char sequence +// } +// +// public char charAt(int index) { +// return '*'; // This is the important part +// } +// +// public int length() { +// return mSource.length(); // Return default +// } +// +// public CharSequence subSequence(int start, int end) { +// return mSource.subSequence(start, end); // Return default +// } +// } +// +// } +// +// public String getText() { +// return myEdie.getText().toString().trim(); +// } +// +// public void setText(String text) { +// myEdie.setText(text); +// } +// +// //代码设置是否显示清除按钮 +// public void setEditClean(boolean isCanClose) { +// showClean = isCanClose; +// } +// +// //代码设置是否显示小眼睛 +// public void setEditEye(boolean isCanSee) { +// showEye = isCanSee; +// if (showEye == true) { +// ivEditEye.setVisibility(View.VISIBLE); +// } else { +// ivEditEye.setVisibility(View.GONE); +// } +// } +// +// private void showEditClean() { +// if (showClean == true) { +// AnimationUtils.showAndHiddenCenterAnimation(ivEditClean, AnimationUtils.AnimationState.STATE_SHOW, 500); +// } +// } +// +// private void hideEditClean() { +// if (showClean == true) { +// AnimationUtils.showAndHiddenCenterAnimation(ivEditClean, AnimationUtils.AnimationState.STATE_HIDDEN, 500); +// } +// } +// +// private void moveEditEye() { +// if (showEye) { +// ObjectAnimator.ofFloat(ivEditEye, "translationX", -Utils.dp2px(mContext, ivWidth)).setDuration(500).start(); +// } +// } +// +// private void UndoEditEye() { +// if (showEye) { +// ObjectAnimator.ofFloat(ivEditEye, "translationX", 0).setDuration(500).start(); +// } +// } +// +// public OnEditInputListener onEditInputListener; +// +// public void setOnEditInputListener(OnEditInputListener listener) { +// onEditInputListener = listener; +// } +// //输入监听 +// public interface OnEditInputListener { +// void input(String content); +// } +// +//} diff --git a/AndroidStudioProjects/app/src/main/java/com/example/myapplication/GlobalValue.java b/AndroidStudioProjects/app/src/main/java/com/example/myapplication/GlobalValue.java new file mode 100644 index 0000000000000000000000000000000000000000..c47ce0accb255d2d2c9f5c4617070281fe508643 --- /dev/null +++ b/AndroidStudioProjects/app/src/main/java/com/example/myapplication/GlobalValue.java @@ -0,0 +1,12 @@ +package com.example.myapplication; + +public class GlobalValue { + private boolean isCheck; + public boolean isCheck() { + return isCheck; + } + public void setCheck(boolean check) { + isCheck = check; + } + +} diff --git a/AndroidStudioProjects/app/src/main/java/com/example/myapplication/MainActivity.java b/AndroidStudioProjects/app/src/main/java/com/example/myapplication/MainActivity.java new file mode 100644 index 0000000000000000000000000000000000000000..9ecef70c492548963b5b456d1371bd0ff6c4a867 --- /dev/null +++ b/AndroidStudioProjects/app/src/main/java/com/example/myapplication/MainActivity.java @@ -0,0 +1,78 @@ +package com.example.myapplication; + +import androidx.appcompat.app.AppCompatActivity; + +import android.graphics.drawable.Drawable; +import android.os.Bundle; +import android.provider.Settings; +import android.view.View; +import android.view.inputmethod.EditorInfo; +import android.widget.EditText; +import android.widget.RadioButton; +import android.widget.RadioGroup; + +public class MainActivity extends AppCompatActivity { + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_main); + EditText useName=(EditText) findViewById(R.id.input_id); + EditText password=(EditText) findViewById(R.id.input_password); + Drawable drawable1=getResources().getDrawable(R.mipmap.user_icno); + Drawable drawable2=getResources().getDrawable(R.mipmap.lock_icon); + drawable1.setBounds(0,0,75,75); + drawable2.setBounds(0,0,75,75); + useName.setCompoundDrawables(drawable1,null,null,null); + password.setCompoundDrawables(drawable2,null,null,null); + + + + + final RadioButton rb_bug = (RadioButton) findViewById(R.id.remeber_button); + final GlobalValue globalValue=new GlobalValue(); + + rb_bug.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + boolean isCheck=globalValue.isCheck(); + if (isCheck){ + if(v==rb_bug) + rb_bug.setChecked(false); + }else { + if(v==rb_bug) + rb_bug.setChecked(true); + } + globalValue.setCheck(!isCheck); + } + + }); +// final int InputType_PSW_Hide = EditorInfo.TYPE_CLASS_TEXT | EditorInfo.TYPE_TEXT_VARIATION_PASSWORD;// 129 +// final int InputType_PSW_Visible = EditorInfo.TYPE_CLASS_TEXT | EditorInfo.TYPE_TEXT_VARIATION_VISIBLE_PASSWORD;//145 +// +// //设置密码可见 +// public void setPasswordVisibility (EditText editText, boolean setVisible) +// { +// if (null != editText) +// { +// int selectionEnd = editText.getSelectionEnd(); +// int length = editText.getText().toString().length(); +// +// int inputType = editText.getInputType(); +// if (inputType == InputType_PSW_Hide || inputType == InputType_PSW_Visible) +// { +// editText.setInputType(setVisible ? InputType_PSW_Visible : InputType_PSW_Hide); +// } else +// { +// editText.setTransformationMethod(setVisible ? HideReturnsTransformationMethod.getInstance() : PasswordTransformationMethod.getInstance()); +// } +// +// editText.setSelection((Math.min(selectionEnd, length))); +// //执行上面的代码后光标会处于输入框的最前方-->重置光标位置 +// } +// } + + + + + } +} \ No newline at end of file diff --git a/AndroidStudioProjects/app/src/main/res/drawable/draw1.xml b/AndroidStudioProjects/app/src/main/res/drawable/draw1.xml new file mode 100644 index 0000000000000000000000000000000000000000..927afc3c416bb9de0128c53d99e99173f844b02d --- /dev/null +++ b/AndroidStudioProjects/app/src/main/res/drawable/draw1.xml @@ -0,0 +1,23 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/AndroidStudioProjects/app/src/main/res/drawable/ic_launcher_background.xml b/AndroidStudioProjects/app/src/main/res/drawable/ic_launcher_background.xml new file mode 100644 index 0000000000000000000000000000000000000000..07d5da9cbf141911847041df5d7b87f0dd5ef9d4 --- /dev/null +++ b/AndroidStudioProjects/app/src/main/res/drawable/ic_launcher_background.xml @@ -0,0 +1,170 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/AndroidStudioProjects/app/src/main/res/drawable/ic_launcher_foreground.xml b/AndroidStudioProjects/app/src/main/res/drawable/ic_launcher_foreground.xml new file mode 100644 index 0000000000000000000000000000000000000000..a8b409b1d1a67ab9d6be93a1920501644837a571 --- /dev/null +++ b/AndroidStudioProjects/app/src/main/res/drawable/ic_launcher_foreground.xml @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/AndroidStudioProjects/app/src/main/res/drawable/img.xml b/AndroidStudioProjects/app/src/main/res/drawable/img.xml new file mode 100644 index 0000000000000000000000000000000000000000..ed0ec22dc958465fd413c28fd617cdaeaa53369b --- /dev/null +++ b/AndroidStudioProjects/app/src/main/res/drawable/img.xml @@ -0,0 +1,16 @@ + + + + + + + + + \ No newline at end of file diff --git a/AndroidStudioProjects/app/src/main/res/drawable/login.xml b/AndroidStudioProjects/app/src/main/res/drawable/login.xml new file mode 100644 index 0000000000000000000000000000000000000000..49bdb3cad3516db8c7f51059f5726418f9718d8e --- /dev/null +++ b/AndroidStudioProjects/app/src/main/res/drawable/login.xml @@ -0,0 +1,17 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/AndroidStudioProjects/app/src/main/res/layout/activity_main.xml b/AndroidStudioProjects/app/src/main/res/layout/activity_main.xml new file mode 100644 index 0000000000000000000000000000000000000000..ae8d888662f35d83340576e288a96ddf153f2d1b --- /dev/null +++ b/AndroidStudioProjects/app/src/main/res/layout/activity_main.xml @@ -0,0 +1,121 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + +