feat:完成文件上传SDK集成(腾讯Cos),移除七牛云文件存储SDK
feat:内置的图片选择框架(Matisse),增加返回图片宽高信息,以及Android33权限适配 fix:修复部分场景Android33权限适配问题
This commit is contained in:
@@ -20,7 +20,6 @@ import com.bumptech.glide.request.target.ViewTarget;
|
|||||||
import com.bytedance.hume.readapk.HumeSDK;
|
import com.bytedance.hume.readapk.HumeSDK;
|
||||||
import com.chuhai.utils.MetaDataUtils;
|
import com.chuhai.utils.MetaDataUtils;
|
||||||
import com.coorchice.library.utils.LogUtils;
|
import com.coorchice.library.utils.LogUtils;
|
||||||
import com.facebook.stetho.Stetho;
|
|
||||||
import com.hjq.toast.ToastUtils;
|
import com.hjq.toast.ToastUtils;
|
||||||
import com.mob.MobSDK;
|
import com.mob.MobSDK;
|
||||||
import com.mob.moblink.MobLink;
|
import com.mob.moblink.MobLink;
|
||||||
@@ -256,7 +255,6 @@ public class XChatApplication extends BaseApp {
|
|||||||
ViewTarget.setTagId(R.id.tag_glide);
|
ViewTarget.setTagId(R.id.tag_glide);
|
||||||
|
|
||||||
init(channel);
|
init(channel);
|
||||||
initStetho(context);
|
|
||||||
|
|
||||||
//生命周期监听
|
//生命周期监听
|
||||||
if (lifeManager == null) {
|
if (lifeManager == null) {
|
||||||
@@ -551,18 +549,4 @@ public class XChatApplication extends BaseApp {
|
|||||||
}
|
}
|
||||||
GlobalHandleManager.get().unInit();
|
GlobalHandleManager.get().unInit();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* 初始化Stetho(网络调试)
|
|
||||||
*/
|
|
||||||
private static void initStetho(Context context) {
|
|
||||||
if (Env.isDebug()) {
|
|
||||||
Stetho.initialize(
|
|
||||||
Stetho.newInitializerBuilder(context)
|
|
||||||
.enableDumpapp(Stetho.defaultDumperPluginsProvider(context))
|
|
||||||
.enableWebKitInspector(Stetho.defaultInspectorModulesProvider(context))
|
|
||||||
.build()
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@@ -4,6 +4,7 @@ import android.Manifest
|
|||||||
import android.annotation.SuppressLint
|
import android.annotation.SuppressLint
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
|
import android.os.Build
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import com.netease.nim.uikit.StatusBarUtil
|
import com.netease.nim.uikit.StatusBarUtil
|
||||||
import com.nnbc123.app.R
|
import com.nnbc123.app.R
|
||||||
@@ -87,8 +88,7 @@ class AddSkillActivity : BaseBindingActivity<ActivitySkillEditBinding>() {
|
|||||||
.append("\n2.").append(ResUtil.getString(R.string.permission_denied_tips_image))
|
.append("\n2.").append(ResUtil.getString(R.string.permission_denied_tips_image))
|
||||||
.toString()
|
.toString()
|
||||||
val params = arrayOf(
|
val params = arrayOf(
|
||||||
Manifest.permission.RECORD_AUDIO,
|
Manifest.permission.RECORD_AUDIO
|
||||||
Manifest.permission.WRITE_EXTERNAL_STORAGE
|
|
||||||
)
|
)
|
||||||
if (RequestPermissionPromptDialog.isNeedPrompt() && !PermissionHelper.isAllGranted(
|
if (RequestPermissionPromptDialog.isNeedPrompt() && !PermissionHelper.isAllGranted(
|
||||||
rxPermissions,
|
rxPermissions,
|
||||||
|
@@ -6,6 +6,8 @@ import android.content.pm.PackageManager;
|
|||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
import androidx.core.app.ActivityCompat;
|
import androidx.core.app.ActivityCompat;
|
||||||
import androidx.core.content.ContextCompat;
|
import androidx.core.content.ContextCompat;
|
||||||
|
|
||||||
|
import android.os.Build;
|
||||||
import android.text.TextUtils;
|
import android.text.TextUtils;
|
||||||
|
|
||||||
import com.nnbc123.app.takephoto.app.TakePhoto;
|
import com.nnbc123.app.takephoto.app.TakePhoto;
|
||||||
@@ -23,7 +25,7 @@ import java.util.ArrayList;
|
|||||||
*/
|
*/
|
||||||
public class PermissionManager {
|
public class PermissionManager {
|
||||||
public enum TPermission {
|
public enum TPermission {
|
||||||
STORAGE(Manifest.permission.WRITE_EXTERNAL_STORAGE),
|
STORAGE((Build.VERSION.SDK_INT >= 33) ? Manifest.permission.READ_MEDIA_IMAGES : Manifest.permission.WRITE_EXTERNAL_STORAGE),
|
||||||
CAMERA(Manifest.permission.CAMERA);
|
CAMERA(Manifest.permission.CAMERA);
|
||||||
String stringValue;
|
String stringValue;
|
||||||
|
|
||||||
|
@@ -38,6 +38,8 @@ public class CustomItem implements Parcelable, Serializable {
|
|||||||
*/
|
*/
|
||||||
private int fileType;
|
private int fileType;
|
||||||
private String format;
|
private String format;
|
||||||
|
private int width;
|
||||||
|
private int height;
|
||||||
|
|
||||||
public CustomItem(String path, int fileType) {
|
public CustomItem(String path, int fileType) {
|
||||||
this(path, fileType, "jpeg");
|
this(path, fileType, "jpeg");
|
||||||
@@ -50,12 +52,16 @@ public class CustomItem implements Parcelable, Serializable {
|
|||||||
this.path = path;
|
this.path = path;
|
||||||
this.fileType = fileType;
|
this.fileType = fileType;
|
||||||
this.format = format;
|
this.format = format;
|
||||||
|
this.width = 0;
|
||||||
|
this.height = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected CustomItem(Parcel in) {
|
protected CustomItem(Parcel in) {
|
||||||
this.path = in.readString();
|
this.path = in.readString();
|
||||||
this.fileType = in.readInt();
|
this.fileType = in.readInt();
|
||||||
this.format = in.readString();
|
this.format = in.readString();
|
||||||
|
this.width = in.readInt();
|
||||||
|
this.height = in.readInt();
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isImage() {
|
public boolean isImage() {
|
||||||
@@ -104,6 +110,22 @@ public class CustomItem implements Parcelable, Serializable {
|
|||||||
this.format = format;
|
this.format = format;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public int getWidth() {
|
||||||
|
return width;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setWidth(int width) {
|
||||||
|
this.width = width;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getHeight() {
|
||||||
|
return height;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setHeight(int height) {
|
||||||
|
this.height = height;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int describeContents() {
|
public int describeContents() {
|
||||||
return 0;
|
return 0;
|
||||||
@@ -114,5 +136,7 @@ public class CustomItem implements Parcelable, Serializable {
|
|||||||
dest.writeString(this.path);
|
dest.writeString(this.path);
|
||||||
dest.writeInt(this.fileType);
|
dest.writeInt(this.fileType);
|
||||||
dest.writeString(this.format);
|
dest.writeString(this.format);
|
||||||
|
dest.writeInt(this.width);
|
||||||
|
dest.writeInt(this.height);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -22,6 +22,7 @@ import android.net.Uri;
|
|||||||
import android.os.Parcel;
|
import android.os.Parcel;
|
||||||
import android.os.Parcelable;
|
import android.os.Parcelable;
|
||||||
import android.provider.MediaStore;
|
import android.provider.MediaStore;
|
||||||
|
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
|
|
||||||
import com.zhihu.matisse.MimeType;
|
import com.zhihu.matisse.MimeType;
|
||||||
@@ -46,8 +47,10 @@ public class Item implements Parcelable {
|
|||||||
public final Uri uri;
|
public final Uri uri;
|
||||||
public final long size;
|
public final long size;
|
||||||
public final long duration; // only for video, in ms
|
public final long duration; // only for video, in ms
|
||||||
|
public final int width;
|
||||||
|
public final int height;
|
||||||
|
|
||||||
private Item(long id, String mimeType, long size, long duration) {
|
private Item(long id, String mimeType, long size, long duration,int width,int height) {
|
||||||
this.id = id;
|
this.id = id;
|
||||||
this.mimeType = mimeType;
|
this.mimeType = mimeType;
|
||||||
Uri contentUri;
|
Uri contentUri;
|
||||||
@@ -62,14 +65,18 @@ public class Item implements Parcelable {
|
|||||||
this.uri = ContentUris.withAppendedId(contentUri, id);
|
this.uri = ContentUris.withAppendedId(contentUri, id);
|
||||||
this.size = size;
|
this.size = size;
|
||||||
this.duration = duration;
|
this.duration = duration;
|
||||||
|
this.width = width;
|
||||||
|
this.height = height;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Item(String mimeType, Uri uri, long size, long duration) {
|
public Item(String mimeType, Uri uri, long size, long duration, int width, int height) {
|
||||||
this.id = 1;
|
this.id = 1;
|
||||||
this.mimeType = mimeType;
|
this.mimeType = mimeType;
|
||||||
this.uri = uri;
|
this.uri = uri;
|
||||||
this.size = size;
|
this.size = size;
|
||||||
this.duration = duration;
|
this.duration = duration;
|
||||||
|
this.width = width;
|
||||||
|
this.height = height;
|
||||||
}
|
}
|
||||||
|
|
||||||
private Item(Parcel source) {
|
private Item(Parcel source) {
|
||||||
@@ -78,13 +85,44 @@ public class Item implements Parcelable {
|
|||||||
uri = source.readParcelable(Uri.class.getClassLoader());
|
uri = source.readParcelable(Uri.class.getClassLoader());
|
||||||
size = source.readLong();
|
size = source.readLong();
|
||||||
duration = source.readLong();
|
duration = source.readLong();
|
||||||
|
width = source.readInt();
|
||||||
|
height = source.readInt();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Item valueOf(Cursor cursor) {
|
public static Item valueOf(Cursor cursor) {
|
||||||
return new Item(cursor.getLong(cursor.getColumnIndex(MediaStore.Files.FileColumns._ID)),
|
return new Item(getCursorLong(cursor, MediaStore.Files.FileColumns._ID, 0),
|
||||||
cursor.getString(cursor.getColumnIndex(MediaStore.MediaColumns.MIME_TYPE)),
|
getCursorString(cursor, MediaStore.MediaColumns.MIME_TYPE, ""),
|
||||||
cursor.getLong(cursor.getColumnIndex(MediaStore.MediaColumns.SIZE)),
|
getCursorLong(cursor, MediaStore.MediaColumns.SIZE, 0),
|
||||||
cursor.getLong(cursor.getColumnIndex("duration")));
|
getCursorLong(cursor, "duration", 0),
|
||||||
|
getCursorInt(cursor, MediaStore.MediaColumns.WIDTH, 0),
|
||||||
|
getCursorInt(cursor, MediaStore.MediaColumns.HEIGHT, 0));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static long getCursorLong(Cursor cursor, String key, long defaultValue) {
|
||||||
|
int index = cursor.getColumnIndex(key);
|
||||||
|
long value = defaultValue;
|
||||||
|
if (index >= 0) {
|
||||||
|
value = cursor.getLong(index);
|
||||||
|
}
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String getCursorString(Cursor cursor, String key, String defaultValue) {
|
||||||
|
int index = cursor.getColumnIndex(key);
|
||||||
|
String value = defaultValue;
|
||||||
|
if (index >= 0) {
|
||||||
|
value = cursor.getString(index);
|
||||||
|
}
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int getCursorInt(Cursor cursor, String key, int defaultValue) {
|
||||||
|
int index = cursor.getColumnIndex(key);
|
||||||
|
int value = defaultValue;
|
||||||
|
if (index >= 0) {
|
||||||
|
value = cursor.getInt(index);
|
||||||
|
}
|
||||||
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -99,6 +137,8 @@ public class Item implements Parcelable {
|
|||||||
dest.writeParcelable(uri, 0);
|
dest.writeParcelable(uri, 0);
|
||||||
dest.writeLong(size);
|
dest.writeLong(size);
|
||||||
dest.writeLong(duration);
|
dest.writeLong(duration);
|
||||||
|
dest.writeInt(width);
|
||||||
|
dest.writeInt(height);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Uri getContentUri() {
|
public Uri getContentUri() {
|
||||||
@@ -134,7 +174,9 @@ public class Item implements Parcelable {
|
|||||||
&& (uri != null && uri.equals(other.uri)
|
&& (uri != null && uri.equals(other.uri)
|
||||||
|| (uri == null && other.uri == null))
|
|| (uri == null && other.uri == null))
|
||||||
&& size == other.size
|
&& size == other.size
|
||||||
&& duration == other.duration;
|
&& duration == other.duration
|
||||||
|
&& width == other.width
|
||||||
|
&& height == other.height;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -147,6 +189,8 @@ public class Item implements Parcelable {
|
|||||||
result = 31 * result + uri.hashCode();
|
result = 31 * result + uri.hashCode();
|
||||||
result = 31 * result + Long.valueOf(size).hashCode();
|
result = 31 * result + Long.valueOf(size).hashCode();
|
||||||
result = 31 * result + Long.valueOf(duration).hashCode();
|
result = 31 * result + Long.valueOf(duration).hashCode();
|
||||||
|
result = 31 * result + Integer.valueOf(width).hashCode();
|
||||||
|
result = 31 * result + Integer.valueOf(height).hashCode();
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -39,6 +39,8 @@ public class AlbumMediaLoader extends CursorLoader {
|
|||||||
MediaStore.MediaColumns.DISPLAY_NAME,
|
MediaStore.MediaColumns.DISPLAY_NAME,
|
||||||
MediaStore.MediaColumns.MIME_TYPE,
|
MediaStore.MediaColumns.MIME_TYPE,
|
||||||
MediaStore.MediaColumns.SIZE,
|
MediaStore.MediaColumns.SIZE,
|
||||||
|
MediaStore.MediaColumns.WIDTH,
|
||||||
|
MediaStore.MediaColumns.HEIGHT,
|
||||||
"duration"};
|
"duration"};
|
||||||
|
|
||||||
// === params for album ALL && showSingleMediaType: false ===
|
// === params for album ALL && showSingleMediaType: false ===
|
||||||
@@ -143,7 +145,7 @@ public class AlbumMediaLoader extends CursorLoader {
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
MatrixCursor dummy = new MatrixCursor(PROJECTION);
|
MatrixCursor dummy = new MatrixCursor(PROJECTION);
|
||||||
dummy.addRow(new Object[]{Item.ITEM_ID_CAPTURE, Item.ITEM_DISPLAY_NAME_CAPTURE, "", 0, 0});
|
dummy.addRow(new Object[]{Item.ITEM_ID_CAPTURE, Item.ITEM_DISPLAY_NAME_CAPTURE, "", 0, 0, 0, 0});
|
||||||
return new MergeCursor(new Cursor[]{dummy, result});
|
return new MergeCursor(new Cursor[]{dummy, result});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -165,6 +165,9 @@ public class SelectedItemCollection {
|
|||||||
CustomItem customItem = new CustomItem();
|
CustomItem customItem = new CustomItem();
|
||||||
customItem.setPath(PathUtils.getPath(mContext, item.getContentUri()));
|
customItem.setPath(PathUtils.getPath(mContext, item.getContentUri()));
|
||||||
customItem.setFileType(item.isGif() ? 1 : 0);
|
customItem.setFileType(item.isGif() ? 1 : 0);
|
||||||
|
customItem.setWidth(item.width);
|
||||||
|
customItem.setHeight(item.height);
|
||||||
|
customItem.setFormat(item.mimeType);
|
||||||
paths.add(customItem);
|
paths.add(customItem);
|
||||||
}
|
}
|
||||||
return paths;
|
return paths;
|
||||||
|
@@ -21,6 +21,8 @@ import android.content.Intent;
|
|||||||
import android.content.pm.PackageManager;
|
import android.content.pm.PackageManager;
|
||||||
import android.content.res.TypedArray;
|
import android.content.res.TypedArray;
|
||||||
import android.database.Cursor;
|
import android.database.Cursor;
|
||||||
|
import android.graphics.Bitmap;
|
||||||
|
import android.graphics.BitmapFactory;
|
||||||
import android.graphics.PorterDuff;
|
import android.graphics.PorterDuff;
|
||||||
import android.graphics.drawable.Drawable;
|
import android.graphics.drawable.Drawable;
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
@@ -250,6 +252,9 @@ public class MatisseActivity extends AppCompatActivity implements
|
|||||||
for (Item item : selected) {
|
for (Item item : selected) {
|
||||||
selectedUris.add(item.getContentUri());
|
selectedUris.add(item.getContentUri());
|
||||||
CustomItem customItem = new CustomItem();
|
CustomItem customItem = new CustomItem();
|
||||||
|
customItem.setFormat(item.mimeType);
|
||||||
|
customItem.setWidth(item.width);
|
||||||
|
customItem.setHeight(item.height);
|
||||||
customItem.setFileType(item.isGif() ? 1 : 0);
|
customItem.setFileType(item.isGif() ? 1 : 0);
|
||||||
customItem.setPath(PathUtils.getPath(this, item.getContentUri()));
|
customItem.setPath(PathUtils.getPath(this, item.getContentUri()));
|
||||||
selectedPaths.add(customItem);
|
selectedPaths.add(customItem);
|
||||||
@@ -280,6 +285,7 @@ public class MatisseActivity extends AppCompatActivity implements
|
|||||||
CustomItem customItem = new CustomItem();
|
CustomItem customItem = new CustomItem();
|
||||||
customItem.setFileType(0);
|
customItem.setFileType(0);
|
||||||
customItem.setPath(path);
|
customItem.setPath(path);
|
||||||
|
trySetImageInfo(customItem);
|
||||||
selectedPath.add(customItem);
|
selectedPath.add(customItem);
|
||||||
Intent result = new Intent();
|
Intent result = new Intent();
|
||||||
result.putParcelableArrayListExtra(EXTRA_RESULT_SELECTION, selected);
|
result.putParcelableArrayListExtra(EXTRA_RESULT_SELECTION, selected);
|
||||||
@@ -298,6 +304,9 @@ public class MatisseActivity extends AppCompatActivity implements
|
|||||||
ArrayList<CustomItem> selectedPath = new ArrayList<>(1);
|
ArrayList<CustomItem> selectedPath = new ArrayList<>(1);
|
||||||
Item item = selected.get(0);
|
Item item = selected.get(0);
|
||||||
CustomItem customItem = new CustomItem();
|
CustomItem customItem = new CustomItem();
|
||||||
|
customItem.setFormat(item.mimeType);
|
||||||
|
customItem.setWidth(item.width);
|
||||||
|
customItem.setHeight(item.height);
|
||||||
customItem.setFileType(2);
|
customItem.setFileType(2);
|
||||||
customItem.setPath(item.uri.toString());
|
customItem.setPath(item.uri.toString());
|
||||||
selectedPath.add(customItem);
|
selectedPath.add(customItem);
|
||||||
@@ -309,6 +318,26 @@ public class MatisseActivity extends AppCompatActivity implements
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 补充图片信息
|
||||||
|
private void trySetImageInfo(CustomItem customItem) {
|
||||||
|
try {
|
||||||
|
BitmapFactory.Options options = new BitmapFactory.Options();
|
||||||
|
options.inJustDecodeBounds = true;
|
||||||
|
Bitmap bitmap = BitmapFactory.decodeFile(customItem.getPath(), options);
|
||||||
|
if (customItem.getWidth() <= 0) {
|
||||||
|
customItem.setWidth(options.outWidth);
|
||||||
|
}
|
||||||
|
if (customItem.getHeight() <= 0) {
|
||||||
|
customItem.setHeight(options.outHeight);
|
||||||
|
}
|
||||||
|
if (customItem.getFormat() == null || customItem.getFormat().length() == 0) {
|
||||||
|
customItem.setFormat(options.outMimeType);
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private void updateBottomToolbar() {
|
private void updateBottomToolbar() {
|
||||||
int selectedCount = mSelectedCollection.count();
|
int selectedCount = mSelectedCollection.count();
|
||||||
if (selectedCount == 0) {
|
if (selectedCount == 0) {
|
||||||
@@ -521,20 +550,16 @@ public class MatisseActivity extends AppCompatActivity implements
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void capture() {
|
public void capture() {
|
||||||
// if (mMediaStoreCompat != null) {
|
if (ContextCompat.checkSelfPermission(this, (Build.VERSION.SDK_INT >= 33) ? Manifest.permission.READ_MEDIA_IMAGES : Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED
|
||||||
if (ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED
|
|
||||||
&& ContextCompat.checkSelfPermission(this, Manifest.permission.RECORD_AUDIO) == PackageManager.PERMISSION_GRANTED
|
&& ContextCompat.checkSelfPermission(this, Manifest.permission.RECORD_AUDIO) == PackageManager.PERMISSION_GRANTED
|
||||||
&& ContextCompat.checkSelfPermission(this, Manifest.permission.CAMERA) == PackageManager.PERMISSION_GRANTED) {
|
&& ContextCompat.checkSelfPermission(this, Manifest.permission.CAMERA) == PackageManager.PERMISSION_GRANTED) {
|
||||||
mMediaStoreCompat.dispatchCaptureIntent(this, REQUEST_CODE_CAPTURE);
|
mMediaStoreCompat.dispatchCaptureIntent(this, REQUEST_CODE_CAPTURE);
|
||||||
// startActivityForResult(new Intent(this, CameraActivity.class)
|
|
||||||
// .putExtra(ConstantValue.KEY_TYPE, mSpec.type), REQUEST_CODE_CAPTURE);
|
|
||||||
} else {
|
} else {
|
||||||
ActivityCompat.requestPermissions(this, new String[]{
|
ActivityCompat.requestPermissions(this, new String[]{
|
||||||
Manifest.permission.WRITE_EXTERNAL_STORAGE,
|
(Build.VERSION.SDK_INT >= 33) ? Manifest.permission.READ_MEDIA_IMAGES : Manifest.permission.WRITE_EXTERNAL_STORAGE,
|
||||||
Manifest.permission.RECORD_AUDIO,
|
Manifest.permission.RECORD_AUDIO,
|
||||||
Manifest.permission.CAMERA}, 10);
|
Manifest.permission.CAMERA}, 10);
|
||||||
}
|
}
|
||||||
// }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@@ -4,6 +4,7 @@ import android.Manifest;
|
|||||||
import android.annotation.SuppressLint;
|
import android.annotation.SuppressLint;
|
||||||
import android.app.Activity;
|
import android.app.Activity;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
|
import android.os.Build;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.widget.ImageView;
|
import android.widget.ImageView;
|
||||||
@@ -158,7 +159,7 @@ public class BigPhotoActivity extends BaseActivity implements OnFragmentOptionLi
|
|||||||
if (item == null) {
|
if (item == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
checkPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE)
|
checkPermission((Build.VERSION.SDK_INT >= 33) ? Manifest.permission.READ_MEDIA_IMAGES : Manifest.permission.WRITE_EXTERNAL_STORAGE)
|
||||||
.compose(bindToLifecycle())
|
.compose(bindToLifecycle())
|
||||||
.subscribe(aBoolean -> {
|
.subscribe(aBoolean -> {
|
||||||
if (aBoolean) {
|
if (aBoolean) {
|
||||||
|
@@ -48,9 +48,9 @@ public class PublishPresenter extends BaseMvpPresenter<IPublishView> {
|
|||||||
|
|
||||||
private MiniWorldChooseInfo miniWorldChooseInfo = new MiniWorldChooseInfo();
|
private MiniWorldChooseInfo miniWorldChooseInfo = new MiniWorldChooseInfo();
|
||||||
|
|
||||||
public void publishDy(List<String> list, long worldId, String content, boolean isOriginalImage) {
|
public void publishDy(List<DynamicMedia> list, long worldId, String content, boolean isOriginalImage) {
|
||||||
publishBody = new PublishBody();
|
publishBody = new PublishBody();
|
||||||
List<String> uploadList = new ArrayList<>(list);
|
List<DynamicMedia> uploadList = new ArrayList<>(list);
|
||||||
int type = ListUtils.isListEmpty(uploadList) ? 0 : 2;
|
int type = ListUtils.isListEmpty(uploadList) ? 0 : 2;
|
||||||
publishBody.setType(type);
|
publishBody.setType(type);
|
||||||
publishBody.setUid(AuthModel.get().getCurrentUid());
|
publishBody.setUid(AuthModel.get().getCurrentUid());
|
||||||
@@ -77,7 +77,7 @@ public class PublishPresenter extends BaseMvpPresenter<IPublishView> {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private Single<String> uploadImage(List<String> imagePaths) {
|
private Single<String> uploadImage(List<DynamicMedia> imagePaths) {
|
||||||
upload(imagePaths);
|
upload(imagePaths);
|
||||||
return Single.create(emitter ->
|
return Single.create(emitter ->
|
||||||
mImageUploadSubscribe = Observable.interval(500, TimeUnit.MILLISECONDS)
|
mImageUploadSubscribe = Observable.interval(500, TimeUnit.MILLISECONDS)
|
||||||
@@ -91,22 +91,31 @@ public class PublishPresenter extends BaseMvpPresenter<IPublishView> {
|
|||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
private void upload(List<String> imagePaths) {
|
private void upload(List<DynamicMedia> imagePaths) {
|
||||||
if (imagePaths == null || imagePaths.size() == 0) {
|
if (imagePaths == null || imagePaths.size() == 0) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
String file = imagePaths.get(0);
|
DynamicMedia item = imagePaths.get(0);
|
||||||
|
String file = item.getLocalFilePath();
|
||||||
Single.create((SingleOnSubscribe<String>) e -> {
|
Single.create((SingleOnSubscribe<String>) e -> {
|
||||||
long fileLength = JXFileUtils.getFileLength(file);
|
long fileLength = JXFileUtils.getFileLength(file);
|
||||||
LogUtil.print("文件大小:" + fileLength);
|
LogUtil.print("文件大小:" + fileLength);
|
||||||
String compressFile = null;
|
String compressFile = null;
|
||||||
if (!isOriginalImage && fileLength > ImageUploadConfig.MAX_FILE_SIZE) {
|
if (!isOriginalImage && fileLength > ImageUploadConfig.MAX_FILE_SIZE) {
|
||||||
compressFile = JXImageUtils.compressImagePxAndQuality(
|
JXImageUtils.CompressResult result = JXImageUtils.compressImagePxAndQuality(
|
||||||
file, DirectoryHelper.get().getDynamicDir(),
|
file, DirectoryHelper.get().getDynamicDir(),
|
||||||
"dynamic_" + System.currentTimeMillis() + ".jpg",
|
"dynamic_" + System.currentTimeMillis() + ".jpg",
|
||||||
ImageUploadConfig.EXPECT_MIN_WIDTH,
|
ImageUploadConfig.EXPECT_MIN_WIDTH,
|
||||||
ImageUploadConfig.EXPECT_COMPRESS_SIZE);
|
ImageUploadConfig.EXPECT_COMPRESS_SIZE);
|
||||||
|
if (result != null) {
|
||||||
|
compressFile = result.getPath();
|
||||||
|
if (result.getWidth() > 0 && item.getWidth() != result.getWidth()) {
|
||||||
|
item.setWidth(result.getWidth());
|
||||||
|
}
|
||||||
|
if (result.getHeight() > 0 && item.getHeight() != result.getHeight()) {
|
||||||
|
item.setHeight(result.getHeight());
|
||||||
|
}
|
||||||
|
}
|
||||||
LogUtil.print("压缩后:" + compressFile);
|
LogUtil.print("压缩后:" + compressFile);
|
||||||
}
|
}
|
||||||
if (!TextUtils.isEmpty(compressFile)) {
|
if (!TextUtils.isEmpty(compressFile)) {
|
||||||
@@ -118,21 +127,22 @@ public class PublishPresenter extends BaseMvpPresenter<IPublishView> {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
.compose(RxHelper.handleSchedulers())
|
.compose(RxHelper.handleSchedulers())
|
||||||
.flatMap((Function<String, SingleSource<DynamicMedia>>)
|
.flatMap((Function<String, SingleSource<String>>)
|
||||||
path -> FileModel.get().uploadFileReturnImageInfo(path))
|
path -> FileModel.get().uploadFile(path))
|
||||||
.compose(bindUntilEvent(PresenterEvent.DESTROY))
|
.compose(bindUntilEvent(PresenterEvent.DESTROY))
|
||||||
.subscribe(new DontWarnObserver<DynamicMedia>() {
|
.subscribe(new DontWarnObserver<String>() {
|
||||||
@Override
|
@Override
|
||||||
public void acceptThrowable(DynamicMedia media, Throwable throwable) {
|
public void acceptThrowable(String url, Throwable throwable) {
|
||||||
super.acceptThrowable(media, throwable);
|
super.acceptThrowable(url, throwable);
|
||||||
if (throwable != null) {
|
if (throwable != null) {
|
||||||
if (mImageUploadSubscribe != null) {
|
if (mImageUploadSubscribe != null) {
|
||||||
mImageUploadSubscribe.dispose();
|
mImageUploadSubscribe.dispose();
|
||||||
}
|
}
|
||||||
dealUploadFileError(throwable);
|
dealUploadFileError(throwable);
|
||||||
} else {
|
} else {
|
||||||
LogUtil.print("上传成功:", media);
|
LogUtil.print("上传成功:", url);
|
||||||
publishBody.addDynamicMedia(media);
|
item.setResUrl(url);
|
||||||
|
publishBody.addDynamicMedia(item);
|
||||||
imagePaths.remove(0);
|
imagePaths.remove(0);
|
||||||
upload(imagePaths);
|
upload(imagePaths);
|
||||||
}
|
}
|
||||||
|
@@ -338,7 +338,7 @@ public class PublishActivity extends BaseMvpActivity<IPublishView, PublishPresen
|
|||||||
tvPublish.setEnabled(false);
|
tvPublish.setEnabled(false);
|
||||||
getDialogManager().showProgressDialog(context);
|
getDialogManager().showProgressDialog(context);
|
||||||
getMvpPresenter().publishDy(
|
getMvpPresenter().publishDy(
|
||||||
ObjectTypeHelper.customToStringList(uploadList),
|
ObjectTypeHelper.customToMediaList(uploadList),
|
||||||
getMvpPresenter().getWorldId(), etContent.getText().toString(), isOriginalImage);
|
getMvpPresenter().getWorldId(), etContent.getText().toString(), isOriginalImage);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -443,6 +443,7 @@ public class PublishActivity extends BaseMvpActivity<IPublishView, PublishPresen
|
|||||||
public void onPublishFailed(Throwable throwable) {
|
public void onPublishFailed(Throwable throwable) {
|
||||||
getDialogManager().dismissDialog();
|
getDialogManager().dismissDialog();
|
||||||
toast(throwable.getMessage());
|
toast(throwable.getMessage());
|
||||||
|
updatePublishStatus();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@@ -22,6 +22,22 @@ public class ObjectTypeHelper {
|
|||||||
return resultList;
|
return resultList;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static List<DynamicMedia> customToMediaList(List<CustomItem> paramsList) {
|
||||||
|
List<DynamicMedia> resultList = new ArrayList<>();
|
||||||
|
if (paramsList == null) {
|
||||||
|
return resultList;
|
||||||
|
}
|
||||||
|
for (CustomItem item : paramsList) {
|
||||||
|
DynamicMedia media = new DynamicMedia();
|
||||||
|
media.setLocalFilePath(item.getPath());
|
||||||
|
media.setWidth(item.getWidth());
|
||||||
|
media.setHeight(item.getHeight());
|
||||||
|
media.setFormat(item.getFormat());
|
||||||
|
resultList.add(media);
|
||||||
|
}
|
||||||
|
return resultList;
|
||||||
|
}
|
||||||
|
|
||||||
public static List<CustomItem> stringToCustomList(List<String> paramsList) {
|
public static List<CustomItem> stringToCustomList(List<String> paramsList) {
|
||||||
List<CustomItem> resultList = new ArrayList<>();
|
List<CustomItem> resultList = new ArrayList<>();
|
||||||
if (paramsList == null) {
|
if (paramsList == null) {
|
||||||
@@ -40,6 +56,8 @@ public class ObjectTypeHelper {
|
|||||||
}
|
}
|
||||||
for (DynamicMedia media : paramsList) {
|
for (DynamicMedia media : paramsList) {
|
||||||
CustomItem item = new CustomItem();
|
CustomItem item = new CustomItem();
|
||||||
|
item.setWidth(media.getWidth());
|
||||||
|
item.setHeight(media.getHeight());
|
||||||
item.setPath(media.getResUrl());
|
item.setPath(media.getResUrl());
|
||||||
item.setFileType(CustomItem.UNKOWN);
|
item.setFileType(CustomItem.UNKOWN);
|
||||||
if (media.isJpgOrPng()) {
|
if (media.isJpgOrPng()) {
|
||||||
|
@@ -97,6 +97,7 @@ dependencies {
|
|||||||
implementation 'com.google.firebase:firebase-analytics-ktx'
|
implementation 'com.google.firebase:firebase-analytics-ktx'
|
||||||
|
|
||||||
api 'com.android.billingclient:billing:6.0.1'
|
api 'com.android.billingclient:billing:6.0.1'
|
||||||
|
api 'com.qcloud.cos:cos-android:5.9.23'
|
||||||
}
|
}
|
||||||
repositories {
|
repositories {
|
||||||
mavenCentral()
|
mavenCentral()
|
||||||
|
@@ -2,21 +2,17 @@ package com.nnbc123.core.file;
|
|||||||
|
|
||||||
import android.text.TextUtils;
|
import android.text.TextUtils;
|
||||||
|
|
||||||
import com.netease.nim.uikit.common.util.log.LogUtil;
|
import com.chuhai.utils.AppUtils;
|
||||||
import com.qiniu.android.common.FixedZone;
|
import com.chuhai.utils.PathUtils;
|
||||||
import com.qiniu.android.storage.Configuration;
|
import com.nnbc123.core.file.cos.CosClient;
|
||||||
import com.qiniu.android.storage.UploadManager;
|
import com.nnbc123.core.file.cos.CosToken;
|
||||||
import com.nnbc123.core.base.BaseModel;
|
import com.nnbc123.core.base.BaseModel;
|
||||||
import com.nnbc123.core.bean.response.ServiceResult;
|
import com.nnbc123.core.bean.response.ServiceResult;
|
||||||
import com.nnbc123.core.community.bean.DynamicMedia;
|
|
||||||
import com.nnbc123.core.exception.ErrorThrowable;
|
import com.nnbc123.core.exception.ErrorThrowable;
|
||||||
import com.nnbc123.core.utils.net.RxHelper;
|
import com.nnbc123.core.utils.net.RxHelper;
|
||||||
import com.nnbc123.library.net.rxnet.RxNet;
|
import com.nnbc123.library.net.rxnet.RxNet;
|
||||||
|
|
||||||
import org.json.JSONObject;
|
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
|
import java.util.UUID;
|
||||||
import io.reactivex.Single;
|
import io.reactivex.Single;
|
||||||
import retrofit2.http.GET;
|
import retrofit2.http.GET;
|
||||||
|
|
||||||
@@ -27,119 +23,49 @@ public class FileModel extends BaseModel implements IFileModel {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private FileModel() {
|
private FileModel() {
|
||||||
Configuration config = new Configuration.Builder()
|
|
||||||
.zone(FixedZone.zone2) // 设置区域,不指定会自动选择。指定不同区域的上传域名、备用域名、备用IP。
|
|
||||||
.build();
|
|
||||||
uploadManager = new UploadManager(config);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static FileModel get() {
|
public static FileModel get() {
|
||||||
return Helper.INSTANCE;
|
return Helper.INSTANCE;
|
||||||
}
|
}
|
||||||
|
|
||||||
private UploadManager uploadManager;
|
|
||||||
private final Api api = RxNet.create(Api.class);
|
private final Api api = RxNet.create(Api.class);
|
||||||
|
private CosToken cosToken;
|
||||||
|
|
||||||
|
private Single<CosToken> getCosToken() {
|
||||||
|
if (cosToken != null && cosToken.isValid()) {
|
||||||
|
return Single.just(cosToken);
|
||||||
|
} else {
|
||||||
|
return api.getCosToken().compose(RxHelper.handleSchedulers())
|
||||||
|
.compose(RxHelper.handleBeanData()).map(cosToken -> {
|
||||||
|
FileModel.get().cosToken = cosToken;
|
||||||
|
return cosToken;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Single<String> uploadFile(String path) {
|
public Single<String> uploadFile(String path) {
|
||||||
|
|
||||||
File file;
|
File file;
|
||||||
if (TextUtils.isEmpty(path) || !((file = new File(path)).exists())) {
|
if (TextUtils.isEmpty(path) || !((file = new File(path)).exists())) {
|
||||||
return Single.error(new ErrorThrowable(path + " 为空或者该文件不存在!"));
|
return Single.error(new ErrorThrowable(path + " 为空或者该文件不存在!"));
|
||||||
}
|
}
|
||||||
File finalFile = file;
|
File finalFile = file;
|
||||||
return api.getUploadToken()
|
String outName = UUID.randomUUID().toString() + PathUtils.INSTANCE.getSuffixType(finalFile.getName());
|
||||||
.compose(RxHelper.handleSchedulers())
|
return getCosToken().flatMap(token -> CosClient.INSTANCE.upload(AppUtils.getApp(), finalFile, outName, token).map(cosXmlResult -> cosXmlResult.accessUrl));
|
||||||
.compose(RxHelper.handleBeanData())
|
|
||||||
.flatMap(uploadToken -> Single.create(singleEmitter ->
|
|
||||||
uploadManager.put(finalFile, uploadToken.getKey(), uploadToken.getToken(),
|
|
||||||
(key, info, response) -> {
|
|
||||||
if (info.isOK()) {
|
|
||||||
try {
|
|
||||||
String imgUrl = response.getString("path");
|
|
||||||
singleEmitter.onSuccess(imgUrl);
|
|
||||||
} catch (Exception e) {
|
|
||||||
singleEmitter.onError(e);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
singleEmitter.onError(new Throwable(info.error));
|
|
||||||
}
|
|
||||||
}, null)));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public Single<DynamicMedia> uploadFileReturnImageInfo(String path) {
|
|
||||||
return uploadFileReturnImageInfo(path, null);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Single<DynamicMedia> uploadFileReturnImageInfo(String path, String qiniuName) {
|
|
||||||
File file;
|
|
||||||
if (TextUtils.isEmpty(path) || !((file = new File(path)).exists())) {
|
|
||||||
return Single.error(new ErrorThrowable(path + " 为空或者该文件不存在!"));
|
|
||||||
}
|
|
||||||
|
|
||||||
File finalFile = file;
|
|
||||||
return api.getUploadToken()
|
|
||||||
.compose(RxHelper.handleSchedulers())
|
|
||||||
.compose(RxHelper.handleBeanData())
|
|
||||||
.flatMap(uploadToken -> Single.create(singleEmitter ->
|
|
||||||
uploadManager.put(finalFile, uploadToken.getKey(), uploadToken.getToken(),
|
|
||||||
(key, info, response) -> {
|
|
||||||
if (info.isOK()) {
|
|
||||||
try {
|
|
||||||
LogUtil.print("上传成功");
|
|
||||||
LogUtil.print(response);
|
|
||||||
DynamicMedia media = responseToMeia(response);
|
|
||||||
if (media != null) {
|
|
||||||
singleEmitter.onSuccess(media);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
} catch (Exception e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
singleEmitter.onError(new Throwable("qiniu json error"));
|
|
||||||
} else {
|
|
||||||
singleEmitter.onError(new Throwable(info.error));
|
|
||||||
}
|
|
||||||
}, null)));
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Single<String> downloadFile(String url) {
|
public Single<String> downloadFile(String url) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
private DynamicMedia responseToMeia(JSONObject response) {
|
|
||||||
DynamicMedia media = new DynamicMedia();
|
|
||||||
try {
|
|
||||||
String imgNamePath = response.getString("path");
|
|
||||||
media.setResUrl(imgNamePath);
|
|
||||||
} catch (Exception ex) {
|
|
||||||
ex.printStackTrace();
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
media.setFormat(response.getString("format"));
|
|
||||||
media.setWidth(response.getInt("w"));
|
|
||||||
media.setHeight(response.getInt("h"));
|
|
||||||
} catch (Exception ex) {
|
|
||||||
ex.printStackTrace();
|
|
||||||
}
|
|
||||||
LogUtil.print("七牛上传", media);
|
|
||||||
return media;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface Api {
|
interface Api {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取钱包
|
|
||||||
*
|
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
@GET("/qiniu/upload/getUploadToken")
|
@GET("/tencentCos/getToken")
|
||||||
Single<ServiceResult<UploadToken>> getUploadToken();
|
Single<ServiceResult<CosToken>> getCosToken();
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -23,8 +23,4 @@ public interface IFileModel extends IModel {
|
|||||||
*/
|
*/
|
||||||
Single<String> downloadFile(String url);
|
Single<String> downloadFile(String url);
|
||||||
|
|
||||||
Single<DynamicMedia> uploadFileReturnImageInfo(String path);
|
|
||||||
|
|
||||||
Single<DynamicMedia> uploadFileReturnImageInfo(String path, String qiniuName);
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
113
core/src/main/java/com/nnbc123/core/file/cos/CosClient.kt
Normal file
113
core/src/main/java/com/nnbc123/core/file/cos/CosClient.kt
Normal file
@@ -0,0 +1,113 @@
|
|||||||
|
package com.nnbc123.core.file.cos
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import android.util.Log
|
||||||
|
import com.nnbc123.core.Env
|
||||||
|
import com.tencent.cos.xml.CosXmlService
|
||||||
|
import com.tencent.cos.xml.CosXmlServiceConfig
|
||||||
|
import com.tencent.cos.xml.exception.CosXmlClientException
|
||||||
|
import com.tencent.cos.xml.exception.CosXmlServiceException
|
||||||
|
import com.tencent.cos.xml.listener.CosXmlResultListener
|
||||||
|
import com.tencent.cos.xml.model.CosXmlRequest
|
||||||
|
import com.tencent.cos.xml.model.CosXmlResult
|
||||||
|
import com.tencent.cos.xml.transfer.COSXMLUploadTask
|
||||||
|
import com.tencent.cos.xml.transfer.TransferConfig
|
||||||
|
import com.tencent.cos.xml.transfer.TransferManager
|
||||||
|
import io.reactivex.Single
|
||||||
|
import java.io.File
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Created by Max on 2024/1/16 11:09
|
||||||
|
* Desc:
|
||||||
|
**/
|
||||||
|
object CosClient {
|
||||||
|
private var cosXmlClient: CosXmlService? = null
|
||||||
|
private var credentialProvider: CosCredentialProvider? = null
|
||||||
|
|
||||||
|
private fun getCosXmlClient(context: Context, token: CosToken): CosXmlService {
|
||||||
|
var client = this.cosXmlClient
|
||||||
|
if (client != null && credentialProvider != null) {
|
||||||
|
credentialProvider?.updateCredentials(token.toCredential())
|
||||||
|
return client
|
||||||
|
}
|
||||||
|
// 创建 CosXmlServiceConfig 对象,根据需要修改默认的配置参数
|
||||||
|
val serviceConfig: CosXmlServiceConfig = CosXmlServiceConfig.Builder()
|
||||||
|
.setRegion(token.region)
|
||||||
|
.isHttps(true) // 使用 HTTPS 请求, 默认为 HTTP 请求
|
||||||
|
.builder()
|
||||||
|
|
||||||
|
val credentials = token.toCredential()
|
||||||
|
credentialProvider = CosCredentialProvider(credentials)
|
||||||
|
// 初始化 COS Service,获取实例
|
||||||
|
client = CosXmlService(
|
||||||
|
context,
|
||||||
|
serviceConfig, credentialProvider
|
||||||
|
)
|
||||||
|
cosXmlClient = client
|
||||||
|
return client
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 上传文件
|
||||||
|
* @param outName 远端文件名
|
||||||
|
*/
|
||||||
|
fun upload(
|
||||||
|
context: Context,
|
||||||
|
file: File,
|
||||||
|
outName: String,
|
||||||
|
token: CosToken
|
||||||
|
): Single<CosXmlResult> {
|
||||||
|
if (Env.isDebug()) {
|
||||||
|
Log.e("CosClient", "upload file:${file.absolutePath} outName:${outName}")
|
||||||
|
}
|
||||||
|
return Single.create {
|
||||||
|
uploadFile(context, file, outName, token).apply {
|
||||||
|
setCosXmlResultListener(object : CosXmlResultListener {
|
||||||
|
override fun onSuccess(request: CosXmlRequest?, result: CosXmlResult) {
|
||||||
|
if (Env.isDebug()) {
|
||||||
|
Log.e("CosClient", "upload onSuccess result:${result.accessUrl}")
|
||||||
|
}
|
||||||
|
it.onSuccess(result)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onFail(
|
||||||
|
request: CosXmlRequest?,
|
||||||
|
clientException: CosXmlClientException?,
|
||||||
|
serviceException: CosXmlServiceException?
|
||||||
|
) {
|
||||||
|
if (Env.isDebug()) {
|
||||||
|
Log.e("CosClient", "upload onFail clientException:$clientException")
|
||||||
|
Log.e("CosClient", "upload onFail serviceException:$serviceException")
|
||||||
|
}
|
||||||
|
it.onError(CosException(clientException, serviceException))
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 上传文件
|
||||||
|
* @param outName 远端文件名
|
||||||
|
*/
|
||||||
|
private fun uploadFile(
|
||||||
|
context: Context,
|
||||||
|
file: File,
|
||||||
|
outName: String,
|
||||||
|
token: CosToken
|
||||||
|
): COSXMLUploadTask {
|
||||||
|
val cosXmlService = getCosXmlClient(context, token)
|
||||||
|
// 初始化 TransferConfig,这里使用默认配置,如果需要定制,请参考 SDK 接口文档
|
||||||
|
val transferConfig = TransferConfig.Builder().build()
|
||||||
|
// 初始化 TransferManager
|
||||||
|
val transferManager = TransferManager(
|
||||||
|
cosXmlService,
|
||||||
|
transferConfig
|
||||||
|
)
|
||||||
|
return transferManager.upload(
|
||||||
|
token.bucket, outName,
|
||||||
|
file.absolutePath, null
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,20 @@
|
|||||||
|
package com.nnbc123.core.file.cos
|
||||||
|
|
||||||
|
import com.tencent.qcloud.core.auth.BasicLifecycleCredentialProvider
|
||||||
|
import com.tencent.qcloud.core.auth.QCloudLifecycleCredentials
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Created by Max on 2024/1/16 11:28
|
||||||
|
* Desc:
|
||||||
|
**/
|
||||||
|
class CosCredentialProvider(private var credentials: QCloudLifecycleCredentials) :
|
||||||
|
BasicLifecycleCredentialProvider() {
|
||||||
|
|
||||||
|
fun updateCredentials(credentials: QCloudLifecycleCredentials) {
|
||||||
|
this.credentials = credentials
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun fetchNewCredentials(): QCloudLifecycleCredentials {
|
||||||
|
return credentials
|
||||||
|
}
|
||||||
|
}
|
21
core/src/main/java/com/nnbc123/core/file/cos/CosException.kt
Normal file
21
core/src/main/java/com/nnbc123/core/file/cos/CosException.kt
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
package com.nnbc123.core.file.cos
|
||||||
|
|
||||||
|
import com.tencent.cos.xml.exception.CosXmlClientException
|
||||||
|
import com.tencent.cos.xml.exception.CosXmlServiceException
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Created by Max on 2024/1/16 15:49
|
||||||
|
* Desc:
|
||||||
|
**/
|
||||||
|
class CosException : Exception {
|
||||||
|
var clientException: CosXmlClientException? = null
|
||||||
|
var serviceException: CosXmlServiceException? = null
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
clientException: CosXmlClientException?,
|
||||||
|
serviceException: CosXmlServiceException?
|
||||||
|
) : super(clientException ?: serviceException) {
|
||||||
|
this.clientException = clientException
|
||||||
|
this.serviceException = serviceException
|
||||||
|
}
|
||||||
|
}
|
43
core/src/main/java/com/nnbc123/core/file/cos/CosToken.kt
Normal file
43
core/src/main/java/com/nnbc123/core/file/cos/CosToken.kt
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
package com.nnbc123.core.file.cos
|
||||||
|
|
||||||
|
import com.nnbc123.core.utils.CurrentTimeUtils
|
||||||
|
import com.tencent.qcloud.core.auth.QCloudLifecycleCredentials
|
||||||
|
import com.tencent.qcloud.core.auth.SessionQCloudCredentials
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Created by Max on 2024/1/16 11:44
|
||||||
|
* Desc:
|
||||||
|
**/
|
||||||
|
data class CosToken(
|
||||||
|
val tmpSecretId: String?,
|
||||||
|
val tmpSecretKey: String?,
|
||||||
|
val sessionToken: String?,
|
||||||
|
val bucket: String?,
|
||||||
|
val region: String?,
|
||||||
|
val startTime: Long?,
|
||||||
|
val expireTime: Long?,
|
||||||
|
) {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 是否有效
|
||||||
|
*/
|
||||||
|
fun isValid(): Boolean {
|
||||||
|
if (expireTime == null) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
val currentTime = CurrentTimeUtils.getCurrentTime() / 1000
|
||||||
|
// 预留一点安全时长
|
||||||
|
var safeTime = 30
|
||||||
|
if (safeTime >= (expireTime - (startTime ?: 0))) {
|
||||||
|
safeTime = 0
|
||||||
|
}
|
||||||
|
return currentTime <= (expireTime - safeTime)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun toCredential(): QCloudLifecycleCredentials {
|
||||||
|
return SessionQCloudCredentials(
|
||||||
|
tmpSecretId ?: "", tmpSecretKey ?: "",
|
||||||
|
sessionToken ?: "", startTime ?: 0, expireTime ?: 0
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
@@ -1,10 +0,0 @@
|
|||||||
package com.nnbc123.core.utils;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* create by lvzebiao @2019/11/20
|
|
||||||
*/
|
|
||||||
public class UploadUtils {
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
@@ -2,6 +2,8 @@ package com.nnbc123.core.community.bean;
|
|||||||
|
|
||||||
import android.text.TextUtils;
|
import android.text.TextUtils;
|
||||||
|
|
||||||
|
import kotlin.jvm.Transient;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* create by lvzebiao @2019/11/21
|
* create by lvzebiao @2019/11/21
|
||||||
*/
|
*/
|
||||||
@@ -21,6 +23,17 @@ public class DynamicMedia {
|
|||||||
private int width;
|
private int width;
|
||||||
private int height;
|
private int height;
|
||||||
|
|
||||||
|
@Transient
|
||||||
|
private String localFilePath;
|
||||||
|
|
||||||
|
public String getLocalFilePath() {
|
||||||
|
return localFilePath;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setLocalFilePath(String localFilePath) {
|
||||||
|
this.localFilePath = localFilePath;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 是否是网络图片
|
* 是否是网络图片
|
||||||
* @return -
|
* @return -
|
||||||
@@ -38,14 +51,14 @@ public class DynamicMedia {
|
|||||||
if (TextUtils.isEmpty(format)) {
|
if (TextUtils.isEmpty(format)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return format.toLowerCase().equals("jpeg") || format.toLowerCase().equals("jpg") || format.toLowerCase().equals("png");
|
return format.toLowerCase().contains("jpeg") || format.toLowerCase().contains("jpg") || format.toLowerCase().contains("png");
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isGif() {
|
public boolean isGif() {
|
||||||
if (TextUtils.isEmpty(format)) {
|
if (TextUtils.isEmpty(format)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return format.toLowerCase().equals("gif");
|
return format.toLowerCase().contains("gif");
|
||||||
}
|
}
|
||||||
|
|
||||||
//<editor-fold defaultstate="collapsed" desc="delombok">
|
//<editor-fold defaultstate="collapsed" desc="delombok">
|
||||||
|
@@ -121,8 +121,6 @@ dependencies {
|
|||||||
|
|
||||||
api "com.orhanobut:logger:${loggerVersion}"
|
api "com.orhanobut:logger:${loggerVersion}"
|
||||||
|
|
||||||
api "com.qiniu:qiniu-android-sdk:${qiniu}"
|
|
||||||
|
|
||||||
api "org.greenrobot:eventbus:${eventbusVersion}"
|
api "org.greenrobot:eventbus:${eventbusVersion}"
|
||||||
api "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
|
api "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
|
||||||
|
|
||||||
@@ -143,10 +141,6 @@ dependencies {
|
|||||||
|
|
||||||
api 'com.google.android.exoplayer:exoplayer:2.18.1'
|
api 'com.google.android.exoplayer:exoplayer:2.18.1'
|
||||||
|
|
||||||
// 网络请求chrome数据调试
|
|
||||||
api 'com.facebook.stetho:stetho:1.5.1'
|
|
||||||
api 'com.facebook.stetho:stetho-okhttp3:1.5.1'
|
|
||||||
|
|
||||||
api 'com.geyifeng.immersionbar:immersionbar:3.2.2'
|
api 'com.geyifeng.immersionbar:immersionbar:3.2.2'
|
||||||
|
|
||||||
api files("libs/alicloud-android-utdid-2.6.0.jar")
|
api files("libs/alicloud-android-utdid-2.6.0.jar")
|
||||||
|
@@ -1,338 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) 2015 Square, Inc.
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
package com.nnbc123.library.net.rxnet.interceptor;
|
|
||||||
|
|
||||||
import static okhttp3.internal.platform.Platform.INFO;
|
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
|
||||||
|
|
||||||
import java.io.EOFException;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.nio.charset.Charset;
|
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.Set;
|
|
||||||
import java.util.TreeSet;
|
|
||||||
import java.util.concurrent.TimeUnit;
|
|
||||||
|
|
||||||
import okhttp3.Connection;
|
|
||||||
import okhttp3.Headers;
|
|
||||||
import okhttp3.Interceptor;
|
|
||||||
import okhttp3.MediaType;
|
|
||||||
import okhttp3.OkHttpClient;
|
|
||||||
import okhttp3.Request;
|
|
||||||
import okhttp3.RequestBody;
|
|
||||||
import okhttp3.Response;
|
|
||||||
import okhttp3.ResponseBody;
|
|
||||||
import okhttp3.internal.http.HttpHeaders;
|
|
||||||
import okhttp3.internal.platform.Platform;
|
|
||||||
import okio.Buffer;
|
|
||||||
import okio.BufferedSource;
|
|
||||||
import okio.GzipSource;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* An OkHttp interceptor which logs request and response information. Can be applied as an
|
|
||||||
* {@linkplain OkHttpClient#interceptors() application interceptor} or as a {@linkplain
|
|
||||||
* OkHttpClient#networkInterceptors() network interceptor}. <p> The format of the logs created by
|
|
||||||
* this class should not be considered stable and may change slightly between releases. If you need
|
|
||||||
* a stable logging format, use your own interceptor.
|
|
||||||
*/
|
|
||||||
public final class HttpLoggingInterceptor implements Interceptor {
|
|
||||||
private static final Charset UTF8 = Charset.forName("UTF-8");
|
|
||||||
private final Logger logger;
|
|
||||||
private volatile Set<String> headersToRedact = Collections.emptySet();
|
|
||||||
private volatile Level level = Level.NONE;
|
|
||||||
|
|
||||||
public HttpLoggingInterceptor() {
|
|
||||||
this(Logger.DEFAULT);
|
|
||||||
}
|
|
||||||
|
|
||||||
public HttpLoggingInterceptor(Logger logger) {
|
|
||||||
this.logger = logger;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns true if the body in question probably contains human readable text. Uses a small sample
|
|
||||||
* of code points to detect unicode control characters commonly used in binary file signatures.
|
|
||||||
*/
|
|
||||||
static boolean isPlaintext(Buffer buffer) {
|
|
||||||
try {
|
|
||||||
Buffer prefix = new Buffer();
|
|
||||||
long byteCount = buffer.size() < 64 ? buffer.size() : 64;
|
|
||||||
buffer.copyTo(prefix, 0, byteCount);
|
|
||||||
for (int i = 0; i < 16; i++) {
|
|
||||||
if (prefix.exhausted()) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
int codePoint = prefix.readUtf8CodePoint();
|
|
||||||
if (Character.isISOControl(codePoint) && !Character.isWhitespace(codePoint)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
} catch (EOFException e) {
|
|
||||||
return false; // Truncated UTF-8 sequence.
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static boolean bodyHasUnknownEncoding(Headers headers) {
|
|
||||||
String contentEncoding = headers.get("Content-Encoding");
|
|
||||||
return contentEncoding != null
|
|
||||||
&& !contentEncoding.equalsIgnoreCase("identity")
|
|
||||||
&& !contentEncoding.equalsIgnoreCase("gzip");
|
|
||||||
}
|
|
||||||
|
|
||||||
public void redactHeader(String name) {
|
|
||||||
Set<String> newHeadersToRedact = new TreeSet<>(String.CASE_INSENSITIVE_ORDER);
|
|
||||||
newHeadersToRedact.addAll(headersToRedact);
|
|
||||||
newHeadersToRedact.add(name);
|
|
||||||
headersToRedact = newHeadersToRedact;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Level getLevel() {
|
|
||||||
return level;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Change the level at which this interceptor logs.
|
|
||||||
*/
|
|
||||||
public HttpLoggingInterceptor setLevel(Level level) {
|
|
||||||
if (level == null) throw new NullPointerException("level == null. Use Level.NONE instead.");
|
|
||||||
this.level = level;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
@NonNull
|
|
||||||
@Override
|
|
||||||
public Response intercept(Chain chain) throws IOException {
|
|
||||||
Level level = this.level;
|
|
||||||
|
|
||||||
Request request = chain.request();
|
|
||||||
if (level == Level.NONE) {
|
|
||||||
return chain.proceed(request);
|
|
||||||
}
|
|
||||||
|
|
||||||
boolean logBody = level == Level.BODY;
|
|
||||||
boolean logHeaders = logBody || level == Level.HEADERS;
|
|
||||||
|
|
||||||
synchronized (this) {
|
|
||||||
RequestBody requestBody = request.body();
|
|
||||||
boolean hasRequestBody = requestBody != null;
|
|
||||||
|
|
||||||
Connection connection = chain.connection();
|
|
||||||
String requestStartMessage = "--> "
|
|
||||||
+ request.method()
|
|
||||||
+ ' ' + request.url()
|
|
||||||
+ (connection != null ? " " + connection.protocol() : "");
|
|
||||||
if (!logHeaders && hasRequestBody) {
|
|
||||||
requestStartMessage += " (" + requestBody.contentLength() + "-byte body)";
|
|
||||||
}
|
|
||||||
logger.log(requestStartMessage);
|
|
||||||
|
|
||||||
if (logHeaders) {
|
|
||||||
if (hasRequestBody) {
|
|
||||||
// Request body headers are only present when installed as a network interceptor. Force
|
|
||||||
// them to be included (when available) so there values are known.
|
|
||||||
if (requestBody.contentType() != null) {
|
|
||||||
logger.log("Content-Type: " + requestBody.contentType());
|
|
||||||
}
|
|
||||||
if (requestBody.contentLength() != -1) {
|
|
||||||
logger.log("Content-Length: " + requestBody.contentLength());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Headers headers = request.headers();
|
|
||||||
for (int i = 0, count = headers.size(); i < count; i++) {
|
|
||||||
String name = headers.name(i);
|
|
||||||
// Skip headers from the request body as they are explicitly logged above.
|
|
||||||
if (!"Content-Type".equalsIgnoreCase(name) && !"Content-Length".equalsIgnoreCase(name)) {
|
|
||||||
logHeader(headers, i);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!logBody || !hasRequestBody) {
|
|
||||||
logger.log("--> END " + request.method());
|
|
||||||
} else if (bodyHasUnknownEncoding(request.headers())) {
|
|
||||||
logger.log("--> END " + request.method() + " (encoded body omitted)");
|
|
||||||
} else if (requestBody.isDuplex()) {
|
|
||||||
logger.log("--> END " + request.method() + " (duplex request body omitted)");
|
|
||||||
} else {
|
|
||||||
Buffer buffer = new Buffer();
|
|
||||||
requestBody.writeTo(buffer);
|
|
||||||
|
|
||||||
Charset charset = UTF8;
|
|
||||||
MediaType contentType = requestBody.contentType();
|
|
||||||
if (contentType != null) {
|
|
||||||
charset = contentType.charset(UTF8);
|
|
||||||
}
|
|
||||||
|
|
||||||
logger.log("");
|
|
||||||
if (isPlaintext(buffer)) {
|
|
||||||
logger.log(buffer.readString(charset));
|
|
||||||
logger.log("--> END " + request.method()
|
|
||||||
+ " (" + requestBody.contentLength() + "-byte body)");
|
|
||||||
} else {
|
|
||||||
logger.log("--> END " + request.method() + " (binary "
|
|
||||||
+ requestBody.contentLength() + "-byte body omitted)");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
long startNs = System.nanoTime();
|
|
||||||
Response response;
|
|
||||||
try {
|
|
||||||
response = chain.proceed(request);
|
|
||||||
} catch (Exception e) {
|
|
||||||
logger.log("<-- HTTP FAILED: " + e);
|
|
||||||
throw e;
|
|
||||||
}
|
|
||||||
synchronized (this) {
|
|
||||||
long tookMs = TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - startNs);
|
|
||||||
|
|
||||||
ResponseBody responseBody = response.body();
|
|
||||||
long contentLength = responseBody.contentLength();
|
|
||||||
String bodySize = contentLength != -1 ? contentLength + "-byte" : "unknown-length";
|
|
||||||
logger.log("<-- "
|
|
||||||
+ response.code()
|
|
||||||
+ (response.message().isEmpty() ? "" : ' ' + response.message())
|
|
||||||
+ ' ' + response.request().url()
|
|
||||||
+ " (" + tookMs + "ms" + (!logHeaders ? ", " + bodySize + " body" : "") + ')');
|
|
||||||
|
|
||||||
if (logHeaders) {
|
|
||||||
Headers headers = response.headers();
|
|
||||||
for (int i = 0, count = headers.size(); i < count; i++) {
|
|
||||||
logHeader(headers, i);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!logBody || !HttpHeaders.hasBody(response)) {
|
|
||||||
logger.log("<-- END HTTP");
|
|
||||||
} else if (bodyHasUnknownEncoding(response.headers())) {
|
|
||||||
logger.log("<-- END HTTP (encoded body omitted)");
|
|
||||||
} else {
|
|
||||||
BufferedSource source = responseBody.source();
|
|
||||||
source.request(Long.MAX_VALUE); // Buffer the entire body.
|
|
||||||
Buffer buffer = source.getBuffer();
|
|
||||||
|
|
||||||
Long gzippedLength = null;
|
|
||||||
if ("gzip".equalsIgnoreCase(headers.get("Content-Encoding"))) {
|
|
||||||
gzippedLength = buffer.size();
|
|
||||||
try (GzipSource gzippedResponseBody = new GzipSource(buffer.clone())) {
|
|
||||||
buffer = new Buffer();
|
|
||||||
buffer.writeAll(gzippedResponseBody);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Charset charset = UTF8;
|
|
||||||
MediaType contentType = responseBody.contentType();
|
|
||||||
if (contentType != null) {
|
|
||||||
charset = contentType.charset(UTF8);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!isPlaintext(buffer)) {
|
|
||||||
logger.log("");
|
|
||||||
logger.log("<-- END HTTP (binary " + buffer.size() + "-byte body omitted)");
|
|
||||||
return response;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (contentLength != 0) {
|
|
||||||
logger.log("");
|
|
||||||
logger.log(buffer.clone().readString(charset));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (gzippedLength != null) {
|
|
||||||
logger.log("<-- END HTTP (" + buffer.size() + "-byte, "
|
|
||||||
+ gzippedLength + "-gzipped-byte body)");
|
|
||||||
} else {
|
|
||||||
logger.log("<-- END HTTP (" + buffer.size() + "-byte body)");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return response;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void logHeader(Headers headers, int i) {
|
|
||||||
String value = headersToRedact.contains(headers.name(i)) ? "██" : headers.value(i);
|
|
||||||
logger.log(headers.name(i) + ": " + value);
|
|
||||||
}
|
|
||||||
|
|
||||||
public enum Level {
|
|
||||||
/**
|
|
||||||
* No logs.
|
|
||||||
*/
|
|
||||||
NONE,
|
|
||||||
/**
|
|
||||||
* Logs request and response lines.
|
|
||||||
*
|
|
||||||
* <p>Example:
|
|
||||||
* <pre>{@code
|
|
||||||
* --> POST /greeting http/1.1 (3-byte body)
|
|
||||||
*
|
|
||||||
* <-- 200 OK (22ms, 6-byte body)
|
|
||||||
* }</pre>
|
|
||||||
*/
|
|
||||||
BASIC,
|
|
||||||
/**
|
|
||||||
* Logs request and response lines and their respective headers.
|
|
||||||
*
|
|
||||||
* <p>Example:
|
|
||||||
* <pre>{@code
|
|
||||||
* --> POST /greeting http/1.1
|
|
||||||
* Host: example.com
|
|
||||||
* Content-Type: plain/text
|
|
||||||
* Content-Length: 3
|
|
||||||
* --> END POST
|
|
||||||
*
|
|
||||||
* <-- 200 OK (22ms)
|
|
||||||
* Content-Type: plain/text
|
|
||||||
* Content-Length: 6
|
|
||||||
* <-- END HTTP
|
|
||||||
* }</pre>
|
|
||||||
*/
|
|
||||||
HEADERS,
|
|
||||||
/**
|
|
||||||
* Logs request and response lines and their respective headers and bodies (if present).
|
|
||||||
*
|
|
||||||
* <p>Example:
|
|
||||||
* <pre>{@code
|
|
||||||
* --> POST /greeting http/1.1
|
|
||||||
* Host: example.com
|
|
||||||
* Content-Type: plain/text
|
|
||||||
* Content-Length: 3
|
|
||||||
*
|
|
||||||
* Hi?
|
|
||||||
* --> END POST
|
|
||||||
*
|
|
||||||
* <-- 200 OK (22ms)
|
|
||||||
* Content-Type: plain/text
|
|
||||||
* Content-Length: 6
|
|
||||||
*
|
|
||||||
* Hello!
|
|
||||||
* <-- END HTTP
|
|
||||||
* }</pre>
|
|
||||||
*/
|
|
||||||
BODY
|
|
||||||
}
|
|
||||||
|
|
||||||
public interface Logger {
|
|
||||||
/**
|
|
||||||
* A {@link Logger} defaults output appropriate for the current platform.
|
|
||||||
*/
|
|
||||||
Logger DEFAULT = message -> Platform.get().log(INFO, message, null);
|
|
||||||
|
|
||||||
void log(String message);
|
|
||||||
}
|
|
||||||
}
|
|
@@ -2,13 +2,11 @@ package com.nnbc123.library.net.rxnet.manager;
|
|||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
|
|
||||||
import com.facebook.stetho.okhttp3.StethoInterceptor;
|
|
||||||
import com.google.gson.Gson;
|
import com.google.gson.Gson;
|
||||||
import com.google.gson.GsonBuilder;
|
import com.google.gson.GsonBuilder;
|
||||||
import com.nnbc123.library.BuildConfig;
|
import com.nnbc123.library.BuildConfig;
|
||||||
import com.nnbc123.library.net.rxnet.converter.GsonConverterFactory;
|
import com.nnbc123.library.net.rxnet.converter.GsonConverterFactory;
|
||||||
import com.nnbc123.library.net.rxnet.https.HttpsUtils;
|
import com.nnbc123.library.net.rxnet.https.HttpsUtils;
|
||||||
import com.nnbc123.library.net.rxnet.interceptor.HttpLoggingInterceptor;
|
|
||||||
import com.nnbc123.library.net.rxnet.utils.RxNetLog;
|
import com.nnbc123.library.net.rxnet.utils.RxNetLog;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
@@ -29,6 +27,7 @@ import okhttp3.Cache;
|
|||||||
import okhttp3.ConnectionPool;
|
import okhttp3.ConnectionPool;
|
||||||
import okhttp3.Interceptor;
|
import okhttp3.Interceptor;
|
||||||
import okhttp3.OkHttpClient;
|
import okhttp3.OkHttpClient;
|
||||||
|
import okhttp3.logging.HttpLoggingInterceptor;
|
||||||
import retrofit2.Retrofit;
|
import retrofit2.Retrofit;
|
||||||
import retrofit2.adapter.rxjava2.RxJava2CallAdapterFactory;
|
import retrofit2.adapter.rxjava2.RxJava2CallAdapterFactory;
|
||||||
|
|
||||||
@@ -56,16 +55,9 @@ public final class RxNetManager {
|
|||||||
mBuilder = new OkHttpClient.Builder();
|
mBuilder = new OkHttpClient.Builder();
|
||||||
|
|
||||||
if (RxNetLog.DEBUG) {
|
if (RxNetLog.DEBUG) {
|
||||||
//正式环境千万不要加这玩意,为了方便日志查看,拦截器里面加了synchronized关键字,接口请求是串行的
|
HttpLoggingInterceptor loggingInterceptor = new HttpLoggingInterceptor();
|
||||||
HttpLoggingInterceptor loggingInterceptor = new HttpLoggingInterceptor(new HttpLoggingInterceptor.Logger() {
|
|
||||||
@Override
|
|
||||||
public void log(String message) {
|
|
||||||
RxNetLog.d("OKHttp-------%s", message);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
loggingInterceptor.setLevel(HttpLoggingInterceptor.Level.BODY);
|
loggingInterceptor.setLevel(HttpLoggingInterceptor.Level.BODY);
|
||||||
mBuilder.addInterceptor(loggingInterceptor);
|
mBuilder.addInterceptor(loggingInterceptor);
|
||||||
mBuilder.addNetworkInterceptor(new StethoInterceptor());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for (Interceptor interceptor : interceptors) {
|
for (Interceptor interceptor : interceptors) {
|
||||||
|
@@ -19,6 +19,7 @@ import android.graphics.RectF;
|
|||||||
import android.media.ExifInterface;
|
import android.media.ExifInterface;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
|
|
||||||
|
|
||||||
import com.nnbc123.library.utils.LogUtil;
|
import com.nnbc123.library.utils.LogUtil;
|
||||||
import com.nnbc123.library.utils.StringUtils;
|
import com.nnbc123.library.utils.StringUtils;
|
||||||
import com.nnbc123.library.utils.file.JXFileUtils;
|
import com.nnbc123.library.utils.file.JXFileUtils;
|
||||||
@@ -690,78 +691,79 @@ public class JXImageUtils {
|
|||||||
return baos.toByteArray();
|
return baos.toByteArray();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static CompressResult compressImagePxAndQuality(String inPath, File outDir, String outFileName, int expectWidth, long expectSize) {
|
||||||
|
try {
|
||||||
|
if (outDir == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
if (!outDir.exists()) {
|
||||||
|
//创建目录
|
||||||
|
outDir.mkdirs();
|
||||||
|
}
|
||||||
|
if (!outDir.exists()) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
public static String compressImagePxAndQuality(String inPath, File outDir, String outFileName, int expectWidth, long expectSize) {
|
//读取原图的旋转角度,并写入到压缩图片中
|
||||||
try {
|
ExifInterface exif = new ExifInterface(inPath);
|
||||||
if (outDir == null) {
|
String orientation = exif.getAttribute(ExifInterface.TAG_ORIENTATION);
|
||||||
return null;
|
LogUtil.print("原图:" + inPath);
|
||||||
}
|
// LogUtil.print("原图旋转角度:" + getPicRotate(inPath));
|
||||||
if (!outDir.exists()) {
|
|
||||||
//创建目录
|
|
||||||
outDir.mkdirs();
|
|
||||||
}
|
|
||||||
if (!outDir.exists()) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
//读取原图的旋转角度,并写入到压缩图片中
|
BitmapFactory.Options bmOptions = new BitmapFactory.Options();
|
||||||
ExifInterface exif = new ExifInterface(inPath);
|
bmOptions.inPreferredConfig = Config.RGB_565;
|
||||||
String orientation = exif.getAttribute(ExifInterface.TAG_ORIENTATION);
|
bmOptions.inSampleSize = getExpectInSampleSize(inPath, expectWidth);
|
||||||
LogUtil.print("原图:" + inPath);
|
Bitmap bitmap = BitmapFactory.decodeFile(inPath, bmOptions);
|
||||||
LogUtil.print("原图旋转角度:" + getPicRotate(inPath));
|
|
||||||
|
|
||||||
BitmapFactory.Options bmOptions = new BitmapFactory.Options();
|
LogUtil.print("宽高," + bitmap.getWidth() + "," + bitmap.getHeight());
|
||||||
bmOptions.inPreferredConfig = Config.RGB_565;
|
|
||||||
bmOptions.inSampleSize = getExpectInSampleSize(inPath, expectWidth);
|
|
||||||
Bitmap bitmap = BitmapFactory.decodeFile(inPath, bmOptions);
|
|
||||||
|
|
||||||
LogUtil.print("宽高," + bitmap.getWidth() + "," + bitmap.getHeight());
|
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||||
|
int options = 100;
|
||||||
|
int minQuality = 20;
|
||||||
|
bitmap.compress(Bitmap.CompressFormat.JPEG, options, baos);//质量压缩方法,把压缩后的数据存放到baos中 (100表示不压缩,0表示压缩到最小)
|
||||||
|
Log.e("mouse_debug", "压缩前质量:" + baos.toByteArray().length);
|
||||||
|
while (baos.toByteArray().length > expectSize) {//循环判断如果压缩后图片是否大于指定大小,大于继续压缩
|
||||||
|
baos.reset();//重置baos即让下一次的写入覆盖之前的内容
|
||||||
|
options -= 5;//图片质量每次减少5
|
||||||
|
|
||||||
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
if (options <= minQuality) options = minQuality;//如果图片质量小于5,为保证压缩后的图片质量,图片最底压缩质量为5
|
||||||
int options = 100;
|
LogUtil.print("压缩参数:" + options);
|
||||||
int minQuality = 20;
|
bitmap.compress(Bitmap.CompressFormat.JPEG, options, baos);//将压缩后的图片保存到baos中
|
||||||
bitmap.compress(Bitmap.CompressFormat.JPEG, options, baos);//质量压缩方法,把压缩后的数据存放到baos中 (100表示不压缩,0表示压缩到最小)
|
if (options == minQuality) break;//如果图片的质量已降到最低则,不再进行压缩
|
||||||
Log.e("mouse_debug", "压缩前质量:" + baos.toByteArray().length);
|
}
|
||||||
while (baos.toByteArray().length > expectSize) {//循环判断如果压缩后图片是否大于指定大小,大于继续压缩
|
if (!bitmap.isRecycled()) {
|
||||||
baos.reset();//重置baos即让下一次的写入覆盖之前的内容
|
bitmap.recycle();//回收内存中的图片
|
||||||
options -= 5;//图片质量每次减少5
|
}
|
||||||
|
|
||||||
if (options <= minQuality) options = minQuality;//如果图片质量小于5,为保证压缩后的图片质量,图片最底压缩质量为5
|
File thumbnailFile = new File(outDir, outFileName);
|
||||||
LogUtil.print("压缩参数:" + options);
|
if (thumbnailFile.exists()) {
|
||||||
bitmap.compress(Bitmap.CompressFormat.JPEG, options, baos);//将压缩后的图片保存到baos中
|
thumbnailFile.delete();
|
||||||
if (options == minQuality) break;//如果图片的质量已降到最低则,不再进行压缩
|
}
|
||||||
}
|
thumbnailFile.createNewFile();
|
||||||
if (!bitmap.isRecycled()) {
|
FileOutputStream fos = new FileOutputStream(thumbnailFile);//将压缩后的图片保存的本地上指定路径中
|
||||||
bitmap.recycle();//回收内存中的图片
|
fos.write(baos.toByteArray());
|
||||||
}
|
fos.flush();
|
||||||
|
fos.close();
|
||||||
File thumbnailFile = new File(outDir, outFileName);
|
if (thumbnailFile.exists()) {
|
||||||
if (thumbnailFile.exists()) {
|
String compressPath = thumbnailFile.getPath();
|
||||||
thumbnailFile.delete();
|
exif = new ExifInterface(compressPath);
|
||||||
}
|
if (orientation != null) {
|
||||||
thumbnailFile.createNewFile();
|
exif.setAttribute(ExifInterface.TAG_ORIENTATION, orientation);
|
||||||
FileOutputStream fos = new FileOutputStream(thumbnailFile);//将压缩后的图片保存的本地上指定路径中
|
exif.saveAttributes();
|
||||||
fos.write(baos.toByteArray());
|
}
|
||||||
fos.flush();
|
int width = exif.getAttributeInt(ExifInterface.TAG_IMAGE_WIDTH, 0);
|
||||||
fos.close();
|
int height = exif.getAttributeInt(ExifInterface.TAG_IMAGE_LENGTH, 0);
|
||||||
if (thumbnailFile.exists()) {
|
// LogUtil.print("压缩图旋转角度:" + getPicRotate(compressPath));
|
||||||
String compressPath = thumbnailFile.getPath();
|
return new CompressResult(compressPath, width, height, "image/jpeg");
|
||||||
if (orientation != null) {
|
}
|
||||||
exif = new ExifInterface(compressPath);
|
} catch (Exception ex) {
|
||||||
exif.setAttribute(ExifInterface.TAG_ORIENTATION, orientation);
|
Log.e("mouse_debug", "压缩失败...");
|
||||||
exif.saveAttributes();
|
ex.printStackTrace();
|
||||||
}
|
} catch (OutOfMemoryError error) {
|
||||||
LogUtil.print("压缩图旋转角度:" + getPicRotate(compressPath));
|
LogUtil.print("压缩图片OOM了");
|
||||||
return compressPath;
|
}
|
||||||
}
|
return null;
|
||||||
} catch (Exception ex) {
|
}
|
||||||
Log.e("mouse_debug", "压缩失败...");
|
|
||||||
ex.printStackTrace();
|
|
||||||
} catch (OutOfMemoryError error) {
|
|
||||||
LogUtil.print("压缩图片OOM了");
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static int getExpectInSampleSize(String path, int expectWidth) {
|
public static int getExpectInSampleSize(String path, int expectWidth) {
|
||||||
//先获取图片旋转角度,如果是90或者270°,则以高作为参考值,否则以宽作为参考值
|
//先获取图片旋转角度,如果是90或者270°,则以高作为参考值,否则以宽作为参考值
|
||||||
@@ -814,5 +816,33 @@ public class JXImageUtils {
|
|||||||
return degree;
|
return degree;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static class CompressResult {
|
||||||
|
private String path;
|
||||||
|
private int width;
|
||||||
|
private int height;
|
||||||
|
private String format;
|
||||||
|
|
||||||
|
public CompressResult(String path, int width, int height, String format) {
|
||||||
|
this.path = path;
|
||||||
|
this.width = width;
|
||||||
|
this.height = height;
|
||||||
|
this.format = format;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getPath() {
|
||||||
|
return path;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getWidth() {
|
||||||
|
return width;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getHeight() {
|
||||||
|
return height;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getFormat() {
|
||||||
|
return format;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@@ -5,8 +5,9 @@ import android.net.Uri;
|
|||||||
import android.os.Environment;
|
import android.os.Environment;
|
||||||
import android.text.TextUtils;
|
import android.text.TextUtils;
|
||||||
|
|
||||||
|
import androidx.core.text.TextUtilsCompat;
|
||||||
|
|
||||||
import com.nnbc123.library.utils.FP;
|
import com.nnbc123.library.utils.FP;
|
||||||
import com.qiniu.android.utils.StringUtils;
|
|
||||||
import com.nnbc123.library.common.application.BaseApp;
|
import com.nnbc123.library.common.application.BaseApp;
|
||||||
import com.nnbc123.library.common.util.Logger;
|
import com.nnbc123.library.common.util.Logger;
|
||||||
|
|
||||||
@@ -877,41 +878,4 @@ public class FileHelper {
|
|||||||
}
|
}
|
||||||
return content;
|
return content;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* 文件转换成字符串
|
|
||||||
*
|
|
||||||
* @param filePath 文件路径
|
|
||||||
* @return 字符串内容
|
|
||||||
*/
|
|
||||||
public static String getTxtFileContent(String filePath) {
|
|
||||||
String content = "";
|
|
||||||
if (!StringUtils.isNullOrEmpty(filePath)) {
|
|
||||||
File file = new File(filePath);
|
|
||||||
if (file.isFile()) {
|
|
||||||
FileInputStream inputStream = null;
|
|
||||||
try {
|
|
||||||
inputStream = new FileInputStream(file);
|
|
||||||
String line;
|
|
||||||
StringBuilder sb = new StringBuilder();
|
|
||||||
BufferedReader buffReader = new BufferedReader(new InputStreamReader(inputStream));
|
|
||||||
while ((line = buffReader.readLine()) != null) {
|
|
||||||
sb.append(line).append("\n");
|
|
||||||
}
|
|
||||||
content = sb.toString();
|
|
||||||
} catch (Exception e) {
|
|
||||||
Logger.error(TAG, "getTxtFileContent read fail, e = " + e);
|
|
||||||
} finally {
|
|
||||||
if (inputStream != null) {
|
|
||||||
try {
|
|
||||||
inputStream.close();
|
|
||||||
} catch (Exception ignore) {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return content;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@@ -45,451 +45,475 @@ import java.io.File
|
|||||||
* 路径 工具类 By https://github.com/Blankj/AndroidUtilCode -> PathUtils.java
|
* 路径 工具类 By https://github.com/Blankj/AndroidUtilCode -> PathUtils.java
|
||||||
* Created by Max on 2018/12/12.
|
* Created by Max on 2018/12/12.
|
||||||
*/
|
*/
|
||||||
class PathUtils private constructor() {
|
object PathUtils {
|
||||||
init {
|
|
||||||
throw UnsupportedOperationException("u can't instantiate me...")
|
/**
|
||||||
|
* Return the path of /system.
|
||||||
|
*
|
||||||
|
* @return the path of /system
|
||||||
|
*/
|
||||||
|
val rootPath: String
|
||||||
|
get() = Environment.getRootDirectory().absolutePath
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the path of /data.
|
||||||
|
*
|
||||||
|
* @return the path of /data
|
||||||
|
*/
|
||||||
|
val dataPath: String
|
||||||
|
get() = Environment.getDataDirectory().absolutePath
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the path of /cache.
|
||||||
|
*
|
||||||
|
* @return the path of /cache
|
||||||
|
*/
|
||||||
|
val downloadCachePath: String
|
||||||
|
get() = Environment.getDownloadCacheDirectory().absolutePath
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the path of /data/data/package.
|
||||||
|
*
|
||||||
|
* @return the path of /data/data/package
|
||||||
|
*/
|
||||||
|
fun getInternalAppDataPath(application: Application): String {
|
||||||
|
return if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N) {
|
||||||
|
application.applicationInfo.dataDir
|
||||||
|
} else application.dataDir.absolutePath
|
||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
/**
|
||||||
/**
|
* Return the path of /data/data/package/code_cache.
|
||||||
* Return the path of /system.
|
*
|
||||||
*
|
* @return the path of /data/data/package/code_cache
|
||||||
* @return the path of /system
|
*/
|
||||||
*/
|
fun getInternalAppCodeCacheDir(application: Application): String {
|
||||||
val rootPath: String
|
return if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
|
||||||
get() = Environment.getRootDirectory().absolutePath
|
application.applicationInfo.dataDir + "/code_cache"
|
||||||
|
} else application.codeCacheDir.absolutePath
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return the path of /data.
|
* Return the path of /data/data/package/cache.
|
||||||
*
|
*
|
||||||
* @return the path of /data
|
* @return the path of /data/data/package/cache
|
||||||
*/
|
*/
|
||||||
val dataPath: String
|
fun getInternalAppCachePath(application: Application): String {
|
||||||
get() = Environment.getDataDirectory().absolutePath
|
return application.cacheDir.absolutePath
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return the path of /cache.
|
* Return the path of /data/data/package/databases.
|
||||||
*
|
*
|
||||||
* @return the path of /cache
|
* @return the path of /data/data/package/databases
|
||||||
*/
|
*/
|
||||||
val downloadCachePath: String
|
fun getInternalAppDbsPath(application: Application): String {
|
||||||
get() = Environment.getDownloadCacheDirectory().absolutePath
|
return application.applicationInfo.dataDir + "/databases"
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return the path of /data/data/package.
|
* Return the path of /data/data/package/databases/name.
|
||||||
*
|
*
|
||||||
* @return the path of /data/data/package
|
* @param name The name of database.
|
||||||
*/
|
* @return the path of /data/data/package/databases/name
|
||||||
fun getInternalAppDataPath(application: Application): String {
|
*/
|
||||||
return if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N) {
|
fun getInternalAppDbPath(application: Application, name: String?): String {
|
||||||
application.applicationInfo.dataDir
|
return application.getDatabasePath(name).absolutePath
|
||||||
} else application.dataDir.absolutePath
|
}
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return the path of /data/data/package/code_cache.
|
* Return the path of /data/data/package/files.
|
||||||
*
|
*
|
||||||
* @return the path of /data/data/package/code_cache
|
* @return the path of /data/data/package/files
|
||||||
*/
|
*/
|
||||||
fun getInternalAppCodeCacheDir(application: Application): String {
|
fun getInternalAppFilesPath(application: Application): String {
|
||||||
return if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
|
return application.filesDir.absolutePath
|
||||||
application.applicationInfo.dataDir + "/code_cache"
|
}
|
||||||
} else application.codeCacheDir.absolutePath
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return the path of /data/data/package/cache.
|
* Return the path of /data/data/package/shared_prefs.
|
||||||
*
|
*
|
||||||
* @return the path of /data/data/package/cache
|
* @return the path of /data/data/package/shared_prefs
|
||||||
*/
|
*/
|
||||||
fun getInternalAppCachePath(application: Application): String {
|
fun getInternalAppSpPath(application: Application): String {
|
||||||
return application.cacheDir.absolutePath
|
return application.applicationInfo.dataDir + "shared_prefs"
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return the path of /data/data/package/databases.
|
* Return the path of /data/data/package/no_backup.
|
||||||
*
|
*
|
||||||
* @return the path of /data/data/package/databases
|
* @return the path of /data/data/package/no_backup
|
||||||
*/
|
*/
|
||||||
fun getInternalAppDbsPath(application: Application): String {
|
fun getInternalAppNoBackupFilesPath(application: Application): String {
|
||||||
return application.applicationInfo.dataDir + "/databases"
|
return if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
|
||||||
}
|
application.applicationInfo.dataDir + "no_backup"
|
||||||
|
} else application.noBackupFilesDir.absolutePath
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return the path of /data/data/package/databases/name.
|
* Return the path of /storage/emulated/0.
|
||||||
*
|
*
|
||||||
* @param name The name of database.
|
* @return the path of /storage/emulated/0
|
||||||
* @return the path of /data/data/package/databases/name
|
*/
|
||||||
*/
|
val externalStoragePath: String?
|
||||||
fun getInternalAppDbPath(application: Application, name: String?): String {
|
get() = if (isExternalStorageDisable) null else Environment.getExternalStorageDirectory().absolutePath
|
||||||
return application.getDatabasePath(name).absolutePath
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return the path of /data/data/package/files.
|
* Return the path of /storage/emulated/0/Music.
|
||||||
*
|
*
|
||||||
* @return the path of /data/data/package/files
|
* @return the path of /storage/emulated/0/Music
|
||||||
*/
|
*/
|
||||||
fun getInternalAppFilesPath(application: Application): String {
|
val externalMusicPath: String?
|
||||||
return application.filesDir.absolutePath
|
get() = if (isExternalStorageDisable) null else Environment.getExternalStoragePublicDirectory(
|
||||||
}
|
Environment.DIRECTORY_MUSIC
|
||||||
|
).absolutePath
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return the path of /data/data/package/shared_prefs.
|
* Return the path of /storage/emulated/0/Podcasts.
|
||||||
*
|
*
|
||||||
* @return the path of /data/data/package/shared_prefs
|
* @return the path of /storage/emulated/0/Podcasts
|
||||||
*/
|
*/
|
||||||
fun getInternalAppSpPath(application: Application): String {
|
val externalPodcastsPath: String?
|
||||||
return application.applicationInfo.dataDir + "shared_prefs"
|
get() = if (isExternalStorageDisable) null else Environment.getExternalStoragePublicDirectory(
|
||||||
}
|
Environment.DIRECTORY_PODCASTS
|
||||||
|
).absolutePath
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return the path of /data/data/package/no_backup.
|
* Return the path of /storage/emulated/0/Ringtones.
|
||||||
*
|
*
|
||||||
* @return the path of /data/data/package/no_backup
|
* @return the path of /storage/emulated/0/Ringtones
|
||||||
*/
|
*/
|
||||||
fun getInternalAppNoBackupFilesPath(application: Application): String {
|
val externalRingtonesPath: String?
|
||||||
return if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
|
get() = if (isExternalStorageDisable) null else Environment.getExternalStoragePublicDirectory(
|
||||||
application.applicationInfo.dataDir + "no_backup"
|
Environment.DIRECTORY_RINGTONES
|
||||||
} else application.noBackupFilesDir.absolutePath
|
).absolutePath
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return the path of /storage/emulated/0.
|
* Return the path of /storage/emulated/0/Alarms.
|
||||||
*
|
*
|
||||||
* @return the path of /storage/emulated/0
|
* @return the path of /storage/emulated/0/Alarms
|
||||||
*/
|
*/
|
||||||
val externalStoragePath: String?
|
val externalAlarmsPath: String?
|
||||||
get() = if (isExternalStorageDisable) null else Environment.getExternalStorageDirectory().absolutePath
|
get() = if (isExternalStorageDisable) null else Environment.getExternalStoragePublicDirectory(
|
||||||
|
Environment.DIRECTORY_ALARMS
|
||||||
|
).absolutePath
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return the path of /storage/emulated/0/Music.
|
* Return the path of /storage/emulated/0/Notifications.
|
||||||
*
|
*
|
||||||
* @return the path of /storage/emulated/0/Music
|
* @return the path of /storage/emulated/0/Notifications
|
||||||
*/
|
*/
|
||||||
val externalMusicPath: String?
|
val externalNotificationsPath: String?
|
||||||
get() = if (isExternalStorageDisable) null else Environment.getExternalStoragePublicDirectory(
|
get() = if (isExternalStorageDisable) null else Environment.getExternalStoragePublicDirectory(
|
||||||
Environment.DIRECTORY_MUSIC
|
Environment.DIRECTORY_NOTIFICATIONS
|
||||||
).absolutePath
|
).absolutePath
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return the path of /storage/emulated/0/Podcasts.
|
* Return the path of /storage/emulated/0/Pictures.
|
||||||
*
|
*
|
||||||
* @return the path of /storage/emulated/0/Podcasts
|
* @return the path of /storage/emulated/0/Pictures
|
||||||
*/
|
*/
|
||||||
val externalPodcastsPath: String?
|
val externalPicturesPath: String?
|
||||||
get() = if (isExternalStorageDisable) null else Environment.getExternalStoragePublicDirectory(
|
get() = if (isExternalStorageDisable) null else Environment.getExternalStoragePublicDirectory(
|
||||||
Environment.DIRECTORY_PODCASTS
|
Environment.DIRECTORY_PICTURES
|
||||||
).absolutePath
|
).absolutePath
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return the path of /storage/emulated/0/Ringtones.
|
* Return the path of /storage/emulated/0/Movies.
|
||||||
*
|
*
|
||||||
* @return the path of /storage/emulated/0/Ringtones
|
* @return the path of /storage/emulated/0/Movies
|
||||||
*/
|
*/
|
||||||
val externalRingtonesPath: String?
|
val externalMoviesPath: String?
|
||||||
get() = if (isExternalStorageDisable) null else Environment.getExternalStoragePublicDirectory(
|
get() = if (isExternalStorageDisable) null else Environment.getExternalStoragePublicDirectory(
|
||||||
Environment.DIRECTORY_RINGTONES
|
Environment.DIRECTORY_MOVIES
|
||||||
).absolutePath
|
).absolutePath
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return the path of /storage/emulated/0/Alarms.
|
* Return the path of /storage/emulated/0/Download.
|
||||||
*
|
*
|
||||||
* @return the path of /storage/emulated/0/Alarms
|
* @return the path of /storage/emulated/0/Download
|
||||||
*/
|
*/
|
||||||
val externalAlarmsPath: String?
|
val externalDownloadsPath: String?
|
||||||
get() = if (isExternalStorageDisable) null else Environment.getExternalStoragePublicDirectory(
|
get() = if (isExternalStorageDisable) null else Environment.getExternalStoragePublicDirectory(
|
||||||
Environment.DIRECTORY_ALARMS
|
Environment.DIRECTORY_DOWNLOADS
|
||||||
).absolutePath
|
).absolutePath
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return the path of /storage/emulated/0/Notifications.
|
* Return the path of /storage/emulated/0/DCIM.
|
||||||
*
|
*
|
||||||
* @return the path of /storage/emulated/0/Notifications
|
* @return the path of /storage/emulated/0/DCIM
|
||||||
*/
|
*/
|
||||||
val externalNotificationsPath: String?
|
val externalDcimPath: String?
|
||||||
get() = if (isExternalStorageDisable) null else Environment.getExternalStoragePublicDirectory(
|
get() = if (isExternalStorageDisable) null else Environment.getExternalStoragePublicDirectory(
|
||||||
Environment.DIRECTORY_NOTIFICATIONS
|
Environment.DIRECTORY_DCIM
|
||||||
).absolutePath
|
).absolutePath
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return the path of /storage/emulated/0/Pictures.
|
* Return the path of /storage/emulated/0/Documents.
|
||||||
*
|
*
|
||||||
* @return the path of /storage/emulated/0/Pictures
|
* @return the path of /storage/emulated/0/Documents
|
||||||
*/
|
*/
|
||||||
val externalPicturesPath: String?
|
val externalDocumentsPath: String?
|
||||||
get() = if (isExternalStorageDisable) null else Environment.getExternalStoragePublicDirectory(
|
get() {
|
||||||
Environment.DIRECTORY_PICTURES
|
|
||||||
).absolutePath
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return the path of /storage/emulated/0/Movies.
|
|
||||||
*
|
|
||||||
* @return the path of /storage/emulated/0/Movies
|
|
||||||
*/
|
|
||||||
val externalMoviesPath: String?
|
|
||||||
get() = if (isExternalStorageDisable) null else Environment.getExternalStoragePublicDirectory(
|
|
||||||
Environment.DIRECTORY_MOVIES
|
|
||||||
).absolutePath
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return the path of /storage/emulated/0/Download.
|
|
||||||
*
|
|
||||||
* @return the path of /storage/emulated/0/Download
|
|
||||||
*/
|
|
||||||
val externalDownloadsPath: String?
|
|
||||||
get() = if (isExternalStorageDisable) null else Environment.getExternalStoragePublicDirectory(
|
|
||||||
Environment.DIRECTORY_DOWNLOADS
|
|
||||||
).absolutePath
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return the path of /storage/emulated/0/DCIM.
|
|
||||||
*
|
|
||||||
* @return the path of /storage/emulated/0/DCIM
|
|
||||||
*/
|
|
||||||
val externalDcimPath: String?
|
|
||||||
get() = if (isExternalStorageDisable) null else Environment.getExternalStoragePublicDirectory(
|
|
||||||
Environment.DIRECTORY_DCIM
|
|
||||||
).absolutePath
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return the path of /storage/emulated/0/Documents.
|
|
||||||
*
|
|
||||||
* @return the path of /storage/emulated/0/Documents
|
|
||||||
*/
|
|
||||||
val externalDocumentsPath: String?
|
|
||||||
get() {
|
|
||||||
if (isExternalStorageDisable) return null
|
|
||||||
return if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) {
|
|
||||||
Environment.getExternalStorageDirectory().absolutePath + "/Documents"
|
|
||||||
} else Environment.getExternalStoragePublicDirectory(
|
|
||||||
Environment.DIRECTORY_DOCUMENTS
|
|
||||||
).absolutePath
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return the path of /storage/emulated/0/Android/data/package.
|
|
||||||
*
|
|
||||||
* @return the path of /storage/emulated/0/Android/data/package
|
|
||||||
*/
|
|
||||||
fun getExternalAppDataPath(application: Application): String? {
|
|
||||||
return if (isExternalStorageDisable) null else application.externalCacheDir?.parentFile?.absolutePath
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return the path of /storage/emulated/0/Android/data/package/cache.
|
|
||||||
*
|
|
||||||
* @return the path of /storage/emulated/0/Android/data/package/cache
|
|
||||||
*/
|
|
||||||
fun getExternalAppCachePath(application: Application): String? {
|
|
||||||
return if (isExternalStorageDisable) null else application.externalCacheDir?.absolutePath
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return the path of /storage/emulated/0/Android/data/package/files.
|
|
||||||
*
|
|
||||||
* @return the path of /storage/emulated/0/Android/data/package/files
|
|
||||||
*/
|
|
||||||
fun getExternalAppFilesPath(application: Application): String? {
|
|
||||||
return if (isExternalStorageDisable) null else application.getExternalFilesDir(null)?.absolutePath
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return the path of /storage/emulated/0/Android/data/package/files/Music.
|
|
||||||
*
|
|
||||||
* @return the path of /storage/emulated/0/Android/data/package/files/Music
|
|
||||||
*/
|
|
||||||
fun getExternalAppMusicPath(application: Application): String? {
|
|
||||||
return if (isExternalStorageDisable) null else application.getExternalFilesDir(
|
|
||||||
Environment.DIRECTORY_MUSIC
|
|
||||||
)?.absolutePath
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return the path of /storage/emulated/0/Android/data/package/files/Podcasts.
|
|
||||||
*
|
|
||||||
* @return the path of /storage/emulated/0/Android/data/package/files/Podcasts
|
|
||||||
*/
|
|
||||||
fun getExternalAppPodcastsPath(application: Application): String? {
|
|
||||||
return if (isExternalStorageDisable) null else application.getExternalFilesDir(
|
|
||||||
Environment.DIRECTORY_PODCASTS
|
|
||||||
)?.absolutePath
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return the path of /storage/emulated/0/Android/data/package/files/Ringtones.
|
|
||||||
*
|
|
||||||
* @return the path of /storage/emulated/0/Android/data/package/files/Ringtones
|
|
||||||
*/
|
|
||||||
fun getExternalAppRingtonesPath(application: Application): String? {
|
|
||||||
return if (isExternalStorageDisable) null else application.getExternalFilesDir(
|
|
||||||
Environment.DIRECTORY_RINGTONES
|
|
||||||
)?.absolutePath
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return the path of /storage/emulated/0/Android/data/package/files/Alarms.
|
|
||||||
*
|
|
||||||
* @return the path of /storage/emulated/0/Android/data/package/files/Alarms
|
|
||||||
*/
|
|
||||||
fun getExternalAppAlarmsPath(application: Application): String? {
|
|
||||||
return if (isExternalStorageDisable) null else application.getExternalFilesDir(
|
|
||||||
Environment.DIRECTORY_ALARMS
|
|
||||||
)?.absolutePath
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return the path of /storage/emulated/0/Android/data/package/files/Notifications.
|
|
||||||
*
|
|
||||||
* @return the path of /storage/emulated/0/Android/data/package/files/Notifications
|
|
||||||
*/
|
|
||||||
fun getExternalAppNotificationsPath(application: Application): String? {
|
|
||||||
return if (isExternalStorageDisable) null else application.getExternalFilesDir(
|
|
||||||
Environment.DIRECTORY_NOTIFICATIONS
|
|
||||||
)?.absolutePath
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return the path of /storage/emulated/0/Android/data/package/files/Pictures.
|
|
||||||
*
|
|
||||||
* @return path of /storage/emulated/0/Android/data/package/files/Pictures
|
|
||||||
*/
|
|
||||||
fun getExternalAppPicturesPath(application: Application): String? {
|
|
||||||
return if (isExternalStorageDisable) null else application.getExternalFilesDir(
|
|
||||||
Environment.DIRECTORY_PICTURES
|
|
||||||
)?.absolutePath
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return the path of /storage/emulated/0/Android/data/package/files/Movies.
|
|
||||||
*
|
|
||||||
* @return the path of /storage/emulated/0/Android/data/package/files/Movies
|
|
||||||
*/
|
|
||||||
fun getExternalAppMoviesPath(application: Application): String? {
|
|
||||||
return if (isExternalStorageDisable) null else application.getExternalFilesDir(
|
|
||||||
Environment.DIRECTORY_MOVIES
|
|
||||||
)?.absolutePath
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return the path of /storage/emulated/0/Android/data/package/files/Download.
|
|
||||||
*
|
|
||||||
* @return the path of /storage/emulated/0/Android/data/package/files/Download
|
|
||||||
*/
|
|
||||||
fun getExternalAppDownloadPath(application: Application): String? {
|
|
||||||
return if (isExternalStorageDisable) null else application.getExternalFilesDir(
|
|
||||||
Environment.DIRECTORY_DOWNLOADS
|
|
||||||
)?.absolutePath
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return the path of /storage/emulated/0/Android/data/package/files/DCIM.
|
|
||||||
*
|
|
||||||
* @return the path of /storage/emulated/0/Android/data/package/files/DCIM
|
|
||||||
*/
|
|
||||||
fun getExternalAppDcimPath(application: Application): String? {
|
|
||||||
return if (isExternalStorageDisable) null else application.getExternalFilesDir(
|
|
||||||
Environment.DIRECTORY_DCIM
|
|
||||||
)?.absolutePath
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return the path of /storage/emulated/0/Android/data/package/files/Documents.
|
|
||||||
*
|
|
||||||
* @return the path of /storage/emulated/0/Android/data/package/files/Documents
|
|
||||||
*/
|
|
||||||
fun getExternalAppDocumentsPath(application: Application): String? {
|
|
||||||
if (isExternalStorageDisable) return null
|
if (isExternalStorageDisable) return null
|
||||||
return if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) {
|
return if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) {
|
||||||
application.getExternalFilesDir(null)?.absolutePath + "/Documents"
|
Environment.getExternalStorageDirectory().absolutePath + "/Documents"
|
||||||
} else application.getExternalFilesDir(
|
} else Environment.getExternalStoragePublicDirectory(
|
||||||
Environment.DIRECTORY_DOCUMENTS
|
Environment.DIRECTORY_DOCUMENTS
|
||||||
)?.absolutePath
|
).absolutePath
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return the path of /storage/emulated/0/Android/obb/package.
|
* Return the path of /storage/emulated/0/Android/data/package.
|
||||||
*
|
*
|
||||||
* @return the path of /storage/emulated/0/Android/obb/package
|
* @return the path of /storage/emulated/0/Android/data/package
|
||||||
*/
|
*/
|
||||||
fun getExternalAppObbPath(application: Application): String? {
|
fun getExternalAppDataPath(application: Application): String? {
|
||||||
return if (isExternalStorageDisable) null else application.obbDir.absolutePath
|
return if (isExternalStorageDisable) null else application.externalCacheDir?.parentFile?.absolutePath
|
||||||
}
|
}
|
||||||
|
|
||||||
private val isExternalStorageDisable: Boolean
|
/**
|
||||||
private get() = Environment.MEDIA_MOUNTED != Environment.getExternalStorageState()
|
* Return the path of /storage/emulated/0/Android/data/package/cache.
|
||||||
|
*
|
||||||
|
* @return the path of /storage/emulated/0/Android/data/package/cache
|
||||||
|
*/
|
||||||
|
fun getExternalAppCachePath(application: Application): String? {
|
||||||
|
return if (isExternalStorageDisable) null else application.externalCacheDir?.absolutePath
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 判断sub是否在parent之下的文件或子文件夹<br></br>
|
* Return the path of /storage/emulated/0/Android/data/package/files.
|
||||||
*
|
*
|
||||||
* @param parent
|
* @return the path of /storage/emulated/0/Android/data/package/files
|
||||||
* @param sub
|
*/
|
||||||
* @return
|
fun getExternalAppFilesPath(application: Application): String? {
|
||||||
*/
|
return if (isExternalStorageDisable) null else application.getExternalFilesDir(null)?.absolutePath
|
||||||
fun isSub(parent: File, sub: File): Boolean {
|
}
|
||||||
return try {
|
|
||||||
sub.absolutePath.startsWith(parent.absolutePath)
|
|
||||||
} catch (e: Exception) {
|
|
||||||
false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取子绝对路径与父绝对路径的相对路径
|
* Return the path of /storage/emulated/0/Android/data/package/files/Music.
|
||||||
*
|
*
|
||||||
* @param parentPath
|
* @return the path of /storage/emulated/0/Android/data/package/files/Music
|
||||||
* @param subPath
|
*/
|
||||||
* @return
|
fun getExternalAppMusicPath(application: Application): String? {
|
||||||
*/
|
return if (isExternalStorageDisable) null else application.getExternalFilesDir(
|
||||||
fun getRelativePath(parentPath: String?, subPath: String?): String? {
|
Environment.DIRECTORY_MUSIC
|
||||||
return try {
|
)?.absolutePath
|
||||||
if (parentPath == null || subPath == null) {
|
}
|
||||||
return null
|
|
||||||
}
|
|
||||||
if (subPath.startsWith(parentPath)) {
|
|
||||||
subPath.substring(parentPath.length)
|
|
||||||
} else {
|
|
||||||
null
|
|
||||||
}
|
|
||||||
} catch (e: Exception) {
|
|
||||||
null
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 拼接两个路径
|
* Return the path of /storage/emulated/0/Android/data/package/files/Podcasts.
|
||||||
*
|
*
|
||||||
* @param pathA 路径A
|
* @return the path of /storage/emulated/0/Android/data/package/files/Podcasts
|
||||||
* @param pathB 路径B
|
*/
|
||||||
* @return 拼接后的路径
|
fun getExternalAppPodcastsPath(application: Application): String? {
|
||||||
*/
|
return if (isExternalStorageDisable) null else application.getExternalFilesDir(
|
||||||
fun plusPath(pathA: String?, pathB: String?): String? {
|
Environment.DIRECTORY_PODCASTS
|
||||||
if (pathA == null) {
|
)?.absolutePath
|
||||||
return pathB
|
}
|
||||||
}
|
|
||||||
if (pathB == null) {
|
|
||||||
return pathA
|
|
||||||
}
|
|
||||||
return plusPathNotNull(pathA, pathB)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 拼接两个路径
|
* Return the path of /storage/emulated/0/Android/data/package/files/Ringtones.
|
||||||
*
|
*
|
||||||
* @param pathA 路径A
|
* @return the path of /storage/emulated/0/Android/data/package/files/Ringtones
|
||||||
* @param pathB 路径B
|
*/
|
||||||
* @return 拼接后的路径
|
fun getExternalAppRingtonesPath(application: Application): String? {
|
||||||
*/
|
return if (isExternalStorageDisable) null else application.getExternalFilesDir(
|
||||||
fun plusPathNotNull(pathA: String, pathB: String): String {
|
Environment.DIRECTORY_RINGTONES
|
||||||
val pathAEndSeparator = pathA.endsWith(File.separator)
|
)?.absolutePath
|
||||||
val pathBStartSeparator = pathB.startsWith(File.separator)
|
}
|
||||||
return if (pathAEndSeparator && pathBStartSeparator) {
|
|
||||||
pathA + pathB.substring(1)
|
/**
|
||||||
} else if (pathAEndSeparator || pathBStartSeparator) {
|
* Return the path of /storage/emulated/0/Android/data/package/files/Alarms.
|
||||||
pathA + pathB
|
*
|
||||||
} else {
|
* @return the path of /storage/emulated/0/Android/data/package/files/Alarms
|
||||||
pathA + File.separator + pathB
|
*/
|
||||||
}
|
fun getExternalAppAlarmsPath(application: Application): String? {
|
||||||
|
return if (isExternalStorageDisable) null else application.getExternalFilesDir(
|
||||||
|
Environment.DIRECTORY_ALARMS
|
||||||
|
)?.absolutePath
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the path of /storage/emulated/0/Android/data/package/files/Notifications.
|
||||||
|
*
|
||||||
|
* @return the path of /storage/emulated/0/Android/data/package/files/Notifications
|
||||||
|
*/
|
||||||
|
fun getExternalAppNotificationsPath(application: Application): String? {
|
||||||
|
return if (isExternalStorageDisable) null else application.getExternalFilesDir(
|
||||||
|
Environment.DIRECTORY_NOTIFICATIONS
|
||||||
|
)?.absolutePath
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the path of /storage/emulated/0/Android/data/package/files/Pictures.
|
||||||
|
*
|
||||||
|
* @return path of /storage/emulated/0/Android/data/package/files/Pictures
|
||||||
|
*/
|
||||||
|
fun getExternalAppPicturesPath(application: Application): String? {
|
||||||
|
return if (isExternalStorageDisable) null else application.getExternalFilesDir(
|
||||||
|
Environment.DIRECTORY_PICTURES
|
||||||
|
)?.absolutePath
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the path of /storage/emulated/0/Android/data/package/files/Movies.
|
||||||
|
*
|
||||||
|
* @return the path of /storage/emulated/0/Android/data/package/files/Movies
|
||||||
|
*/
|
||||||
|
fun getExternalAppMoviesPath(application: Application): String? {
|
||||||
|
return if (isExternalStorageDisable) null else application.getExternalFilesDir(
|
||||||
|
Environment.DIRECTORY_MOVIES
|
||||||
|
)?.absolutePath
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the path of /storage/emulated/0/Android/data/package/files/Download.
|
||||||
|
*
|
||||||
|
* @return the path of /storage/emulated/0/Android/data/package/files/Download
|
||||||
|
*/
|
||||||
|
fun getExternalAppDownloadPath(application: Application): String? {
|
||||||
|
return if (isExternalStorageDisable) null else application.getExternalFilesDir(
|
||||||
|
Environment.DIRECTORY_DOWNLOADS
|
||||||
|
)?.absolutePath
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the path of /storage/emulated/0/Android/data/package/files/DCIM.
|
||||||
|
*
|
||||||
|
* @return the path of /storage/emulated/0/Android/data/package/files/DCIM
|
||||||
|
*/
|
||||||
|
fun getExternalAppDcimPath(application: Application): String? {
|
||||||
|
return if (isExternalStorageDisable) null else application.getExternalFilesDir(
|
||||||
|
Environment.DIRECTORY_DCIM
|
||||||
|
)?.absolutePath
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the path of /storage/emulated/0/Android/data/package/files/Documents.
|
||||||
|
*
|
||||||
|
* @return the path of /storage/emulated/0/Android/data/package/files/Documents
|
||||||
|
*/
|
||||||
|
fun getExternalAppDocumentsPath(application: Application): String? {
|
||||||
|
if (isExternalStorageDisable) return null
|
||||||
|
return if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) {
|
||||||
|
application.getExternalFilesDir(null)?.absolutePath + "/Documents"
|
||||||
|
} else application.getExternalFilesDir(
|
||||||
|
Environment.DIRECTORY_DOCUMENTS
|
||||||
|
)?.absolutePath
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the path of /storage/emulated/0/Android/obb/package.
|
||||||
|
*
|
||||||
|
* @return the path of /storage/emulated/0/Android/obb/package
|
||||||
|
*/
|
||||||
|
fun getExternalAppObbPath(application: Application): String? {
|
||||||
|
return if (isExternalStorageDisable) null else application.obbDir.absolutePath
|
||||||
|
}
|
||||||
|
|
||||||
|
private val isExternalStorageDisable: Boolean
|
||||||
|
private get() = Environment.MEDIA_MOUNTED != Environment.getExternalStorageState()
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 判断sub是否在parent之下的文件或子文件夹<br></br>
|
||||||
|
*
|
||||||
|
* @param parent
|
||||||
|
* @param sub
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
fun isSub(parent: File, sub: File): Boolean {
|
||||||
|
return try {
|
||||||
|
sub.absolutePath.startsWith(parent.absolutePath)
|
||||||
|
} catch (e: Exception) {
|
||||||
|
false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取子绝对路径与父绝对路径的相对路径
|
||||||
|
*
|
||||||
|
* @param parentPath
|
||||||
|
* @param subPath
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
fun getRelativePath(parentPath: String?, subPath: String?): String? {
|
||||||
|
return try {
|
||||||
|
if (parentPath == null || subPath == null) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
if (subPath.startsWith(parentPath)) {
|
||||||
|
subPath.substring(parentPath.length)
|
||||||
|
} else {
|
||||||
|
null
|
||||||
|
}
|
||||||
|
} catch (e: Exception) {
|
||||||
|
null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 拼接两个路径
|
||||||
|
*
|
||||||
|
* @param pathA 路径A
|
||||||
|
* @param pathB 路径B
|
||||||
|
* @return 拼接后的路径
|
||||||
|
*/
|
||||||
|
fun plusPath(pathA: String?, pathB: String?): String? {
|
||||||
|
if (pathA == null) {
|
||||||
|
return pathB
|
||||||
|
}
|
||||||
|
if (pathB == null) {
|
||||||
|
return pathA
|
||||||
|
}
|
||||||
|
return plusPathNotNull(pathA, pathB)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 拼接两个路径
|
||||||
|
*
|
||||||
|
* @param pathA 路径A
|
||||||
|
* @param pathB 路径B
|
||||||
|
* @return 拼接后的路径
|
||||||
|
*/
|
||||||
|
fun plusPathNotNull(pathA: String, pathB: String): String {
|
||||||
|
val pathAEndSeparator = pathA.endsWith(File.separator)
|
||||||
|
val pathBStartSeparator = pathB.startsWith(File.separator)
|
||||||
|
return if (pathAEndSeparator && pathBStartSeparator) {
|
||||||
|
pathA + pathB.substring(1)
|
||||||
|
} else if (pathAEndSeparator || pathBStartSeparator) {
|
||||||
|
pathA + pathB
|
||||||
|
} else {
|
||||||
|
pathA + File.separator + pathB
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取后缀名称
|
||||||
|
* @param path 路径
|
||||||
|
* @return 后缀格式 .mp4 .gif 等
|
||||||
|
*/
|
||||||
|
fun getSuffixType(path: String): String? {
|
||||||
|
if (path.isEmpty()) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
val dotIndex = path.indexOfLast {
|
||||||
|
'.' == it
|
||||||
|
}
|
||||||
|
val separatorIndex = path.indexOfLast {
|
||||||
|
'/' == it
|
||||||
|
}
|
||||||
|
if (dotIndex >= 0 && dotIndex > separatorIndex) {
|
||||||
|
val suffix = path.substring(dotIndex)
|
||||||
|
val askIndex = suffix.indexOfLast {
|
||||||
|
'?' == it
|
||||||
|
}
|
||||||
|
return if (askIndex >= 0) {
|
||||||
|
suffix.substring(0, askIndex)
|
||||||
|
} else {
|
||||||
|
suffix
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null
|
||||||
|
}
|
||||||
}
|
}
|
@@ -1,25 +0,0 @@
|
|||||||
package com.chuhai.utils
|
|
||||||
|
|
||||||
import android.os.SystemClock
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Created by Max on 2023/10/24 15:11
|
|
||||||
* Desc:服务器时间
|
|
||||||
*/
|
|
||||||
object ServiceTime {
|
|
||||||
|
|
||||||
// 服务器时间与系统开机时间的时差
|
|
||||||
private var serviceTimeDiff: Long? = null
|
|
||||||
|
|
||||||
val time
|
|
||||||
get() = if (serviceTimeDiff == null) System.currentTimeMillis()
|
|
||||||
else SystemClock.elapsedRealtime() + serviceTimeDiff!!
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 刷新服务器时间
|
|
||||||
*/
|
|
||||||
fun refreshServiceTime(time: Long) {
|
|
||||||
//serviceTimeDiff = 服务器时间 - 此刻系统启动时间
|
|
||||||
serviceTimeDiff = time - SystemClock.elapsedRealtime()
|
|
||||||
}
|
|
||||||
}
|
|
@@ -5,6 +5,7 @@ import android.content.Context;
|
|||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.graphics.Bitmap;
|
import android.graphics.Bitmap;
|
||||||
import android.graphics.Color;
|
import android.graphics.Color;
|
||||||
|
import android.os.Build;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.os.Handler;
|
import android.os.Handler;
|
||||||
import android.text.TextUtils;
|
import android.text.TextUtils;
|
||||||
@@ -589,7 +590,7 @@ public class WatchMessagePictureActivity extends UI {
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected String[] getNeedPermissions() {
|
protected String[] getNeedPermissions() {
|
||||||
return new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE};
|
return new String[]{(Build.VERSION.SDK_INT >= 33) ? Manifest.permission.READ_MEDIA_IMAGES : Manifest.permission.WRITE_EXTERNAL_STORAGE};
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user