diff --git a/doc/4-小米便签维护-需求与设计方案-曹志州组.docx b/doc/4-小米便签维护-需求与设计方案-曹志州组.docx index 5cdac1d..262a2a2 100644 Binary files a/doc/4-小米便签维护-需求与设计方案-曹志州组.docx and b/doc/4-小米便签维护-需求与设计方案-曹志州组.docx differ diff --git a/doc/~$小米便签开源代码阅读-精读报告-曹志州组.docx b/doc/~$小米便签开源代码阅读-精读报告-曹志州组.docx new file mode 100644 index 0000000..e5af429 Binary files /dev/null and b/doc/~$小米便签开源代码阅读-精读报告-曹志州组.docx differ diff --git a/doc/~$小米便签维护-需求与设计方案-曹志州组.docx b/doc/~$小米便签维护-需求与设计方案-曹志州组.docx new file mode 100644 index 0000000..5f79f62 Binary files /dev/null and b/doc/~$小米便签维护-需求与设计方案-曹志州组.docx differ diff --git a/other/05_小米便签阅读与维护-汇报_曹志州组.pptx b/other/05_小米便签阅读与维护-汇报_曹志州组.pptx new file mode 100644 index 0000000..d52077a Binary files /dev/null and b/other/05_小米便签阅读与维护-汇报_曹志州组.pptx differ diff --git a/other/~$05_小米便签阅读与维护-汇报_曹志州组.pptx b/other/~$05_小米便签阅读与维护-汇报_曹志州组.pptx new file mode 100644 index 0000000..9080cf9 Binary files /dev/null and b/other/~$05_小米便签阅读与维护-汇报_曹志州组.pptx differ diff --git a/other/实践总结报告-230140126曹志州.docx b/other/实践总结报告-230140126曹志州.docx new file mode 100644 index 0000000..e17814b Binary files /dev/null and b/other/实践总结报告-230140126曹志州.docx differ diff --git a/other/实践总结报告-230140255-李响.docx b/other/实践总结报告-230140255-李响.docx new file mode 100644 index 0000000..ff4f594 Binary files /dev/null and b/other/实践总结报告-230140255-李响.docx differ diff --git a/other/实践总结报告-230340080-符致宁.docx b/other/实践总结报告-230340080-符致宁.docx new file mode 100644 index 0000000..955e00d Binary files /dev/null and b/other/实践总结报告-230340080-符致宁.docx differ diff --git a/other/实践总结报告-230340083周科.docx b/other/实践总结报告-230340083周科.docx new file mode 100644 index 0000000..8815417 Binary files /dev/null and b/other/实践总结报告-230340083周科.docx differ diff --git a/other/实践总结报告_230340081 徐文俊.docx b/other/实践总结报告_230340081 徐文俊.docx new file mode 100644 index 0000000..080e47b Binary files /dev/null and b/other/实践总结报告_230340081 徐文俊.docx differ diff --git a/other/贡献度-曹志州组.xlsx b/other/贡献度-曹志州组.xlsx new file mode 100644 index 0000000..267ea65 Binary files /dev/null and b/other/贡献度-曹志州组.xlsx differ diff --git a/src/main/AndroidManifest.xml b/src/main/AndroidManifest.xml index e69e18a..4010b4f 100644 --- a/src/main/AndroidManifest.xml +++ b/src/main/AndroidManifest.xml @@ -51,7 +51,7 @@ - + > diff --git a/src/main/java/net/micode/notes/gtask/remote/GTaskASyncTask.java b/src/main/java/net/micode/notes/gtask/remote/GTaskASyncTask.java index 08d20f7..ece11af 100644 --- a/src/main/java/net/micode/notes/gtask/remote/GTaskASyncTask.java +++ b/src/main/java/net/micode/notes/gtask/remote/GTaskASyncTask.java @@ -62,6 +62,25 @@ public class GTaskASyncTask extends AsyncTask { message }); } + private void showNotification(int tickerId, String content) { + PendingIntent pendingIntent; + if (tickerId != R.string.ticker_success) { + pendingIntent = PendingIntent.getActivity(mContext, 0, new Intent(mContext, + NotesPreferenceActivity.class), PendingIntent.FLAG_IMMUTABLE); + } else { + pendingIntent = PendingIntent.getActivity(mContext, 0, new Intent(mContext, + NotesListActivity.class), PendingIntent.FLAG_IMMUTABLE); + } + Notification.Builder builder = new Notification.Builder(mContext) + .setAutoCancel(true) + .setContentTitle(mContext.getString(R.string.app_name)) + .setContentText(content) + .setContentIntent(pendingIntent) + .setWhen(System.currentTimeMillis()) + .setOngoing(true); + Notification notification=builder.getNotification(); + mNotifiManager.notify(GTASK_SYNC_NOTIFICATION_ID, notification); + } // private void showNotification(int tickerId, String content) { // Notification notification = new Notification(R.drawable.notification, mContext @@ -81,25 +100,7 @@ public class GTaskASyncTask extends AsyncTask { // pendingIntent); // mNotifiManager.notify(GTASK_SYNC_NOTIFICATION_ID, notification); // } -private void showNotification(int tickerId, String content) { - PendingIntent pendingIntent; - if (tickerId != R.string.ticker_success) { - pendingIntent = PendingIntent.getActivity(mContext, 0, new Intent(mContext, - NotesPreferenceActivity.class), PendingIntent.FLAG_IMMUTABLE); - } else { - pendingIntent = PendingIntent.getActivity(mContext, 0, new Intent(mContext, - NotesListActivity.class), PendingIntent.FLAG_IMMUTABLE); - } - Notification.Builder builder = new Notification.Builder(mContext) - .setAutoCancel(true) - .setContentTitle(mContext.getString(R.string.app_name)) - .setContentText(content) - .setContentIntent(pendingIntent) - .setWhen(System.currentTimeMillis()) - .setOngoing(true); - Notification notification=builder.getNotification(); - mNotifiManager.notify(GTASK_SYNC_NOTIFICATION_ID, notification); -} + @Override protected Integer doInBackground(Void... unused) { publishProgess(mContext.getString(R.string.sync_progress_login, NotesPreferenceActivity diff --git a/src/main/java/net/micode/notes/model/WorkingNote.java b/src/main/java/net/micode/notes/model/WorkingNote.java index be081e4..938375e 100644 --- a/src/main/java/net/micode/notes/model/WorkingNote.java +++ b/src/main/java/net/micode/notes/model/WorkingNote.java @@ -33,12 +33,13 @@ import net.micode.notes.tool.ResourceParser.NoteBgResources; public class WorkingNote { + // Note for the working note private Note mNote; // Note Id private long mNoteId; // Note content - private String mContent; + public String mContent; // Note mode private int mMode; diff --git a/src/main/java/net/micode/notes/ui/NoteEditActivity.java b/src/main/java/net/micode/notes/ui/NoteEditActivity.java index 96a9ff8..e2e3b05 100644 --- a/src/main/java/net/micode/notes/ui/NoteEditActivity.java +++ b/src/main/java/net/micode/notes/ui/NoteEditActivity.java @@ -22,19 +22,30 @@ import android.app.AlertDialog; import android.app.PendingIntent; import android.app.SearchManager; import android.appwidget.AppWidgetManager; +import android.content.ContentResolver; import android.content.ContentUris; +import android.content.ContentValues; import android.content.Context; import android.content.DialogInterface; import android.content.Intent; import android.content.SharedPreferences; +import android.database.Cursor; +import android.graphics.Bitmap; +import android.graphics.BitmapFactory; import android.graphics.Paint; +import android.net.Uri; +import android.os.Build; import android.os.Bundle; import android.preference.PreferenceManager; +import android.provider.DocumentsContract; +import android.provider.MediaStore; +import android.text.Editable; import android.text.Spannable; import android.text.SpannableString; import android.text.TextUtils; import android.text.format.DateUtils; import android.text.style.BackgroundColorSpan; +import android.text.style.ImageSpan; import android.util.Log; import android.view.LayoutInflater; import android.view.Menu; @@ -47,6 +58,7 @@ import android.widget.CheckBox; import android.widget.CompoundButton; import android.widget.CompoundButton.OnCheckedChangeListener; import android.widget.EditText; +import android.widget.ImageButton; import android.widget.ImageView; import android.widget.LinearLayout; import android.widget.TextView; @@ -65,6 +77,7 @@ import net.micode.notes.ui.NoteEditText.OnTextViewChangeListener; import net.micode.notes.widget.NoteWidgetProvider_2x; import net.micode.notes.widget.NoteWidgetProvider_4x; +import java.io.FileNotFoundException; import java.util.HashMap; import java.util.HashSet; import java.util.Map; @@ -149,7 +162,22 @@ public class NoteEditActivity extends Activity implements OnClickListener, private String mUserQuery; private Pattern mPattern; +// @Override +// protected void onCreate(Bundle savedInstanceState) { +// super.onCreate(savedInstanceState); +// this.setContentView(R.layout.note_edit); +// +// if (savedInstanceState == null && !initActivityState(getIntent())) { +// finish(); +// return; +// } +// initResources(); + + private final int PHOTO_REQUEST=1; + + @Override + protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); this.setContentView(R.layout.note_edit); @@ -159,8 +187,24 @@ public class NoteEditActivity extends Activity implements OnClickListener, return; } initResources(); - } + //根据id获取添加图片按钮 + final ImageButton add_img_btn = (ImageButton) findViewById(R.id.add_img_btn); + //为点击图片按钮设置监听器 + add_img_btn.setOnClickListener(new OnClickListener() { + @Override + public void onClick(View view) { + Log.d(TAG, "onClick: click add image button"); + //ACTION_GET_CONTENT: 允许用户选择特殊种类的数据,并返回(特殊种类的数据:照一张相片或录一段音) + Intent loadImage = new Intent(Intent.ACTION_GET_CONTENT); + //Category属性用于指定当前动作(Action)被执行的环境. + //CATEGORY_OPENABLE; 用来指示一个ACTION_GET_CONTENT的intent + loadImage.addCategory(Intent.CATEGORY_OPENABLE); + loadImage.setType("image/*"); + startActivityForResult(loadImage, PHOTO_REQUEST); + } + }); + } /** * Current activity may be killed when the memory is low. Once it is killed, for another time * user load this activity, we should restore the former state @@ -293,6 +337,7 @@ public class NoteEditActivity extends Activity implements OnClickListener, * is not ready */ showAlertHeader(); + convertToImage(); } private void showAlertHeader() { @@ -779,9 +824,14 @@ public class NoteEditActivity extends Activity implements OnClickListener, mNoteEditor.setText(getHighlightQueryResult(mWorkingNote.getContent(), mUserQuery)); mEditTextList.setVisibility(View.GONE); mNoteEditor.setVisibility(View.VISIBLE); + convertToNormalMode(); } } + private void convertToNormalMode() { + + } + private boolean getWorkingText() { boolean hasChecked = false; if (mWorkingNote.getCheckListMode() == TextNote.MODE_CHECK_LIST) { @@ -870,4 +920,182 @@ public class NoteEditActivity extends Activity implements OnClickListener, private void showToast(int resId, int duration) { Toast.makeText(this, resId, duration).show(); } + //获取文件的real path + public String getPath(final Context context, final Uri uri) { + + final boolean isKitKat = Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT; + + // DocumentProvider + if (isKitKat && DocumentsContract.isDocumentUri(context, uri)) { + // ExternalStorageProvider +// if (isExternalStorageDocument(uri)) { +// final String docId = DocumentsContract.getDocumentId(uri); +// final String[] split = docId.split(":"); +// final String type = split[0]; +// +// if ("primary".equalsIgnoreCase(type)) { +// return Environment.getExternalStorageDirectory() + "/" + split[1]; +// } +// } +// // DownloadsProvider +// else if (isDownloadsDocument(uri)) { +// final String id = DocumentsContract.getDocumentId(uri); +// final Uri contentUri = ContentUris.withAppendedId(Uri.parse("content://downloads/public_downloads"), Long.valueOf(id)); +// return getDataColumn(context, contentUri, null, null); +// } + // MediaProvider +// else + if (isMediaDocument(uri)) { + final String docId = DocumentsContract.getDocumentId(uri); + final String[] split = docId.split(":"); + final String type = split[0]; + + Uri contentUri = null; + if ("image".equals(type)) { + contentUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI; + } + + final String selection = "_id=?"; + final String[] selectionArgs = new String[]{split[1]}; + + return getDataColumn(context, contentUri, selection, selectionArgs); + } + } + // Media + else if ("content".equalsIgnoreCase(uri.getScheme())) { + return getDataColumn(context, uri, null, null); + } + // File + else if ("file".equalsIgnoreCase(uri.getScheme())) { + return uri.getPath(); + } + return null; + } + + + //获取数据列_获取此 Uri 的数据列的值。这对MediaStore Uris 和其他基于文件的 ContentProvider。 + public String getDataColumn(Context context, Uri uri, String selection, String[] selectionArgs) { + + Cursor cursor = null; + final String column = "_data"; + final String[] projection = {column}; + + try { + cursor = context.getContentResolver().query(uri, projection, selection, selectionArgs, null); + if (cursor != null && cursor.moveToFirst()) { + final int column_index = cursor.getColumnIndexOrThrow(column); + return cursor.getString(column_index); + } + } finally { + if (cursor != null) + cursor.close(); + } + return null; + } + + + //是否为外部存储文件 +// public boolean isExternalStorageDocument(Uri uri) { +// return "com.android.externalstorage.documents".equals(uri.getAuthority()); +// } +// +// //是否为下载文件 +// public boolean isDownloadsDocument(Uri uri) { +// return "com.android.providers.downloads.documents".equals(uri.getAuthority()); +// } + + //是否为媒体文件 + public boolean isMediaDocument(Uri uri) { + return "com.android.providers.media.documents".equals(uri.getAuthority()); + } + + protected void onActivityResult(int requestCode, int resultCode, Intent intent) { + super.onActivityResult(requestCode, resultCode, intent); + ContentResolver resolver = getContentResolver(); + switch (requestCode) { + case PHOTO_REQUEST: + Uri originalUri = intent.getData(); //1.获得图片的真实路径 + Bitmap bitmap = null; + try { + bitmap = BitmapFactory.decodeStream(resolver.openInputStream(originalUri));//2.解码图片 + } catch (FileNotFoundException e) { + Log.d(TAG, "onActivityResult: get file_exception"); + e.printStackTrace(); + } + + if (bitmap != null) { + //3.根据Bitmap对象创建ImageSpan对象 + Log.d(TAG, "onActivityResult: bitmap is not null"); + ImageSpan imageSpan = new ImageSpan(NoteEditActivity.this, bitmap); + String path = getPath(this, originalUri); + //4.使用[local][/local]将path括起来,用于之后方便识别图片路径在note中的位置 + String img_fragment = "[local]" + path + "[/local]"; + //创建一个SpannableString对象,以便插入用ImageSpan对象封装的图像 + SpannableString spannableString = new SpannableString(img_fragment); + spannableString.setSpan(imageSpan, 0, img_fragment.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); + //5.将选择的图片追加到EditText中光标所在位置 + NoteEditText e = (NoteEditText) findViewById(R.id.note_edit_view); + int index = e.getSelectionStart(); //获取光标所在位置 + Log.d(TAG, "Index是: " + index); + Editable edit_text = e.getEditableText(); + ((android.text.Editable) edit_text).insert(index, spannableString); //将图片插入到光标所在位置 + + mWorkingNote.mContent = e.getText().toString(); + //6.把改动提交到数据库中,两个数据库表都要改的 + ContentResolver contentResolver = getContentResolver(); + ContentValues contentValues = new ContentValues(); + final long id = mWorkingNote.getNoteId(); + contentValues.put("snippet", mWorkingNote.mContent); + contentResolver.update(Uri.parse("content://micode_notes/note"), contentValues, "_id=?", new String[]{"" + id}); + ContentValues contentValues1 = new ContentValues(); + contentValues1.put("content", mWorkingNote.mContent); + contentResolver.update(Uri.parse("content://micode_notes/data"), contentValues1, "mime_type=? and note_id=?", new String[]{"vnd.android.cursor.item/text_note", "" + id}); + + } else { + Toast.makeText(NoteEditActivity.this, "获取图片失败", Toast.LENGTH_SHORT).show(); + } + break; + default: + break; + } + } + private void convertToImage() { + NoteEditText noteEditText = (NoteEditText) findViewById(R.id.note_edit_view); //获取当前的edit + Editable editable = noteEditText.getText();//1.获取text + String noteText = editable.toString(); //2.将note内容转换为字符串 + int length = editable.length(); //内容的长度 + //3.截取img片段 [local]+uri+[local],提取uri + for(int i = 0; i < length; i++) { + for(int j = i; j < length; j++) { + String img_fragment = noteText.substring(i, j+1); //img_fragment:关于图片路径的片段 + if(img_fragment.length() > 15 && img_fragment.endsWith("[/local]") && img_fragment.startsWith("[local]")){ + int limit = 7; //[local]为7个字符 + //[local][/local]共15个字符,剩下的为真正的path长度 + int len = img_fragment.length()-15; + //从[local]之后的len个字符就是path + String path = img_fragment.substring(limit,limit+len);//获取到了图片路径 + Bitmap bitmap = null; + Log.d(TAG, "图片的路径是:"+path); + try { + bitmap = BitmapFactory.decodeFile(path);//将图片路径解码为图片格式 + } catch (Exception e) { + e.printStackTrace(); + } + if(bitmap!=null){ //若图片存在 + Log.d(TAG, "图片不为null"); + ImageSpan imageSpan = new ImageSpan(NoteEditActivity.this, bitmap); + //4.创建一个SpannableString对象,以便插入用ImageSpan对象封装的图像 + String ss = "[local]" + path + "[/local]"; + SpannableString spannableString = new SpannableString(ss); + //5.将指定的标记对象附加到文本的开始...结束范围 + spannableString.setSpan(imageSpan, 0, ss.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); + Log.d(TAG, "Create spannable string success!"); + Editable edit_text = noteEditText.getEditableText(); + edit_text.delete(i,i+len+15); //6.删掉图片路径的文字 + edit_text.insert(i, spannableString); //7.在路径的起始位置插入图片 + } + } + } + } + } } diff --git a/src/main/java/net/micode/notes/ui/NotesListActivity.java b/src/main/java/net/micode/notes/ui/NotesListActivity.java index e843aec..5ccb862 100644 --- a/src/main/java/net/micode/notes/ui/NotesListActivity.java +++ b/src/main/java/net/micode/notes/ui/NotesListActivity.java @@ -79,8 +79,9 @@ import java.io.InputStreamReader; import java.util.HashSet; public class NotesListActivity extends Activity implements OnClickListener, OnItemLongClickListener { - private static final int FOLDER_NOTE_LIST_QUERY_TOKEN = 0; + private static final int FOLDER_NOTE_LIST_QUERY_TOKEN = 0; + private int mode=-1; private static final int FOLDER_LIST_QUERY_TOKEN = 1; private static final int MENU_FOLDER_DELETE = 0; @@ -139,6 +140,11 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.note_list); + + getWindow().setBackgroundDrawableResource(R.drawable.fly); + getWindow().setBackgroundDrawableResource(R.drawable.pink); + getWindow().setBackgroundDrawableResource(R.drawable.falus); + initResources(); /** @@ -733,6 +739,7 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt return false; } switch (item.getItemId()) { + case MENU_FOLDER_VIEW: openFolder(mFocusNoteDataItem); break; @@ -775,12 +782,34 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt } else { Log.e(TAG, "Wrong state:" + mState); } + if(mode==-1) { + menu.findItem(R.id.menu_fly).setVisible(false); + }else if(mode==0) { + menu.findItem(R.id.menu_falus).setVisible(false); + }else if(mode==1){ + menu.findItem(R.id.menu_pink).setVisible(false); + } return true; } @Override public boolean onOptionsItemSelected(MenuItem item) { switch (item.getItemId()) { + case R.id.menu_fly: { + mode=-1; + getWindow().setBackgroundDrawableResource(R.drawable.fly); + break; + } + case R.id.menu_falus: { + mode=0; + getWindow().setBackgroundDrawableResource(R.drawable.falus); + break; + } + case R.id.menu_pink: { + mode=-1; + getWindow().setBackgroundDrawableResource(R.drawable.pink); + break; + } case R.id.menu_new_folder: { showCreateOrModifyFolderDialog(true); break; diff --git a/src/main/res/drawable-hdpi/falus.png b/src/main/res/drawable-hdpi/falus.png new file mode 100644 index 0000000..fdde90d Binary files /dev/null and b/src/main/res/drawable-hdpi/falus.png differ diff --git a/src/main/res/drawable-hdpi/fly.png b/src/main/res/drawable-hdpi/fly.png new file mode 100644 index 0000000..5731e71 Binary files /dev/null and b/src/main/res/drawable-hdpi/fly.png differ diff --git a/src/main/res/drawable-hdpi/grassland.png b/src/main/res/drawable-hdpi/grassland.png new file mode 100644 index 0000000..fd7d196 Binary files /dev/null and b/src/main/res/drawable-hdpi/grassland.png differ diff --git a/src/main/res/drawable-hdpi/magma.png b/src/main/res/drawable-hdpi/magma.png new file mode 100644 index 0000000..37dd3ba Binary files /dev/null and b/src/main/res/drawable-hdpi/magma.png differ diff --git a/src/main/res/drawable-hdpi/pink.png b/src/main/res/drawable-hdpi/pink.png new file mode 100644 index 0000000..fdde90d Binary files /dev/null and b/src/main/res/drawable-hdpi/pink.png differ diff --git a/src/main/res/drawable-hdpi/water.png b/src/main/res/drawable-hdpi/water.png new file mode 100644 index 0000000..6265f01 Binary files /dev/null and b/src/main/res/drawable-hdpi/water.png differ diff --git a/src/main/res/drawable/menu_aijiang.png b/src/main/res/drawable/menu_aijiang.png new file mode 100644 index 0000000..fdde90d Binary files /dev/null and b/src/main/res/drawable/menu_aijiang.png differ diff --git a/src/main/res/drawable/menu_zhanshen.png b/src/main/res/drawable/menu_zhanshen.png new file mode 100644 index 0000000..fdde90d Binary files /dev/null and b/src/main/res/drawable/menu_zhanshen.png differ diff --git a/src/main/res/drawable/menu_zhousang.png b/src/main/res/drawable/menu_zhousang.png new file mode 100644 index 0000000..5731e71 Binary files /dev/null and b/src/main/res/drawable/menu_zhousang.png differ diff --git a/src/main/res/layout/note_edit.xml b/src/main/res/layout/note_edit.xml index 10b2aa7..1a7d78c 100644 --- a/src/main/res/layout/note_edit.xml +++ b/src/main/res/layout/note_edit.xml @@ -115,7 +115,14 @@ android:background="@drawable/bg_color_btn_mask" /> - + + android:layout_height="fill_parent"> + + + + + + diff --git a/src/main/res/mipmap-anydpi/ic_launcher.xml b/src/main/res/mipmap-anydpi/ic_launcher.xml new file mode 100644 index 0000000..6f3b755 --- /dev/null +++ b/src/main/res/mipmap-anydpi/ic_launcher.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/src/main/res/mipmap-anydpi/ic_launcher_round.xml b/src/main/res/mipmap-anydpi/ic_launcher_round.xml new file mode 100644 index 0000000..6f3b755 --- /dev/null +++ b/src/main/res/mipmap-anydpi/ic_launcher_round.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/src/main/res/values/strings.xml b/src/main/res/values/strings.xml index 55df868..7c7e5bf 100644 --- a/src/main/res/values/strings.xml +++ b/src/main/res/values/strings.xml @@ -17,6 +17,9 @@ + Background: fly + Background: falus + Background: pink Notes Notes 2x2 Notes 4x4 diff --git a/src/main/res/xml/backup_rules.xml b/src/main/res/xml/backup_rules.xml index fa0f996..4df9255 100644 --- a/src/main/res/xml/backup_rules.xml +++ b/src/main/res/xml/backup_rules.xml @@ -2,7 +2,7 @@ Sample backup rules file; uncomment and customize as necessary. See https://developer.android.com/guide/topics/data/autobackup for details. - Note: This file is ignored for devices older that API 31 + Note: This file is ignored for devices older than API 31 See https://developer.android.com/about/versions/12/backup-restore -->